Per Gunnar Floe spotted a reversed test in sftp_cleanup_requests().
[u/mdw/putty] / 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 }