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