Sebastian Kuschel reports that pfd_closing can be called for a socket
[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 ((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 }