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