]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Additional robustness to SFTP packet parsing and memory allocation.
[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 #include <limits.h>
10
11 #include "misc.h"
12 #include "int64.h"
13 #include "tree234.h"
14 #include "sftp.h"
15
16 #define GET_32BIT(cp) \
17     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
18     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
19     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
20     ((unsigned long)(unsigned char)(cp)[3]))
21
22 #define PUT_32BIT(cp, value) { \
23     (cp)[0] = (unsigned char)((value) >> 24); \
24     (cp)[1] = (unsigned char)((value) >> 16); \
25     (cp)[2] = (unsigned char)((value) >> 8); \
26     (cp)[3] = (unsigned char)(value); }
27
28 struct sftp_packet {
29     char *data;
30     unsigned length, maxlen;
31     unsigned savedpos;
32     int type;
33 };
34
35 static const char *fxp_error_message;
36 static int fxp_errtype;
37
38 static void fxp_internal_error(char *msg);
39
40 /* ----------------------------------------------------------------------
41  * SFTP packet construction functions.
42  */
43 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
44 {
45     if (pkt->maxlen < length) {
46         pkt->maxlen = length + 256;
47         pkt->data = sresize(pkt->data, pkt->maxlen, char);
48     }
49 }
50 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
51 {
52     pkt->length += len;
53     sftp_pkt_ensure(pkt, pkt->length);
54     memcpy(pkt->data + pkt->length - len, data, len);
55 }
56 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
57 {
58     sftp_pkt_adddata(pkt, &byte, 1);
59 }
60 static struct sftp_packet *sftp_pkt_init(int pkt_type)
61 {
62     struct sftp_packet *pkt;
63     pkt = snew(struct sftp_packet);
64     pkt->data = NULL;
65     pkt->savedpos = -1;
66     pkt->length = 0;
67     pkt->maxlen = 0;
68     sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
69     return pkt;
70 }
71 /*
72 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
73 {
74     sftp_pkt_adddata(pkt, &value, 1);
75 }
76 */
77 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
78                                unsigned long value)
79 {
80     unsigned char x[4];
81     PUT_32BIT(x, value);
82     sftp_pkt_adddata(pkt, x, 4);
83 }
84 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
85 {
86     unsigned char x[8];
87     PUT_32BIT(x, value.hi);
88     PUT_32BIT(x + 4, value.lo);
89     sftp_pkt_adddata(pkt, x, 8);
90 }
91 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
92 {
93     sftp_pkt_adduint32(pkt, 0);
94     pkt->savedpos = pkt->length;
95 }
96 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
97 {
98     sftp_pkt_adddata(pkt, data, strlen(data));
99     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
100 }
101 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
102                                     char *data, int len)
103 {
104     sftp_pkt_adddata(pkt, data, len);
105     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
106 }
107 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
108 {
109     sftp_pkt_addstring_start(pkt);
110     sftp_pkt_addstring_str(pkt, data);
111 }
112 static void sftp_pkt_addattrs(struct sftp_packet *pkt, struct fxp_attrs attrs)
113 {
114     sftp_pkt_adduint32(pkt, attrs.flags);
115     if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
116         sftp_pkt_adduint32(pkt, attrs.size.hi);
117         sftp_pkt_adduint32(pkt, attrs.size.lo);
118     }
119     if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) {
120         sftp_pkt_adduint32(pkt, attrs.uid);
121         sftp_pkt_adduint32(pkt, attrs.gid);
122     }
123     if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
124         sftp_pkt_adduint32(pkt, attrs.permissions);
125     }
126     if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
127         sftp_pkt_adduint32(pkt, attrs.atime);
128         sftp_pkt_adduint32(pkt, attrs.mtime);
129     }
130     if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) {
131         /*
132          * We currently don't support sending any extended
133          * attributes.
134          */
135     }
136 }
137
138 /* ----------------------------------------------------------------------
139  * SFTP packet decode functions.
140  */
141
142 static int sftp_pkt_getbyte(struct sftp_packet *pkt, unsigned char *ret)
143 {
144     if (pkt->length - pkt->savedpos < 1)
145         return 0;
146     *ret = (unsigned char) pkt->data[pkt->savedpos];
147     pkt->savedpos++;
148     return 1;
149 }
150 static int sftp_pkt_getuint32(struct sftp_packet *pkt, unsigned long *ret)
151 {
152     if (pkt->length - pkt->savedpos < 4)
153         return 0;
154     *ret = GET_32BIT(pkt->data + pkt->savedpos);
155     pkt->savedpos += 4;
156     return 1;
157 }
158 static int 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 0;
164     *length = GET_32BIT(pkt->data + pkt->savedpos);
165     pkt->savedpos += 4;
166     if (pkt->length - pkt->savedpos < *length || *length < 0) {
167         *length = 0;
168         return 0;
169     }
170     *p = pkt->data + pkt->savedpos;
171     pkt->savedpos += *length;
172     return 1;
173 }
174 static int sftp_pkt_getattrs(struct sftp_packet *pkt, struct fxp_attrs *ret)
175 {
176     if (!sftp_pkt_getuint32(pkt, &ret->flags))
177         return 0;
178     if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {
179         unsigned long hi, lo;
180         if (!sftp_pkt_getuint32(pkt, &hi) ||
181             !sftp_pkt_getuint32(pkt, &lo))
182             return 0;
183         ret->size = uint64_make(hi, lo);
184     }
185     if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {
186         if (!sftp_pkt_getuint32(pkt, &ret->uid) ||
187             !sftp_pkt_getuint32(pkt, &ret->gid))
188             return 0;
189     }
190     if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
191         if (!sftp_pkt_getuint32(pkt, &ret->permissions))
192             return 0;
193     }
194     if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
195         if (!sftp_pkt_getuint32(pkt, &ret->atime) ||
196             !sftp_pkt_getuint32(pkt, &ret->mtime))
197             return 0;
198     }
199     if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {
200         unsigned long count;
201         if (!sftp_pkt_getuint32(pkt, &count))
202             return 0;
203         while (count--) {
204             char *str;
205             int len;
206             /*
207              * We should try to analyse these, if we ever find one
208              * we recognise.
209              */
210             if (!sftp_pkt_getstring(pkt, &str, &len) ||
211                 !sftp_pkt_getstring(pkt, &str, &len))
212                 return 0;
213         }
214     }
215     return 1;
216 }
217 static void sftp_pkt_free(struct sftp_packet *pkt)
218 {
219     if (pkt->data)
220         sfree(pkt->data);
221     sfree(pkt);
222 }
223
224 /* ----------------------------------------------------------------------
225  * Send and receive packet functions.
226  */
227 int sftp_send(struct sftp_packet *pkt)
228 {
229     int ret;
230     char x[4];
231     PUT_32BIT(x, pkt->length);
232     ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
233     sftp_pkt_free(pkt);
234     return ret;
235 }
236 struct sftp_packet *sftp_recv(void)
237 {
238     struct sftp_packet *pkt;
239     char x[4];
240     unsigned char uc;
241
242     if (!sftp_recvdata(x, 4))
243         return NULL;
244
245     pkt = snew(struct sftp_packet);
246     pkt->savedpos = 0;
247     pkt->length = pkt->maxlen = GET_32BIT(x);
248     pkt->data = snewn(pkt->length, char);
249
250     if (!sftp_recvdata(pkt->data, pkt->length)) {
251         sftp_pkt_free(pkt);
252         return NULL;
253     }
254
255     if (!sftp_pkt_getbyte(pkt, &uc)) {
256         sftp_pkt_free(pkt);
257         return NULL;
258     } else {
259         pkt->type = uc;
260     }
261
262     return pkt;
263 }
264
265 /* ----------------------------------------------------------------------
266  * Request ID allocation and temporary dispatch routines.
267  */
268
269 #define REQUEST_ID_OFFSET 256
270
271 struct sftp_request {
272     unsigned id;
273     int registered;
274     void *userdata;
275 };
276
277 static int sftp_reqcmp(void *av, void *bv)
278 {
279     struct sftp_request *a = (struct sftp_request *)av;
280     struct sftp_request *b = (struct sftp_request *)bv;
281     if (a->id < b->id)
282         return -1;
283     if (a->id > b->id)
284         return +1;
285     return 0;
286 }
287 static int sftp_reqfind(void *av, void *bv)
288 {
289     unsigned *a = (unsigned *) av;
290     struct sftp_request *b = (struct sftp_request *)bv;
291     if (*a < b->id)
292         return -1;
293     if (*a > b->id)
294         return +1;
295     return 0;
296 }
297
298 static tree234 *sftp_requests;
299
300 static struct sftp_request *sftp_alloc_request(void)
301 {
302     unsigned low, high, mid;
303     int tsize;
304     struct sftp_request *r;
305
306     if (sftp_requests == NULL)
307         sftp_requests = newtree234(sftp_reqcmp);
308
309     /*
310      * First-fit allocation of request IDs: always pick the lowest
311      * unused one. To do this, binary-search using the counted
312      * B-tree to find the largest ID which is in a contiguous
313      * sequence from the beginning. (Precisely everything in that
314      * sequence must have ID equal to its tree index plus
315      * REQUEST_ID_OFFSET.)
316      */
317     tsize = count234(sftp_requests);
318
319     low = -1;
320     high = tsize;
321     while (high - low > 1) {
322         mid = (high + low) / 2;
323         r = index234(sftp_requests, mid);
324         if (r->id == mid + REQUEST_ID_OFFSET)
325             low = mid;                 /* this one is fine */
326         else
327             high = mid;                /* this one is past it */
328     }
329     /*
330      * Now low points to either -1, or the tree index of the
331      * largest ID in the initial sequence.
332      */
333     {
334         unsigned i = low + 1 + REQUEST_ID_OFFSET;
335         assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
336     }
337
338     /*
339      * So the request ID we need to create is
340      * low + 1 + REQUEST_ID_OFFSET.
341      */
342     r = snew(struct sftp_request);
343     r->id = low + 1 + REQUEST_ID_OFFSET;
344     r->registered = 0;
345     r->userdata = NULL;
346     add234(sftp_requests, r);
347     return r;
348 }
349
350 void sftp_cleanup_request(void)
351 {
352     if (sftp_requests == NULL) {
353         freetree234(sftp_requests);
354         sftp_requests = NULL;
355     }
356 }
357
358 void sftp_register(struct sftp_request *req)
359 {
360     req->registered = 1;
361 }
362
363 struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
364 {
365     unsigned long id;
366     struct sftp_request *req;
367
368     if (!pktin) {
369         fxp_internal_error("did not receive a valid SFTP packet\n");
370         return NULL;
371     }
372
373     if (!sftp_pkt_getuint32(pktin, &id)) {
374         fxp_internal_error("did not receive a valid SFTP packet\n");
375         return NULL;
376     }
377     req = find234(sftp_requests, &id, sftp_reqfind);
378
379     if (!req || !req->registered) {
380         fxp_internal_error("request ID mismatch\n");
381         sftp_pkt_free(pktin);
382         return NULL;
383     }
384
385     del234(sftp_requests, req);
386
387     return req;
388 }
389
390 /* ----------------------------------------------------------------------
391  * String handling routines.
392  */
393
394 static char *mkstr(char *s, int len)
395 {
396     char *p = snewn(len + 1, char);
397     memcpy(p, s, len);
398     p[len] = '\0';
399     return p;
400 }
401
402 /* ----------------------------------------------------------------------
403  * SFTP primitives.
404  */
405
406 /*
407  * Deal with (and free) an FXP_STATUS packet. Return 1 if
408  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
409  * Also place the status into fxp_errtype.
410  */
411 static int fxp_got_status(struct sftp_packet *pktin)
412 {
413     static const char *const messages[] = {
414         /* SSH_FX_OK. The only time we will display a _message_ for this
415          * is if we were expecting something other than FXP_STATUS on
416          * success, so this is actually an error message! */
417         "unexpected OK response",
418         "end of file",
419         "no such file or directory",
420         "permission denied",
421         "failure",
422         "bad message",
423         "no connection",
424         "connection lost",
425         "operation unsupported",
426     };
427
428     if (pktin->type != SSH_FXP_STATUS) {
429         fxp_error_message = "expected FXP_STATUS packet";
430         fxp_errtype = -1;
431     } else {
432         unsigned long ul;
433         if (!sftp_pkt_getuint32(pktin, &ul)) {
434             fxp_error_message = "malformed FXP_STATUS packet";
435             fxp_errtype = -1;
436         } else {
437             fxp_errtype = ul;
438             if (fxp_errtype < 0 ||
439                 fxp_errtype >= sizeof(messages) / sizeof(*messages))
440                 fxp_error_message = "unknown error code";
441             else
442                 fxp_error_message = messages[fxp_errtype];
443         }
444     }
445
446     if (fxp_errtype == SSH_FX_OK)
447         return 1;
448     else if (fxp_errtype == SSH_FX_EOF)
449         return 0;
450     else
451         return -1;
452 }
453
454 static void fxp_internal_error(char *msg)
455 {
456     fxp_error_message = msg;
457     fxp_errtype = -1;
458 }
459
460 const char *fxp_error(void)
461 {
462     return fxp_error_message;
463 }
464
465 int fxp_error_type(void)
466 {
467     return fxp_errtype;
468 }
469
470 /*
471  * Perform exchange of init/version packets. Return 0 on failure.
472  */
473 int fxp_init(void)
474 {
475     struct sftp_packet *pktout, *pktin;
476     unsigned long remotever;
477
478     pktout = sftp_pkt_init(SSH_FXP_INIT);
479     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
480     sftp_send(pktout);
481
482     pktin = sftp_recv();
483     if (!pktin) {
484         fxp_internal_error("could not connect");
485         return 0;
486     }
487     if (pktin->type != SSH_FXP_VERSION) {
488         fxp_internal_error("did not receive FXP_VERSION");
489         sftp_pkt_free(pktin);
490         return 0;
491     }
492     if (!sftp_pkt_getuint32(pktin, &remotever)) {
493         fxp_internal_error("malformed FXP_VERSION packet");
494         sftp_pkt_free(pktin);
495         return 0;
496     }
497     if (remotever > SFTP_PROTO_VERSION) {
498         fxp_internal_error
499             ("remote protocol is more advanced than we support");
500         sftp_pkt_free(pktin);
501         return 0;
502     }
503     /*
504      * In principle, this packet might also contain extension-
505      * string pairs. We should work through them and look for any
506      * we recognise. In practice we don't currently do so because
507      * we know we don't recognise _any_.
508      */
509     sftp_pkt_free(pktin);
510
511     return 1;
512 }
513
514 /*
515  * Canonify a pathname.
516  */
517 struct sftp_request *fxp_realpath_send(char *path)
518 {
519     struct sftp_request *req = sftp_alloc_request();
520     struct sftp_packet *pktout;
521
522     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
523     sftp_pkt_adduint32(pktout, req->id);
524     sftp_pkt_addstring_start(pktout);
525     sftp_pkt_addstring_str(pktout, path);
526     sftp_send(pktout);
527
528     return req;
529 }
530
531 char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req)
532 {
533     sfree(req);
534
535     if (pktin->type == SSH_FXP_NAME) {
536         unsigned long count;
537         char *path;
538         int len;
539
540         if (!sftp_pkt_getuint32(pktin, &count) || count != 1) {
541             fxp_internal_error("REALPATH did not return name count of 1\n");
542             sftp_pkt_free(pktin);
543             return NULL;
544         }
545         if (!sftp_pkt_getstring(pktin, &path, &len)) {
546             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
547             sftp_pkt_free(pktin);
548             return NULL;
549         }
550         path = mkstr(path, len);
551         sftp_pkt_free(pktin);
552         return path;
553     } else {
554         fxp_got_status(pktin);
555         sftp_pkt_free(pktin);
556         return NULL;
557     }
558 }
559
560 /*
561  * Open a file.
562  */
563 struct sftp_request *fxp_open_send(char *path, int type)
564 {
565     struct sftp_request *req = sftp_alloc_request();
566     struct sftp_packet *pktout;
567
568     pktout = sftp_pkt_init(SSH_FXP_OPEN);
569     sftp_pkt_adduint32(pktout, req->id);
570     sftp_pkt_addstring(pktout, path);
571     sftp_pkt_adduint32(pktout, type);
572     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
573     sftp_send(pktout);
574
575     return req;
576 }
577
578 struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin,
579                                  struct sftp_request *req)
580 {
581     sfree(req);
582
583     if (pktin->type == SSH_FXP_HANDLE) {
584         char *hstring;
585         struct fxp_handle *handle;
586         int len;
587
588         if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
589             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
590             sftp_pkt_free(pktin);
591             return NULL;
592         }
593         handle = snew(struct fxp_handle);
594         handle->hstring = mkstr(hstring, len);
595         handle->hlen = len;
596         sftp_pkt_free(pktin);
597         return handle;
598     } else {
599         fxp_got_status(pktin);
600         sftp_pkt_free(pktin);
601         return NULL;
602     }
603 }
604
605 /*
606  * Open a directory.
607  */
608 struct sftp_request *fxp_opendir_send(char *path)
609 {
610     struct sftp_request *req = sftp_alloc_request();
611     struct sftp_packet *pktout;
612
613     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
614     sftp_pkt_adduint32(pktout, req->id);
615     sftp_pkt_addstring(pktout, path);
616     sftp_send(pktout);
617
618     return req;
619 }
620
621 struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin,
622                                     struct sftp_request *req)
623 {
624     sfree(req);
625     if (pktin->type == SSH_FXP_HANDLE) {
626         char *hstring;
627         struct fxp_handle *handle;
628         int len;
629
630         if (!sftp_pkt_getstring(pktin, &hstring, &len)) {
631             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
632             sftp_pkt_free(pktin);
633             return NULL;
634         }
635         handle = snew(struct fxp_handle);
636         handle->hstring = mkstr(hstring, len);
637         handle->hlen = len;
638         sftp_pkt_free(pktin);
639         return handle;
640     } else {
641         fxp_got_status(pktin);
642         sftp_pkt_free(pktin);
643         return NULL;
644     }
645 }
646
647 /*
648  * Close a file/dir.
649  */
650 struct sftp_request *fxp_close_send(struct fxp_handle *handle)
651 {
652     struct sftp_request *req = sftp_alloc_request();
653     struct sftp_packet *pktout;
654
655     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
656     sftp_pkt_adduint32(pktout, req->id);
657     sftp_pkt_addstring_start(pktout);
658     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
659     sftp_send(pktout);
660
661     sfree(handle->hstring);
662     sfree(handle);
663
664     return req;
665 }
666
667 void fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req)
668 {
669     sfree(req);
670     fxp_got_status(pktin);
671     sftp_pkt_free(pktin);
672 }
673
674 struct sftp_request *fxp_mkdir_send(char *path)
675 {
676     struct sftp_request *req = sftp_alloc_request();
677     struct sftp_packet *pktout;
678
679     pktout = sftp_pkt_init(SSH_FXP_MKDIR);
680     sftp_pkt_adduint32(pktout, req->id);
681     sftp_pkt_addstring(pktout, path);
682     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
683     sftp_send(pktout);
684
685     return req;
686 }
687
688 int fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
689 {
690     int id;
691     sfree(req);
692     id = fxp_got_status(pktin);
693     sftp_pkt_free(pktin);
694     if (id != 1) {
695         return 0;
696     }
697     return 1;
698 }
699
700 struct sftp_request *fxp_rmdir_send(char *path)
701 {
702     struct sftp_request *req = sftp_alloc_request();
703     struct sftp_packet *pktout;
704
705     pktout = sftp_pkt_init(SSH_FXP_RMDIR);
706     sftp_pkt_adduint32(pktout, req->id);
707     sftp_pkt_addstring(pktout, path);
708     sftp_send(pktout);
709
710     return req;
711 }
712
713 int fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req)
714 {
715     int id;
716     sfree(req);
717     id = fxp_got_status(pktin);
718     sftp_pkt_free(pktin);
719     if (id != 1) {
720         return 0;
721     }
722     return 1;
723 }
724
725 struct sftp_request *fxp_remove_send(char *fname)
726 {
727     struct sftp_request *req = sftp_alloc_request();
728     struct sftp_packet *pktout;
729
730     pktout = sftp_pkt_init(SSH_FXP_REMOVE);
731     sftp_pkt_adduint32(pktout, req->id);
732     sftp_pkt_addstring(pktout, fname);
733     sftp_send(pktout);
734
735     return req;
736 }
737
738 int fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req)
739 {
740     int id;
741     sfree(req);
742     id = fxp_got_status(pktin);
743     sftp_pkt_free(pktin);
744     if (id != 1) {
745         return 0;
746     }
747     return 1;
748 }
749
750 struct sftp_request *fxp_rename_send(char *srcfname, char *dstfname)
751 {
752     struct sftp_request *req = sftp_alloc_request();
753     struct sftp_packet *pktout;
754
755     pktout = sftp_pkt_init(SSH_FXP_RENAME);
756     sftp_pkt_adduint32(pktout, req->id);
757     sftp_pkt_addstring(pktout, srcfname);
758     sftp_pkt_addstring(pktout, dstfname);
759     sftp_send(pktout);
760
761     return req;
762 }
763
764 int fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req)
765 {
766     int id;
767     sfree(req);
768     id = fxp_got_status(pktin);
769     sftp_pkt_free(pktin);
770     if (id != 1) {
771         return 0;
772     }
773     return 1;
774 }
775
776 /*
777  * Retrieve the attributes of a file. We have fxp_stat which works
778  * on filenames, and fxp_fstat which works on open file handles.
779  */
780 struct sftp_request *fxp_stat_send(char *fname)
781 {
782     struct sftp_request *req = sftp_alloc_request();
783     struct sftp_packet *pktout;
784
785     pktout = sftp_pkt_init(SSH_FXP_STAT);
786     sftp_pkt_adduint32(pktout, req->id);
787     sftp_pkt_addstring(pktout, fname);
788     sftp_send(pktout);
789
790     return req;
791 }
792
793 int fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req,
794                   struct fxp_attrs *attrs)
795 {
796     sfree(req);
797     if (pktin->type == SSH_FXP_ATTRS) {
798         if (!sftp_pkt_getattrs(pktin, attrs)) {
799             fxp_internal_error("malformed SSH_FXP_ATTRS packet");
800             sftp_pkt_free(pktin);
801             return 0;
802         }
803         sftp_pkt_free(pktin);
804         return 1;
805     } else {
806         fxp_got_status(pktin);
807         sftp_pkt_free(pktin);
808         return 0;
809     }
810 }
811
812 struct sftp_request *fxp_fstat_send(struct fxp_handle *handle)
813 {
814     struct sftp_request *req = sftp_alloc_request();
815     struct sftp_packet *pktout;
816
817     pktout = sftp_pkt_init(SSH_FXP_FSTAT);
818     sftp_pkt_adduint32(pktout, req->id);
819     sftp_pkt_addstring_start(pktout);
820     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
821     sftp_send(pktout);
822
823     return req;
824 }
825
826 int fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req,
827                    struct fxp_attrs *attrs)
828 {
829     sfree(req);
830     if (pktin->type == SSH_FXP_ATTRS) {
831         if (!sftp_pkt_getattrs(pktin, attrs)) {
832             fxp_internal_error("malformed SSH_FXP_ATTRS packet");
833             sftp_pkt_free(pktin);
834             return 0;
835         }
836         sftp_pkt_free(pktin);
837         return 1;
838     } else {
839         fxp_got_status(pktin);
840         sftp_pkt_free(pktin);
841         return 0;
842     }
843 }
844
845 /*
846  * Set the attributes of a file.
847  */
848 struct sftp_request *fxp_setstat_send(char *fname, struct fxp_attrs attrs)
849 {
850     struct sftp_request *req = sftp_alloc_request();
851     struct sftp_packet *pktout;
852
853     pktout = sftp_pkt_init(SSH_FXP_SETSTAT);
854     sftp_pkt_adduint32(pktout, req->id);
855     sftp_pkt_addstring(pktout, fname);
856     sftp_pkt_addattrs(pktout, attrs);
857     sftp_send(pktout);
858
859     return req;
860 }
861
862 int fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
863 {
864     int id;
865     sfree(req);
866     id = fxp_got_status(pktin);
867     sftp_pkt_free(pktin);
868     if (id != 1) {
869         return 0;
870     }
871     return 1;
872 }
873
874 struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle,
875                                        struct fxp_attrs attrs)
876 {
877     struct sftp_request *req = sftp_alloc_request();
878     struct sftp_packet *pktout;
879
880     pktout = sftp_pkt_init(SSH_FXP_FSETSTAT);
881     sftp_pkt_adduint32(pktout, req->id);
882     sftp_pkt_addstring_start(pktout);
883     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
884     sftp_pkt_addattrs(pktout, attrs);
885     sftp_send(pktout);
886
887     return req;
888 }
889
890 int fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req)
891 {
892     int id;
893     sfree(req);
894     id = fxp_got_status(pktin);
895     sftp_pkt_free(pktin);
896     if (id != 1) {
897         return 0;
898     }
899     return 1;
900 }
901
902 /*
903  * Read from a file. Returns the number of bytes read, or -1 on an
904  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
905  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
906  * error indicator. It might even depend on the SFTP server.)
907  */
908 struct sftp_request *fxp_read_send(struct fxp_handle *handle,
909                                    uint64 offset, int len)
910 {
911     struct sftp_request *req = sftp_alloc_request();
912     struct sftp_packet *pktout;
913
914     pktout = sftp_pkt_init(SSH_FXP_READ);
915     sftp_pkt_adduint32(pktout, req->id);
916     sftp_pkt_addstring_start(pktout);
917     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
918     sftp_pkt_adduint64(pktout, offset);
919     sftp_pkt_adduint32(pktout, len);
920     sftp_send(pktout);
921
922     return req;
923 }
924
925 int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req,
926                   char *buffer, int len)
927 {
928     sfree(req);
929     if (pktin->type == SSH_FXP_DATA) {
930         char *str;
931         int rlen;
932
933         if (!sftp_pkt_getstring(pktin, &str, &rlen)) {
934             fxp_internal_error("READ returned malformed SSH_FXP_DATA packet");
935             sftp_pkt_free(pktin);
936             return -1;
937         }
938
939         if (rlen > len || rlen < 0) {
940             fxp_internal_error("READ returned more bytes than requested");
941             sftp_pkt_free(pktin);
942             return -1;
943         }
944
945         memcpy(buffer, str, rlen);
946         sftp_pkt_free(pktin);
947         return rlen;
948     } else {
949         fxp_got_status(pktin);
950         sftp_pkt_free(pktin);
951         return -1;
952     }
953 }
954
955 /*
956  * Read from a directory.
957  */
958 struct sftp_request *fxp_readdir_send(struct fxp_handle *handle)
959 {
960     struct sftp_request *req = sftp_alloc_request();
961     struct sftp_packet *pktout;
962
963     pktout = sftp_pkt_init(SSH_FXP_READDIR);
964     sftp_pkt_adduint32(pktout, req->id);
965     sftp_pkt_addstring_start(pktout);
966     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
967     sftp_send(pktout);
968
969     return req;
970 }
971
972 struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin,
973                                    struct sftp_request *req)
974 {
975     sfree(req);
976     if (pktin->type == SSH_FXP_NAME) {
977         struct fxp_names *ret;
978         unsigned long i;
979
980         /*
981          * Sanity-check the number of names. Minimum is obviously
982          * zero. Maximum is the remaining space in the packet
983          * divided by the very minimum length of a name, which is
984          * 12 bytes (4 for an empty filename, 4 for an empty
985          * longname, 4 for a set of attribute flags indicating that
986          * no other attributes are supplied).
987          */
988         if (!sftp_pkt_getuint32(pktin, &i) ||
989             i > (pktin->length-pktin->savedpos)/12) {
990             fxp_internal_error("malformed FXP_NAME packet");
991             sftp_pkt_free(pktin);
992             return NULL;
993         }
994
995         /*
996          * Ensure the implicit multiplication in the snewn() call
997          * doesn't suffer integer overflow and cause us to malloc
998          * too little space.
999          */
1000         if (i > INT_MAX / sizeof(struct fxp_name)) {
1001             fxp_internal_error("unreasonably large FXP_NAME packet");
1002             sftp_pkt_free(pktin);
1003             return NULL;
1004         }
1005
1006         ret = snew(struct fxp_names);
1007         ret->nnames = i;
1008         ret->names = snewn(ret->nnames, struct fxp_name);
1009         for (i = 0; i < ret->nnames; i++) {
1010             char *str1, *str2;
1011             int len1, len2;
1012             if (!sftp_pkt_getstring(pktin, &str1, &len1) ||
1013                 !sftp_pkt_getstring(pktin, &str2, &len2) ||
1014                 !sftp_pkt_getattrs(pktin, &ret->names[i].attrs)) {
1015                 fxp_internal_error("malformed FXP_NAME packet");
1016                 while (i--) {
1017                     sfree(ret->names[i].filename);
1018                     sfree(ret->names[i].longname);
1019                 }
1020                 sfree(ret->names);
1021                 sfree(ret);
1022                 sfree(pktin);
1023                 return NULL;
1024             }
1025             ret->names[i].filename = mkstr(str1, len1);
1026             ret->names[i].longname = mkstr(str2, len2);
1027         }
1028         sftp_pkt_free(pktin);
1029         return ret;
1030     } else {
1031         fxp_got_status(pktin);
1032         sftp_pkt_free(pktin);
1033         return NULL;
1034     }
1035 }
1036
1037 /*
1038  * Write to a file. Returns 0 on error, 1 on OK.
1039  */
1040 struct sftp_request *fxp_write_send(struct fxp_handle *handle,
1041                                     char *buffer, uint64 offset, int len)
1042 {
1043     struct sftp_request *req = sftp_alloc_request();
1044     struct sftp_packet *pktout;
1045
1046     pktout = sftp_pkt_init(SSH_FXP_WRITE);
1047     sftp_pkt_adduint32(pktout, req->id);
1048     sftp_pkt_addstring_start(pktout);
1049     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
1050     sftp_pkt_adduint64(pktout, offset);
1051     sftp_pkt_addstring_start(pktout);
1052     sftp_pkt_addstring_data(pktout, buffer, len);
1053     sftp_send(pktout);
1054
1055     return req;
1056 }
1057
1058 int fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req)
1059 {
1060     sfree(req);
1061     fxp_got_status(pktin);
1062     sftp_pkt_free(pktin);
1063     return fxp_errtype == SSH_FX_OK;
1064 }
1065
1066 /*
1067  * Free up an fxp_names structure.
1068  */
1069 void fxp_free_names(struct fxp_names *names)
1070 {
1071     int i;
1072
1073     for (i = 0; i < names->nnames; i++) {
1074         sfree(names->names[i].filename);
1075         sfree(names->names[i].longname);
1076     }
1077     sfree(names->names);
1078     sfree(names);
1079 }
1080
1081 /*
1082  * Duplicate an fxp_name structure.
1083  */
1084 struct fxp_name *fxp_dup_name(struct fxp_name *name)
1085 {
1086     struct fxp_name *ret;
1087     ret = snew(struct fxp_name);
1088     ret->filename = dupstr(name->filename);
1089     ret->longname = dupstr(name->longname);
1090     ret->attrs = name->attrs;          /* structure copy */
1091     return ret;
1092 }
1093
1094 /*
1095  * Free up an fxp_name structure.
1096  */
1097 void fxp_free_name(struct fxp_name *name)
1098 {
1099     sfree(name->filename);
1100     sfree(name->longname);
1101     sfree(name);
1102 }
1103
1104 /*
1105  * Store user data in an sftp_request structure.
1106  */
1107 void *fxp_get_userdata(struct sftp_request *req)
1108 {
1109     return req->userdata;
1110 }
1111
1112 void fxp_set_userdata(struct sftp_request *req, void *data)
1113 {
1114     req->userdata = data;
1115 }
1116
1117 /*
1118  * A wrapper to go round fxp_read_* and fxp_write_*, which manages
1119  * the queueing of multiple read/write requests.
1120  */
1121
1122 struct req {
1123     char *buffer;
1124     int len, retlen, complete;
1125     uint64 offset;
1126     struct req *next, *prev;
1127 };
1128
1129 struct fxp_xfer {
1130     uint64 offset, furthestdata, filesize;
1131     int req_totalsize, req_maxsize, eof, err;
1132     struct fxp_handle *fh;
1133     struct req *head, *tail;
1134 };
1135
1136 static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64 offset)
1137 {
1138     struct fxp_xfer *xfer = snew(struct fxp_xfer);
1139
1140     xfer->fh = fh;
1141     xfer->offset = offset;
1142     xfer->head = xfer->tail = NULL;
1143     xfer->req_totalsize = 0;
1144     xfer->req_maxsize = 16384;
1145     xfer->err = 0;
1146     xfer->filesize = uint64_make(ULONG_MAX, ULONG_MAX);
1147     xfer->furthestdata = uint64_make(0, 0);
1148
1149     return xfer;
1150 }
1151
1152 int xfer_done(struct fxp_xfer *xfer)
1153 {
1154     /*
1155      * We're finished if we've seen EOF _and_ there are no
1156      * outstanding requests.
1157      */
1158     return (xfer->eof || xfer->err) && !xfer->head;
1159 }
1160
1161 void xfer_download_queue(struct fxp_xfer *xfer)
1162 {
1163     while (xfer->req_totalsize < xfer->req_maxsize &&
1164            !xfer->eof && !xfer->err) {
1165         /*
1166          * Queue a new read request.
1167          */
1168         struct req *rr;
1169         struct sftp_request *req;
1170
1171         rr = snew(struct req);
1172         rr->offset = xfer->offset;
1173         rr->complete = 0;
1174         if (xfer->tail) {
1175             xfer->tail->next = rr;
1176             rr->prev = xfer->tail;
1177         } else {
1178             xfer->head = rr;
1179             rr->prev = NULL;
1180         }
1181         xfer->tail = rr;
1182         rr->next = NULL;
1183
1184         rr->len = 4096;
1185         rr->buffer = snewn(rr->len, char);
1186         sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len));
1187         fxp_set_userdata(req, rr);
1188
1189         xfer->offset = uint64_add32(xfer->offset, rr->len);
1190         xfer->req_totalsize += rr->len;
1191
1192 #ifdef DEBUG_DOWNLOAD
1193         { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing read request %p at %s\n", rr, buf); }
1194 #endif
1195     }
1196 }
1197
1198 struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64 offset)
1199 {
1200     struct fxp_xfer *xfer = xfer_init(fh, offset);
1201
1202     xfer->eof = FALSE;
1203     xfer_download_queue(xfer);
1204
1205     return xfer;
1206 }
1207
1208 int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1209 {
1210     struct sftp_request *rreq;
1211     struct req *rr;
1212
1213     rreq = sftp_find_request(pktin);
1214     rr = (struct req *)fxp_get_userdata(rreq);
1215     if (!rr)
1216         return 0;                      /* this packet isn't ours */
1217     rr->retlen = fxp_read_recv(pktin, rreq, rr->buffer, rr->len);
1218 #ifdef DEBUG_DOWNLOAD
1219     printf("read request %p has returned [%d]\n", rr, rr->retlen);
1220 #endif
1221
1222     if ((rr->retlen < 0 && fxp_error_type()==SSH_FX_EOF) || rr->retlen == 0) {
1223         xfer->eof = TRUE;
1224         rr->complete = -1;
1225 #ifdef DEBUG_DOWNLOAD
1226         printf("setting eof\n");
1227 #endif
1228     } else if (rr->retlen < 0) {
1229         /* some error other than EOF; signal it back to caller */
1230         xfer_set_error(xfer);
1231         rr->complete = -1;
1232         return -1;
1233     }
1234
1235     rr->complete = 1;
1236
1237     /*
1238      * Special case: if we have received fewer bytes than we
1239      * actually read, we should do something. For the moment I'll
1240      * just throw an ersatz FXP error to signal this; the SFTP
1241      * draft I've got says that it can't happen except on special
1242      * files, in which case seeking probably has very little
1243      * meaning and so queueing an additional read request to fill
1244      * up the gap sounds like the wrong answer. I'm not sure what I
1245      * should be doing here - if it _was_ a special file, I suspect
1246      * I simply shouldn't have been queueing multiple requests in
1247      * the first place...
1248      */
1249     if (rr->retlen > 0 && uint64_compare(xfer->furthestdata, rr->offset) < 0) {
1250         xfer->furthestdata = rr->offset;
1251 #ifdef DEBUG_DOWNLOAD
1252         { char buf[40];
1253         uint64_decimal(xfer->furthestdata, buf);
1254         printf("setting furthestdata = %s\n", buf); }
1255 #endif
1256     }
1257
1258     if (rr->retlen < rr->len) {
1259         uint64 filesize = uint64_add32(rr->offset,
1260                                        (rr->retlen < 0 ? 0 : rr->retlen));
1261 #ifdef DEBUG_DOWNLOAD
1262         { char buf[40];
1263         uint64_decimal(filesize, buf);
1264         printf("short block! trying filesize = %s\n", buf); }
1265 #endif
1266         if (uint64_compare(xfer->filesize, filesize) > 0) {
1267             xfer->filesize = filesize;
1268 #ifdef DEBUG_DOWNLOAD
1269             printf("actually changing filesize\n");
1270 #endif      
1271         }
1272     }
1273
1274     if (uint64_compare(xfer->furthestdata, xfer->filesize) > 0) {
1275         fxp_error_message = "received a short buffer from FXP_READ, but not"
1276             " at EOF";
1277         fxp_errtype = -1;
1278         xfer_set_error(xfer);
1279         return -1;
1280     }
1281
1282     return 1;
1283 }
1284
1285 void xfer_set_error(struct fxp_xfer *xfer)
1286 {
1287     xfer->err = 1;
1288 }
1289
1290 int xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len)
1291 {
1292     void *retbuf = NULL;
1293     int retlen = 0;
1294
1295     /*
1296      * Discard anything at the head of the rr queue with complete <
1297      * 0; return the first thing with complete > 0.
1298      */
1299     while (xfer->head && xfer->head->complete && !retbuf) {
1300         struct req *rr = xfer->head;
1301
1302         if (rr->complete > 0) {
1303             retbuf = rr->buffer;
1304             retlen = rr->retlen;
1305 #ifdef DEBUG_DOWNLOAD
1306             printf("handing back data from read request %p\n", rr);
1307 #endif
1308         }
1309 #ifdef DEBUG_DOWNLOAD
1310         else
1311             printf("skipping failed read request %p\n", rr);
1312 #endif
1313
1314         xfer->head = xfer->head->next;
1315         if (xfer->head)
1316             xfer->head->prev = NULL;
1317         else
1318             xfer->tail = NULL;
1319         xfer->req_totalsize -= rr->len;
1320         sfree(rr);
1321     }
1322
1323     if (retbuf) {
1324         *buf = retbuf;
1325         *len = retlen;
1326         return 1;
1327     } else
1328         return 0;
1329 }
1330
1331 struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64 offset)
1332 {
1333     struct fxp_xfer *xfer = xfer_init(fh, offset);
1334
1335     /*
1336      * We set `eof' to 1 because this will cause xfer_done() to
1337      * return true iff there are no outstanding requests. During an
1338      * upload, our caller will be responsible for working out
1339      * whether all the data has been sent, so all it needs to know
1340      * from us is whether the outstanding requests have been
1341      * handled once that's done.
1342      */
1343     xfer->eof = 1;
1344
1345     return xfer;
1346 }
1347
1348 int xfer_upload_ready(struct fxp_xfer *xfer)
1349 {
1350     if (xfer->req_totalsize < xfer->req_maxsize)
1351         return 1;
1352     else
1353         return 0;
1354 }
1355
1356 void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len)
1357 {
1358     struct req *rr;
1359     struct sftp_request *req;
1360
1361     rr = snew(struct req);
1362     rr->offset = xfer->offset;
1363     rr->complete = 0;
1364     if (xfer->tail) {
1365         xfer->tail->next = rr;
1366         rr->prev = xfer->tail;
1367     } else {
1368         xfer->head = rr;
1369         rr->prev = NULL;
1370     }
1371     xfer->tail = rr;
1372     rr->next = NULL;
1373
1374     rr->len = len;
1375     rr->buffer = NULL;
1376     sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len));
1377     fxp_set_userdata(req, rr);
1378
1379     xfer->offset = uint64_add32(xfer->offset, rr->len);
1380     xfer->req_totalsize += rr->len;
1381
1382 #ifdef DEBUG_UPLOAD
1383     { char buf[40]; uint64_decimal(rr->offset, buf); printf("queueing write request %p at %s [len %d]\n", rr, buf, len); }
1384 #endif
1385 }
1386
1387 int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin)
1388 {
1389     struct sftp_request *rreq;
1390     struct req *rr, *prev, *next;
1391     int ret;
1392
1393     rreq = sftp_find_request(pktin);
1394     rr = (struct req *)fxp_get_userdata(rreq);
1395     if (!rr)
1396         return 0;                      /* this packet isn't ours */
1397     ret = fxp_write_recv(pktin, rreq);
1398 #ifdef DEBUG_UPLOAD
1399     printf("write request %p has returned [%d]\n", rr, ret);
1400 #endif
1401
1402     /*
1403      * Remove this one from the queue.
1404      */
1405     prev = rr->prev;
1406     next = rr->next;
1407     if (prev)
1408         prev->next = next;
1409     else
1410         xfer->head = next;
1411     if (next)
1412         next->prev = prev;
1413     else
1414         xfer->tail = prev;
1415     xfer->req_totalsize -= rr->len;
1416     sfree(rr);
1417
1418     if (!ret)
1419         return -1;
1420
1421     return 1;
1422 }
1423
1424 void xfer_cleanup(struct fxp_xfer *xfer)
1425 {
1426     struct req *rr;
1427     while (xfer->head) {
1428         rr = xfer->head;
1429         xfer->head = xfer->head->next;
1430         sfree(rr->buffer);
1431         sfree(rr);
1432     }
1433     sfree(xfer);
1434 }