source: filezilla/trunk/fuentes/src/putty/sftp.c @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

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