Fix error handling in sftp (the sftp_recv return value was being
[u/mdw/putty] / sftp.c
1 /*
2 * sftp.c: SFTP generic client code.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9
10 #include "misc.h"
11 #include "int64.h"
12 #include "sftp.h"
13
14 #define GET_32BIT(cp) \
15 (((unsigned long)(unsigned char)(cp)[0] << 24) | \
16 ((unsigned long)(unsigned char)(cp)[1] << 16) | \
17 ((unsigned long)(unsigned char)(cp)[2] << 8) | \
18 ((unsigned long)(unsigned char)(cp)[3]))
19
20 #define PUT_32BIT(cp, value) { \
21 (cp)[0] = (unsigned char)((value) >> 24); \
22 (cp)[1] = (unsigned char)((value) >> 16); \
23 (cp)[2] = (unsigned char)((value) >> 8); \
24 (cp)[3] = (unsigned char)(value); }
25
26 struct sftp_packet {
27 char *data;
28 int length, maxlen;
29 int savedpos;
30 int type;
31 };
32
33 static const char *fxp_error_message;
34 static int fxp_errtype;
35
36 /* ----------------------------------------------------------------------
37 * SFTP packet construction functions.
38 */
39 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
40 {
41 if (pkt->maxlen < length) {
42 pkt->maxlen = length + 256;
43 pkt->data = srealloc(pkt->data, pkt->maxlen);
44 }
45 }
46 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
47 {
48 pkt->length += len;
49 sftp_pkt_ensure(pkt, pkt->length);
50 memcpy(pkt->data + pkt->length - len, data, len);
51 }
52 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
53 {
54 sftp_pkt_adddata(pkt, &byte, 1);
55 }
56 static struct sftp_packet *sftp_pkt_init(int pkt_type)
57 {
58 struct sftp_packet *pkt;
59 pkt = smalloc(sizeof(struct sftp_packet));
60 pkt->data = NULL;
61 pkt->savedpos = -1;
62 pkt->length = 0;
63 pkt->maxlen = 0;
64 sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
65 fxp_error_message = NULL;
66 return pkt;
67 }
68 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
69 {
70 sftp_pkt_adddata(pkt, &value, 1);
71 }
72 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
73 unsigned long value)
74 {
75 unsigned char x[4];
76 PUT_32BIT(x, value);
77 sftp_pkt_adddata(pkt, x, 4);
78 }
79 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
80 {
81 unsigned char x[8];
82 PUT_32BIT(x, value.hi);
83 PUT_32BIT(x + 4, value.lo);
84 sftp_pkt_adddata(pkt, x, 8);
85 }
86 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
87 {
88 sftp_pkt_adduint32(pkt, 0);
89 pkt->savedpos = pkt->length;
90 }
91 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
92 {
93 sftp_pkt_adddata(pkt, data, strlen(data));
94 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
95 }
96 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
97 char *data, int len)
98 {
99 sftp_pkt_adddata(pkt, data, len);
100 PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
101 }
102 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
103 {
104 sftp_pkt_addstring_start(pkt);
105 sftp_pkt_addstring_str(pkt, data);
106 }
107 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
108 {
109 sftp_pkt_adduint32(pkt, attrs.flags);
110 if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
111 sftp_pkt_adduint32(pkt, attrs.size.hi);
112 sftp_pkt_adduint32(pkt, attrs.size.lo);
113 }
114 if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
115 sftp_pkt_adduint32(pkt, attrs.uid);
116 sftp_pkt_adduint32(pkt, attrs.gid);
117 }
118 if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
119 sftp_pkt_adduint32(pkt, attrs.permissions);
120 }
121 if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
122 sftp_pkt_adduint32(pkt, attrs.atime);
123 sftp_pkt_adduint32(pkt, attrs.mtime);
124 }
125 if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
126 /*
127 * We currently don't support sending any extended
128 * attributes.
129 */
130 }
131 }
132
133 /* ----------------------------------------------------------------------
134 * SFTP packet decode functions.
135 */
136
137 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
138 {
139 unsigned char value;
140 if (pkt->length - pkt->savedpos < 1)
141 return 0; /* arrgh, no way to decline (FIXME?) */
142 value = (unsigned char) pkt->data[pkt->savedpos];
143 pkt->savedpos++;
144 return value;
145 }
146 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
147 {
148 unsigned long value;
149 if (pkt->length - pkt->savedpos < 4)
150 return 0; /* arrgh, no way to decline (FIXME?) */
151 value = GET_32BIT(pkt->data + pkt->savedpos);
152 pkt->savedpos += 4;
153 return value;
154 }
155 static void sftp_pkt_getstring(struct sftp_packet *pkt,
156 char **p, int *length)
157 {
158 *p = NULL;
159 if (pkt->length - pkt->savedpos < 4)
160 return;
161 *length = GET_32BIT(pkt->data + pkt->savedpos);
162 pkt->savedpos += 4;
163 if (pkt->length - pkt->savedpos < *length)
164 return;
165 *p = pkt->data + pkt->savedpos;
166 pkt->savedpos += *length;
167 }
168 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
169 {
170 struct fxp_attrs ret;
171 ret.flags = sftp_pkt_getuint32(pkt);
172 if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
173 unsigned long hi, lo;
174 hi = sftp_pkt_getuint32(pkt);
175 lo = sftp_pkt_getuint32(pkt);
176 ret.size = uint64_make(hi, lo);
177 }
178 if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
179 ret.uid = sftp_pkt_getuint32(pkt);
180 ret.gid = sftp_pkt_getuint32(pkt);
181 }
182 if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
183 ret.permissions = sftp_pkt_getuint32(pkt);
184 }
185 if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
186 ret.atime = sftp_pkt_getuint32(pkt);
187 ret.mtime = sftp_pkt_getuint32(pkt);
188 }
189 if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
190 int count;
191 count = sftp_pkt_getuint32(pkt);
192 while (count--) {
193 char *str;
194 int len;
195 /*
196 * We should try to analyse these, if we ever find one
197 * we recognise.
198 */
199 sftp_pkt_getstring(pkt, &str, &len);
200 sftp_pkt_getstring(pkt, &str, &len);
201 }
202 }
203 return ret;
204 }
205 static void sftp_pkt_free(struct sftp_packet *pkt)
206 {
207 if (pkt->data)
208 sfree(pkt->data);
209 sfree(pkt);
210 }
211
212 /* ----------------------------------------------------------------------
213 * Send and receive packet functions.
214 */
215 int sftp_send(struct sftp_packet *pkt)
216 {
217 int ret;
218 char x[4];
219 PUT_32BIT(x, pkt->length);
220 ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
221 sftp_pkt_free(pkt);
222 return ret;
223 }
224 struct sftp_packet *sftp_recv(void)
225 {
226 struct sftp_packet *pkt;
227 char x[4];
228
229 if (!sftp_recvdata(x, 4))
230 return NULL;
231
232 pkt = smalloc(sizeof(struct sftp_packet));
233 pkt->savedpos = 0;
234 pkt->length = pkt->maxlen = GET_32BIT(x);
235 pkt->data = smalloc(pkt->length);
236
237 if (!sftp_recvdata(pkt->data, pkt->length)) {
238 sftp_pkt_free(pkt);
239 return NULL;
240 }
241
242 pkt->type = sftp_pkt_getbyte(pkt);
243
244 return pkt;
245 }
246
247 /* ----------------------------------------------------------------------
248 * String handling routines.
249 */
250
251 static char *mkstr(char *s, int len)
252 {
253 char *p = smalloc(len + 1);
254 memcpy(p, s, len);
255 p[len] = '\0';
256 return p;
257 }
258
259 /* ----------------------------------------------------------------------
260 * SFTP primitives.
261 */
262
263 /*
264 * Deal with (and free) an FXP_STATUS packet. Return 1 if
265 * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
266 * Also place the status into fxp_errtype.
267 */
268 static int fxp_got_status(struct sftp_packet *pktin)
269 {
270 static const char *const messages[] = {
271 /* SSH_FX_OK. The only time we will display a _message_ for this
272 * is if we were expecting something other than FXP_STATUS on
273 * success, so this is actually an error message! */
274 "unexpected OK response",
275 "end of file",
276 "no such file or directory",
277 "permission denied",
278 "failure",
279 "bad message",
280 "no connection",
281 "connection lost",
282 "operation unsupported",
283 };
284
285 if (pktin->type != SSH_FXP_STATUS) {
286 fxp_error_message = "expected FXP_STATUS packet";
287 fxp_errtype = -1;
288 } else {
289 fxp_errtype = sftp_pkt_getuint32(pktin);
290 if (fxp_errtype < 0 ||
291 fxp_errtype >= sizeof(messages) / sizeof(*messages))
292 fxp_error_message = "unknown error code";
293 else
294 fxp_error_message = messages[fxp_errtype];
295 }
296
297 if (fxp_errtype == SSH_FX_OK)
298 return 1;
299 else if (fxp_errtype == SSH_FX_EOF)
300 return 0;
301 else
302 return -1;
303 }
304
305 static void fxp_internal_error(char *msg)
306 {
307 fxp_error_message = msg;
308 fxp_errtype = -1;
309 }
310
311 const char *fxp_error(void)
312 {
313 return fxp_error_message;
314 }
315
316 int fxp_error_type(void)
317 {
318 return fxp_errtype;
319 }
320
321 /*
322 * Perform exchange of init/version packets. Return 0 on failure.
323 */
324 int fxp_init(void)
325 {
326 struct sftp_packet *pktout, *pktin;
327 int remotever;
328
329 pktout = sftp_pkt_init(SSH_FXP_INIT);
330 sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
331 sftp_send(pktout);
332
333 pktin = sftp_recv();
334 if (!pktin) {
335 fxp_internal_error("could not connect");
336 return 0;
337 }
338 if (pktin->type != SSH_FXP_VERSION) {
339 fxp_internal_error("did not receive FXP_VERSION");
340 return 0;
341 }
342 remotever = sftp_pkt_getuint32(pktin);
343 if (remotever > SFTP_PROTO_VERSION) {
344 fxp_internal_error
345 ("remote protocol is more advanced than we support");
346 return 0;
347 }
348 /*
349 * In principle, this packet might also contain extension-
350 * string pairs. We should work through them and look for any
351 * we recognise. In practice we don't currently do so because
352 * we know we don't recognise _any_.
353 */
354 sftp_pkt_free(pktin);
355
356 return 1;
357 }
358
359 /*
360 * Canonify a pathname.
361 */
362 char *fxp_realpath(char *path)
363 {
364 struct sftp_packet *pktin, *pktout;
365 int id;
366
367 pktout = sftp_pkt_init(SSH_FXP_REALPATH);
368 sftp_pkt_adduint32(pktout, 0x123); /* request id */
369 sftp_pkt_addstring_start(pktout);
370 sftp_pkt_addstring_str(pktout, path);
371 sftp_send(pktout);
372 pktin = sftp_recv();
373 if (!pktin) {
374 fxp_internal_error("did not receive a valid SFTP packet\n");
375 return NULL;
376 }
377 id = sftp_pkt_getuint32(pktin);
378 if (id != 0x123) {
379 fxp_internal_error("request ID mismatch\n");
380 return NULL;
381 }
382 if (pktin->type == SSH_FXP_NAME) {
383 int count;
384 char *path;
385 int len;
386
387 count = sftp_pkt_getuint32(pktin);
388 if (count != 1) {
389 fxp_internal_error("REALPATH returned name count != 1\n");
390 return NULL;
391 }
392 sftp_pkt_getstring(pktin, &path, &len);
393 if (!path) {
394 fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
395 return NULL;
396 }
397 path = mkstr(path, len);
398 sftp_pkt_free(pktin);
399 return path;
400 } else {
401 fxp_got_status(pktin);
402 return NULL;
403 }
404 }
405
406 /*
407 * Open a file.
408 */
409 struct fxp_handle *fxp_open(char *path, int type)
410 {
411 struct sftp_packet *pktin, *pktout;
412 int id;
413
414 pktout = sftp_pkt_init(SSH_FXP_OPEN);
415 sftp_pkt_adduint32(pktout, 0x567); /* request id */
416 sftp_pkt_addstring(pktout, path);
417 sftp_pkt_adduint32(pktout, type);
418 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
419 sftp_send(pktout);
420 pktin = sftp_recv();
421 if (!pktin) {
422 fxp_internal_error("did not receive a valid SFTP packet\n");
423 return NULL;
424 }
425 id = sftp_pkt_getuint32(pktin);
426 if (id != 0x567) {
427 fxp_internal_error("request ID mismatch\n");
428 return NULL;
429 }
430 if (pktin->type == SSH_FXP_HANDLE) {
431 char *hstring;
432 struct fxp_handle *handle;
433 int len;
434
435 sftp_pkt_getstring(pktin, &hstring, &len);
436 if (!hstring) {
437 fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
438 return NULL;
439 }
440 handle = smalloc(sizeof(struct fxp_handle));
441 handle->hstring = mkstr(hstring, len);
442 handle->hlen = len;
443 sftp_pkt_free(pktin);
444 return handle;
445 } else {
446 fxp_got_status(pktin);
447 return NULL;
448 }
449 }
450
451 /*
452 * Open a directory.
453 */
454 struct fxp_handle *fxp_opendir(char *path)
455 {
456 struct sftp_packet *pktin, *pktout;
457 int id;
458
459 pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
460 sftp_pkt_adduint32(pktout, 0x456); /* request id */
461 sftp_pkt_addstring(pktout, path);
462 sftp_send(pktout);
463 pktin = sftp_recv();
464 if (!pktin) {
465 fxp_internal_error("did not receive a valid SFTP packet\n");
466 return NULL;
467 }
468 id = sftp_pkt_getuint32(pktin);
469 if (id != 0x456) {
470 fxp_internal_error("request ID mismatch\n");
471 return NULL;
472 }
473 if (pktin->type == SSH_FXP_HANDLE) {
474 char *hstring;
475 struct fxp_handle *handle;
476 int len;
477
478 sftp_pkt_getstring(pktin, &hstring, &len);
479 if (!hstring) {
480 fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
481 return NULL;
482 }
483 handle = smalloc(sizeof(struct fxp_handle));
484 handle->hstring = mkstr(hstring, len);
485 handle->hlen = len;
486 sftp_pkt_free(pktin);
487 return handle;
488 } else {
489 fxp_got_status(pktin);
490 return NULL;
491 }
492 }
493
494 /*
495 * Close a file/dir.
496 */
497 void fxp_close(struct fxp_handle *handle)
498 {
499 struct sftp_packet *pktin, *pktout;
500 int id;
501
502 pktout = sftp_pkt_init(SSH_FXP_CLOSE);
503 sftp_pkt_adduint32(pktout, 0x789); /* request id */
504 sftp_pkt_addstring_start(pktout);
505 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
506 sftp_send(pktout);
507 pktin = sftp_recv();
508 if (!pktin) {
509 fxp_internal_error("did not receive a valid SFTP packet\n");
510 return;
511 }
512 id = sftp_pkt_getuint32(pktin);
513 if (id != 0x789) {
514 fxp_internal_error("request ID mismatch\n");
515 return;
516 }
517 fxp_got_status(pktin);
518 sfree(handle->hstring);
519 sfree(handle);
520 }
521
522 int fxp_mkdir(char *path)
523 {
524 struct sftp_packet *pktin, *pktout;
525 int id;
526
527 pktout = sftp_pkt_init(SSH_FXP_MKDIR);
528 sftp_pkt_adduint32(pktout, 0x234); /* request id */
529 sftp_pkt_addstring(pktout, path);
530 sftp_pkt_adduint32(pktout, 0); /* (FIXME) empty ATTRS structure */
531 sftp_send(pktout);
532 pktin = sftp_recv();
533 if (!pktin) {
534 fxp_internal_error("did not receive a valid SFTP packet\n");
535 return 0;
536 }
537 id = sftp_pkt_getuint32(pktin);
538 if (id != 0x234) {
539 fxp_internal_error("request ID mismatch\n");
540 return 0;
541 }
542 id = fxp_got_status(pktin);
543 if (id != 1) {
544 return 0;
545 }
546 return 1;
547 }
548
549 int fxp_rmdir(char *path)
550 {
551 struct sftp_packet *pktin, *pktout;
552 int id;
553
554 pktout = sftp_pkt_init(SSH_FXP_RMDIR);
555 sftp_pkt_adduint32(pktout, 0x345); /* request id */
556 sftp_pkt_addstring(pktout, path);
557 sftp_send(pktout);
558 pktin = sftp_recv();
559 if (!pktin) {
560 fxp_internal_error("did not receive a valid SFTP packet\n");
561 return 0;
562 }
563 id = sftp_pkt_getuint32(pktin);
564 if (id != 0x345) {
565 fxp_internal_error("request ID mismatch\n");
566 return 0;
567 }
568 id = fxp_got_status(pktin);
569 if (id != 1) {
570 return 0;
571 }
572 return 1;
573 }
574
575 int fxp_remove(char *fname)
576 {
577 struct sftp_packet *pktin, *pktout;
578 int id;
579
580 pktout = sftp_pkt_init(SSH_FXP_REMOVE);
581 sftp_pkt_adduint32(pktout, 0x678); /* request id */
582 sftp_pkt_addstring(pktout, fname);
583 sftp_send(pktout);
584 pktin = sftp_recv();
585 if (!pktin) {
586 fxp_internal_error("did not receive a valid SFTP packet\n");
587 return 0;
588 }
589 id = sftp_pkt_getuint32(pktin);
590 if (id != 0x678) {
591 fxp_internal_error("request ID mismatch\n");
592 return 0;
593 }
594 id = fxp_got_status(pktin);
595 if (id != 1) {
596 return 0;
597 }
598 return 1;
599 }
600
601 int fxp_rename(char *srcfname, char *dstfname)
602 {
603 struct sftp_packet *pktin, *pktout;
604 int id;
605
606 pktout = sftp_pkt_init(SSH_FXP_RENAME);
607 sftp_pkt_adduint32(pktout, 0x678); /* request id */
608 sftp_pkt_addstring(pktout, srcfname);
609 sftp_pkt_addstring(pktout, dstfname);
610 sftp_send(pktout);
611 pktin = sftp_recv();
612 if (!pktin) {
613 fxp_internal_error("did not receive a valid SFTP packet\n");
614 return 0;
615 }
616 id = sftp_pkt_getuint32(pktin);
617 if (id != 0x678) {
618 fxp_internal_error("request ID mismatch\n");
619 return 0;
620 }
621 id = fxp_got_status(pktin);
622 if (id != 1) {
623 return 0;
624 }
625 return 1;
626 }
627
628 /*
629 * Retrieve the attributes of a file. We have fxp_stat which works
630 * on filenames, and fxp_fstat which works on open file handles.
631 */
632 int fxp_stat(char *fname, struct fxp_attrs *attrs)
633 {
634 struct sftp_packet *pktin, *pktout;
635 int id;
636
637 pktout = sftp_pkt_init(SSH_FXP_STAT);
638 sftp_pkt_adduint32(pktout, 0x678); /* request id */
639 sftp_pkt_addstring(pktout, fname);
640 sftp_send(pktout);
641 pktin = sftp_recv();
642 if (!pktin) {
643 fxp_internal_error("did not receive a valid SFTP packet\n");
644 return 0;
645 }
646 id = sftp_pkt_getuint32(pktin);
647 if (id != 0x678) {
648 fxp_internal_error("request ID mismatch\n");
649 return 0;
650 }
651
652 if (pktin->type == SSH_FXP_ATTRS) {
653 *attrs = sftp_pkt_getattrs(pktin);
654 return 1;
655 } else {
656 fxp_got_status(pktin);
657 return 0;
658 }
659 }
660
661 int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
662 {
663 struct sftp_packet *pktin, *pktout;
664 int id;
665
666 pktout = sftp_pkt_init(SSH_FXP_FSTAT);
667 sftp_pkt_adduint32(pktout, 0x678); /* request id */
668 sftp_pkt_addstring_start(pktout);
669 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
670 sftp_send(pktout);
671 pktin = sftp_recv();
672 if (!pktin) {
673 fxp_internal_error("did not receive a valid SFTP packet\n");
674 return 0;
675 }
676 id = sftp_pkt_getuint32(pktin);
677 if (id != 0x678) {
678 fxp_internal_error("request ID mismatch\n");
679 return 0;
680 }
681
682 if (pktin->type == SSH_FXP_ATTRS) {
683 *attrs = sftp_pkt_getattrs(pktin);
684 return 1;
685 } else {
686 fxp_got_status(pktin);
687 return 0;
688 }
689 }
690
691 /*
692 * Set the attributes of a file.
693 */
694 int fxp_setstat(char *fname, struct fxp_attrs attrs)
695 {
696 struct sftp_packet *pktin, *pktout;
697 int id;
698
699 pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
700 sftp_pkt_adduint32(pktout, 0x678); /* request id */
701 sftp_pkt_addstring(pktout, fname);
702 sftp_pkt_addattrs(pktout, attrs);
703 sftp_send(pktout);
704 pktin = sftp_recv();
705 if (!pktin) {
706 fxp_internal_error("did not receive a valid SFTP packet\n");
707 return 0;
708 }
709 id = sftp_pkt_getuint32(pktin);
710 if (id != 0x678) {
711 fxp_internal_error("request ID mismatch\n");
712 return 0;
713 }
714 id = fxp_got_status(pktin);
715 if (id != 1) {
716 return 0;
717 }
718 return 1;
719 }
720 int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs)
721 {
722 struct sftp_packet *pktin, *pktout;
723 int id;
724
725 pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
726 sftp_pkt_adduint32(pktout, 0x678); /* request id */
727 sftp_pkt_addstring_start(pktout);
728 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
729 sftp_pkt_addattrs(pktout, attrs);
730 sftp_send(pktout);
731 pktin = sftp_recv();
732 if (!pktin) {
733 fxp_internal_error("did not receive a valid SFTP packet\n");
734 return 0;
735 }
736 id = sftp_pkt_getuint32(pktin);
737 if (id != 0x678) {
738 fxp_internal_error("request ID mismatch\n");
739 return 0;
740 }
741 id = fxp_got_status(pktin);
742 if (id != 1) {
743 return 0;
744 }
745 return 1;
746 }
747
748 /*
749 * Read from a file. Returns the number of bytes read, or -1 on an
750 * error, or possibly 0 if EOF. (I'm not entirely sure whether it
751 * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
752 * error indicator. It might even depend on the SFTP server.)
753 */
754 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
755 int len)
756 {
757 struct sftp_packet *pktin, *pktout;
758 int id;
759
760 pktout = sftp_pkt_init(SSH_FXP_READ);
761 sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
762 sftp_pkt_addstring_start(pktout);
763 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
764 sftp_pkt_adduint64(pktout, offset);
765 sftp_pkt_adduint32(pktout, len);
766 sftp_send(pktout);
767 pktin = sftp_recv();
768 if (!pktin) {
769 fxp_internal_error("did not receive a valid SFTP packet\n");
770 return -1;
771 }
772 id = sftp_pkt_getuint32(pktin);
773 if (id != 0xBCD) {
774 fxp_internal_error("request ID mismatch");
775 return -1;
776 }
777 if (pktin->type == SSH_FXP_DATA) {
778 char *str;
779 int rlen;
780
781 sftp_pkt_getstring(pktin, &str, &rlen);
782
783 if (rlen > len || rlen < 0) {
784 fxp_internal_error("READ returned more bytes than requested");
785 return -1;
786 }
787
788 memcpy(buffer, str, rlen);
789 sfree(pktin);
790 return rlen;
791 } else {
792 fxp_got_status(pktin);
793 return -1;
794 }
795 }
796
797 /*
798 * Read from a directory.
799 */
800 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
801 {
802 struct sftp_packet *pktin, *pktout;
803 int id;
804
805 pktout = sftp_pkt_init(SSH_FXP_READDIR);
806 sftp_pkt_adduint32(pktout, 0xABC); /* request id */
807 sftp_pkt_addstring_start(pktout);
808 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
809 sftp_send(pktout);
810 pktin = sftp_recv();
811 if (!pktin) {
812 fxp_internal_error("did not receive a valid SFTP packet\n");
813 return NULL;
814 }
815 id = sftp_pkt_getuint32(pktin);
816 if (id != 0xABC) {
817 fxp_internal_error("request ID mismatch\n");
818 return NULL;
819 }
820 if (pktin->type == SSH_FXP_NAME) {
821 struct fxp_names *ret;
822 int i;
823 ret = smalloc(sizeof(struct fxp_names));
824 ret->nnames = sftp_pkt_getuint32(pktin);
825 ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
826 for (i = 0; i < ret->nnames; i++) {
827 char *str;
828 int len;
829 sftp_pkt_getstring(pktin, &str, &len);
830 ret->names[i].filename = mkstr(str, len);
831 sftp_pkt_getstring(pktin, &str, &len);
832 ret->names[i].longname = mkstr(str, len);
833 ret->names[i].attrs = sftp_pkt_getattrs(pktin);
834 }
835 return ret;
836 } else {
837 fxp_got_status(pktin);
838 return NULL;
839 }
840 }
841
842 /*
843 * Write to a file. Returns 0 on error, 1 on OK.
844 */
845 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
846 int len)
847 {
848 struct sftp_packet *pktin, *pktout;
849 int id;
850
851 pktout = sftp_pkt_init(SSH_FXP_WRITE);
852 sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
853 sftp_pkt_addstring_start(pktout);
854 sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
855 sftp_pkt_adduint64(pktout, offset);
856 sftp_pkt_addstring_start(pktout);
857 sftp_pkt_addstring_data(pktout, buffer, len);
858 sftp_send(pktout);
859 pktin = sftp_recv();
860 if (!pktin) {
861 fxp_internal_error("did not receive a valid SFTP packet\n");
862 return 0;
863 }
864 id = sftp_pkt_getuint32(pktin);
865 if (id != 0xDCB) {
866 fxp_internal_error("request ID mismatch\n");
867 return 0;
868 }
869 fxp_got_status(pktin);
870 return fxp_errtype == SSH_FX_OK;
871 }
872
873 /*
874 * Free up an fxp_names structure.
875 */
876 void fxp_free_names(struct fxp_names *names)
877 {
878 int i;
879
880 for (i = 0; i < names->nnames; i++) {
881 sfree(names->names[i].filename);
882 sfree(names->names[i].longname);
883 }
884 sfree(names->names);
885 sfree(names);
886 }