]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Remove ghastly hack involving fxp_error_message.
[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         return 0;
340     }
341     remotever = sftp_pkt_getuint32(pktin);
342     if (remotever > SFTP_PROTO_VERSION) {
343         fxp_internal_error
344             ("remote protocol is more advanced than we support");
345         return 0;
346     }
347     /*
348      * In principle, this packet might also contain extension-
349      * string pairs. We should work through them and look for any
350      * we recognise. In practice we don't currently do so because
351      * we know we don't recognise _any_.
352      */
353     sftp_pkt_free(pktin);
354
355     return 1;
356 }
357
358 /*
359  * Canonify a pathname.
360  */
361 char *fxp_realpath(char *path)
362 {
363     struct sftp_packet *pktin, *pktout;
364     int id;
365
366     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
367     sftp_pkt_adduint32(pktout, 0x123); /* request id */
368     sftp_pkt_addstring_start(pktout);
369     sftp_pkt_addstring_str(pktout, path);
370     sftp_send(pktout);
371     pktin = sftp_recv();
372     if (!pktin) {
373         fxp_internal_error("did not receive a valid SFTP packet\n");
374         return NULL;
375     }
376     id = sftp_pkt_getuint32(pktin);
377     if (id != 0x123) {
378         fxp_internal_error("request ID mismatch\n");
379         return NULL;
380     }
381     if (pktin->type == SSH_FXP_NAME) {
382         int count;
383         char *path;
384         int len;
385
386         count = sftp_pkt_getuint32(pktin);
387         if (count != 1) {
388             fxp_internal_error("REALPATH returned name count != 1\n");
389             return NULL;
390         }
391         sftp_pkt_getstring(pktin, &path, &len);
392         if (!path) {
393             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
394             return NULL;
395         }
396         path = mkstr(path, len);
397         sftp_pkt_free(pktin);
398         return path;
399     } else {
400         fxp_got_status(pktin);
401         return NULL;
402     }
403 }
404
405 /*
406  * Open a file.
407  */
408 struct fxp_handle *fxp_open(char *path, int type)
409 {
410     struct sftp_packet *pktin, *pktout;
411     int id;
412
413     pktout = sftp_pkt_init(SSH_FXP_OPEN);
414     sftp_pkt_adduint32(pktout, 0x567); /* request id */
415     sftp_pkt_addstring(pktout, path);
416     sftp_pkt_adduint32(pktout, type);
417     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
418     sftp_send(pktout);
419     pktin = sftp_recv();
420     if (!pktin) {
421         fxp_internal_error("did not receive a valid SFTP packet\n");
422         return NULL;
423     }
424     id = sftp_pkt_getuint32(pktin);
425     if (id != 0x567) {
426         fxp_internal_error("request ID mismatch\n");
427         return NULL;
428     }
429     if (pktin->type == SSH_FXP_HANDLE) {
430         char *hstring;
431         struct fxp_handle *handle;
432         int len;
433
434         sftp_pkt_getstring(pktin, &hstring, &len);
435         if (!hstring) {
436             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
437             return NULL;
438         }
439         handle = smalloc(sizeof(struct fxp_handle));
440         handle->hstring = mkstr(hstring, len);
441         handle->hlen = len;
442         sftp_pkt_free(pktin);
443         return handle;
444     } else {
445         fxp_got_status(pktin);
446         return NULL;
447     }
448 }
449
450 /*
451  * Open a directory.
452  */
453 struct fxp_handle *fxp_opendir(char *path)
454 {
455     struct sftp_packet *pktin, *pktout;
456     int id;
457
458     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
459     sftp_pkt_adduint32(pktout, 0x456); /* request id */
460     sftp_pkt_addstring(pktout, path);
461     sftp_send(pktout);
462     pktin = sftp_recv();
463     if (!pktin) {
464         fxp_internal_error("did not receive a valid SFTP packet\n");
465         return NULL;
466     }
467     id = sftp_pkt_getuint32(pktin);
468     if (id != 0x456) {
469         fxp_internal_error("request ID mismatch\n");
470         return NULL;
471     }
472     if (pktin->type == SSH_FXP_HANDLE) {
473         char *hstring;
474         struct fxp_handle *handle;
475         int len;
476
477         sftp_pkt_getstring(pktin, &hstring, &len);
478         if (!hstring) {
479             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
480             return NULL;
481         }
482         handle = smalloc(sizeof(struct fxp_handle));
483         handle->hstring = mkstr(hstring, len);
484         handle->hlen = len;
485         sftp_pkt_free(pktin);
486         return handle;
487     } else {
488         fxp_got_status(pktin);
489         return NULL;
490     }
491 }
492
493 /*
494  * Close a file/dir.
495  */
496 void fxp_close(struct fxp_handle *handle)
497 {
498     struct sftp_packet *pktin, *pktout;
499     int id;
500
501     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
502     sftp_pkt_adduint32(pktout, 0x789); /* request id */
503     sftp_pkt_addstring_start(pktout);
504     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
505     sftp_send(pktout);
506     pktin = sftp_recv();
507     if (!pktin) {
508         fxp_internal_error("did not receive a valid SFTP packet\n");
509         return;
510     }
511     id = sftp_pkt_getuint32(pktin);
512     if (id != 0x789) {
513         fxp_internal_error("request ID mismatch\n");
514         return;
515     }
516     fxp_got_status(pktin);
517     sfree(handle->hstring);
518     sfree(handle);
519 }
520
521 int fxp_mkdir(char *path)
522 {
523     struct sftp_packet *pktin, *pktout;
524     int id;
525
526     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
527     sftp_pkt_adduint32(pktout, 0x234); /* request id */
528     sftp_pkt_addstring(pktout, path);
529     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
530     sftp_send(pktout);
531     pktin = sftp_recv();
532     if (!pktin) {
533         fxp_internal_error("did not receive a valid SFTP packet\n");
534         return 0;
535     }
536     id = sftp_pkt_getuint32(pktin);
537     if (id != 0x234) {
538         fxp_internal_error("request ID mismatch\n");
539         return 0;
540     }
541     id = fxp_got_status(pktin);
542     if (id != 1) {
543         return 0;
544     }
545     return 1;
546 }
547
548 int fxp_rmdir(char *path)
549 {
550     struct sftp_packet *pktin, *pktout;
551     int id;
552
553     pktout = sftp_pkt_init(SSH_FXP_RMDIR);
554     sftp_pkt_adduint32(pktout, 0x345); /* request id */
555     sftp_pkt_addstring(pktout, path);
556     sftp_send(pktout);
557     pktin = sftp_recv();
558     if (!pktin) {
559         fxp_internal_error("did not receive a valid SFTP packet\n");
560         return 0;
561     }
562     id = sftp_pkt_getuint32(pktin);
563     if (id != 0x345) {
564         fxp_internal_error("request ID mismatch\n");
565         return 0;
566     }
567     id = fxp_got_status(pktin);
568     if (id != 1) {
569         return 0;
570     }
571     return 1;
572 }
573
574 int fxp_remove(char *fname)
575 {
576     struct sftp_packet *pktin, *pktout;
577     int id;
578
579     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
580     sftp_pkt_adduint32(pktout, 0x678); /* request id */
581     sftp_pkt_addstring(pktout, fname);
582     sftp_send(pktout);
583     pktin = sftp_recv();
584     if (!pktin) {
585         fxp_internal_error("did not receive a valid SFTP packet\n");
586         return 0;
587     }
588     id = sftp_pkt_getuint32(pktin);
589     if (id != 0x678) {
590         fxp_internal_error("request ID mismatch\n");
591         return 0;
592     }
593     id = fxp_got_status(pktin);
594     if (id != 1) {
595         return 0;
596     }
597     return 1;
598 }
599
600 int fxp_rename(char *srcfname, char *dstfname)
601 {
602     struct sftp_packet *pktin, *pktout;
603     int id;
604
605     pktout = sftp_pkt_init(SSH_FXP_RENAME);
606     sftp_pkt_adduint32(pktout, 0x678); /* request id */
607     sftp_pkt_addstring(pktout, srcfname);
608     sftp_pkt_addstring(pktout, dstfname);
609     sftp_send(pktout);
610     pktin = sftp_recv();
611     if (!pktin) {
612         fxp_internal_error("did not receive a valid SFTP packet\n");
613         return 0;
614     }
615     id = sftp_pkt_getuint32(pktin);
616     if (id != 0x678) {
617         fxp_internal_error("request ID mismatch\n");
618         return 0;
619     }
620     id = fxp_got_status(pktin);
621     if (id != 1) {
622         return 0;
623     }
624     return 1;
625 }
626
627 /*
628  * Retrieve the attributes of a file. We have fxp_stat which works
629  * on filenames, and fxp_fstat which works on open file handles.
630  */
631 int fxp_stat(char *fname, struct fxp_attrs *attrs)
632 {
633     struct sftp_packet *pktin, *pktout;
634     int id;
635
636     pktout = sftp_pkt_init(SSH_FXP_STAT);
637     sftp_pkt_adduint32(pktout, 0x678); /* request id */
638     sftp_pkt_addstring(pktout, fname);
639     sftp_send(pktout);
640     pktin = sftp_recv();
641     if (!pktin) {
642         fxp_internal_error("did not receive a valid SFTP packet\n");
643         return 0;
644     }
645     id = sftp_pkt_getuint32(pktin);
646     if (id != 0x678) {
647         fxp_internal_error("request ID mismatch\n");
648         return 0;
649     }
650
651     if (pktin->type == SSH_FXP_ATTRS) {
652         *attrs = sftp_pkt_getattrs(pktin);
653         return 1;
654     } else {
655         fxp_got_status(pktin);
656         return 0;
657     }
658 }
659
660 int fxp_fstat(struct fxp_handle *handle, struct fxp_attrs *attrs)
661 {
662     struct sftp_packet *pktin, *pktout;
663     int id;
664
665     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
666     sftp_pkt_adduint32(pktout, 0x678); /* request id */
667     sftp_pkt_addstring_start(pktout);
668     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
669     sftp_send(pktout);
670     pktin = sftp_recv();
671     if (!pktin) {
672         fxp_internal_error("did not receive a valid SFTP packet\n");
673         return 0;
674     }
675     id = sftp_pkt_getuint32(pktin);
676     if (id != 0x678) {
677         fxp_internal_error("request ID mismatch\n");
678         return 0;
679     }
680
681     if (pktin->type == SSH_FXP_ATTRS) {
682         *attrs = sftp_pkt_getattrs(pktin);
683         return 1;
684     } else {
685         fxp_got_status(pktin);
686         return 0;
687     }
688 }
689
690 /*
691  * Set the attributes of a file.
692  */
693 int fxp_setstat(char *fname, struct fxp_attrs attrs)
694 {
695     struct sftp_packet *pktin, *pktout;
696     int id;
697
698     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
699     sftp_pkt_adduint32(pktout, 0x678); /* request id */
700     sftp_pkt_addstring(pktout, fname);
701     sftp_pkt_addattrs(pktout, attrs);
702     sftp_send(pktout);
703     pktin = sftp_recv();
704     if (!pktin) {
705         fxp_internal_error("did not receive a valid SFTP packet\n");
706         return 0;
707     }
708     id = sftp_pkt_getuint32(pktin);
709     if (id != 0x678) {
710         fxp_internal_error("request ID mismatch\n");
711         return 0;
712     }
713     id = fxp_got_status(pktin);
714     if (id != 1) {
715         return 0;
716     }
717     return 1;
718 }
719 int fxp_fsetstat(struct fxp_handle *handle, struct fxp_attrs attrs)
720 {
721     struct sftp_packet *pktin, *pktout;
722     int id;
723
724     pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
725     sftp_pkt_adduint32(pktout, 0x678); /* request id */
726     sftp_pkt_addstring_start(pktout);
727     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
728     sftp_pkt_addattrs(pktout, attrs);
729     sftp_send(pktout);
730     pktin = sftp_recv();
731     if (!pktin) {
732         fxp_internal_error("did not receive a valid SFTP packet\n");
733         return 0;
734     }
735     id = sftp_pkt_getuint32(pktin);
736     if (id != 0x678) {
737         fxp_internal_error("request ID mismatch\n");
738         return 0;
739     }
740     id = fxp_got_status(pktin);
741     if (id != 1) {
742         return 0;
743     }
744     return 1;
745 }
746
747 /*
748  * Read from a file. Returns the number of bytes read, or -1 on an
749  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
750  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
751  * error indicator. It might even depend on the SFTP server.)
752  */
753 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
754              int len)
755 {
756     struct sftp_packet *pktin, *pktout;
757     int id;
758
759     pktout = sftp_pkt_init(SSH_FXP_READ);
760     sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
761     sftp_pkt_addstring_start(pktout);
762     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
763     sftp_pkt_adduint64(pktout, offset);
764     sftp_pkt_adduint32(pktout, len);
765     sftp_send(pktout);
766     pktin = sftp_recv();
767     if (!pktin) {
768         fxp_internal_error("did not receive a valid SFTP packet\n");
769         return -1;
770     }
771     id = sftp_pkt_getuint32(pktin);
772     if (id != 0xBCD) {
773         fxp_internal_error("request ID mismatch");
774         return -1;
775     }
776     if (pktin->type == SSH_FXP_DATA) {
777         char *str;
778         int rlen;
779
780         sftp_pkt_getstring(pktin, &str, &rlen);
781
782         if (rlen > len || rlen < 0) {
783             fxp_internal_error("READ returned more bytes than requested");
784             return -1;
785         }
786
787         memcpy(buffer, str, rlen);
788         sfree(pktin);
789         return rlen;
790     } else {
791         fxp_got_status(pktin);
792         return -1;
793     }
794 }
795
796 /*
797  * Read from a directory.
798  */
799 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
800 {
801     struct sftp_packet *pktin, *pktout;
802     int id;
803
804     pktout = sftp_pkt_init(SSH_FXP_READDIR);
805     sftp_pkt_adduint32(pktout, 0xABC); /* request id */
806     sftp_pkt_addstring_start(pktout);
807     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
808     sftp_send(pktout);
809     pktin = sftp_recv();
810     if (!pktin) {
811         fxp_internal_error("did not receive a valid SFTP packet\n");
812         return NULL;
813     }
814     id = sftp_pkt_getuint32(pktin);
815     if (id != 0xABC) {
816         fxp_internal_error("request ID mismatch\n");
817         return NULL;
818     }
819     if (pktin->type == SSH_FXP_NAME) {
820         struct fxp_names *ret;
821         int i;
822         ret = smalloc(sizeof(struct fxp_names));
823         ret->nnames = sftp_pkt_getuint32(pktin);
824         ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
825         for (i = 0; i < ret->nnames; i++) {
826             char *str;
827             int len;
828             sftp_pkt_getstring(pktin, &str, &len);
829             ret->names[i].filename = mkstr(str, len);
830             sftp_pkt_getstring(pktin, &str, &len);
831             ret->names[i].longname = mkstr(str, len);
832             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
833         }
834         return ret;
835     } else {
836         fxp_got_status(pktin);
837         return NULL;
838     }
839 }
840
841 /*
842  * Write to a file. Returns 0 on error, 1 on OK.
843  */
844 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
845               int len)
846 {
847     struct sftp_packet *pktin, *pktout;
848     int id;
849
850     pktout = sftp_pkt_init(SSH_FXP_WRITE);
851     sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
852     sftp_pkt_addstring_start(pktout);
853     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
854     sftp_pkt_adduint64(pktout, offset);
855     sftp_pkt_addstring_start(pktout);
856     sftp_pkt_addstring_data(pktout, buffer, len);
857     sftp_send(pktout);
858     pktin = sftp_recv();
859     if (!pktin) {
860         fxp_internal_error("did not receive a valid SFTP packet\n");
861         return 0;
862     }
863     id = sftp_pkt_getuint32(pktin);
864     if (id != 0xDCB) {
865         fxp_internal_error("request ID mismatch\n");
866         return 0;
867     }
868     fxp_got_status(pktin);
869     return fxp_errtype == SSH_FX_OK;
870 }
871
872 /*
873  * Free up an fxp_names structure.
874  */
875 void fxp_free_names(struct fxp_names *names)
876 {
877     int i;
878
879     for (i = 0; i < names->nnames; i++) {
880         sfree(names->names[i].filename);
881         sfree(names->names[i].longname);
882     }
883     sfree(names->names);
884     sfree(names);
885 }