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