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