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