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