]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Fix error handling in sftp (the sftp_recv return value was being
[PuTTY.git] / 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 }