summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2010-09-24 22:15:11 +1000
committerDamien Miller <djm@mindrot.org>2010-09-24 22:15:11 +1000
commit65e42f87fe945a2bf30d7e02358554dbaefa8a4c (patch)
tree102c10a0b5328a40c79dca19d208f0ca0c1671b5
parent7fe2b1fec3b364faf952828f3875b8e7eed8feb4 (diff)
- djm@cvs.openbsd.org 2010/09/22 22:58:51
[atomicio.c atomicio.h misc.c misc.h scp.c sftp-client.c] [sftp-client.h sftp.1 sftp.c] add an option per-read/write callback to atomicio factor out bandwidth limiting code from scp(1) into a generic bandwidth limiter that can be attached using the atomicio callback mechanism add a bandwidth limit option to sftp(1) using the above "very nice" markus@
-rw-r--r--ChangeLog10
-rw-r--r--atomicio.c33
-rw-r--r--atomicio.h8
-rw-r--r--misc.c66
-rw-r--r--misc.h11
-rw-r--r--scp.c122
-rw-r--r--sftp-client.c219
-rw-r--r--sftp-client.h4
-rw-r--r--sftp.17
-rw-r--r--sftp.c15
10 files changed, 282 insertions, 213 deletions
diff --git a/ChangeLog b/ChangeLog
index 5f3914007..b3338dcb7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -44,6 +44,16 @@
44 ssh_config.5: format the kexalgorithms in a more consistent 44 ssh_config.5: format the kexalgorithms in a more consistent
45 (prettier!) way 45 (prettier!) way
46 ok djm 46 ok djm
47 - djm@cvs.openbsd.org 2010/09/22 22:58:51
48 [atomicio.c atomicio.h misc.c misc.h scp.c sftp-client.c]
49 [sftp-client.h sftp.1 sftp.c]
50 add an option per-read/write callback to atomicio
51
52 factor out bandwidth limiting code from scp(1) into a generic bandwidth
53 limiter that can be attached using the atomicio callback mechanism
54
55 add a bandwidth limit option to sftp(1) using the above
56 "very nice" markus@
47 57
4820100910 5820100910
49 - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact 59 - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact
diff --git a/atomicio.c b/atomicio.c
index a6b2d127a..601b3c371 100644
--- a/atomicio.c
+++ b/atomicio.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: atomicio.c,v 1.25 2007/06/25 12:02:27 dtucker Exp $ */ 1/* $OpenBSD: atomicio.c,v 1.26 2010/09/22 22:58:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2006 Damien Miller. All rights reserved. 3 * Copyright (c) 2006 Damien Miller. All rights reserved.
4 * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. 4 * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
@@ -48,7 +48,8 @@
48 * ensure all of data on socket comes through. f==read || f==vwrite 48 * ensure all of data on socket comes through. f==read || f==vwrite
49 */ 49 */
50size_t 50size_t
51atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) 51atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
52 int (*cb)(void *, size_t), void *cb_arg)
52{ 53{
53 char *s = _s; 54 char *s = _s;
54 size_t pos = 0; 55 size_t pos = 0;
@@ -73,17 +74,28 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
73 return pos; 74 return pos;
74 default: 75 default:
75 pos += (size_t)res; 76 pos += (size_t)res;
77 if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
78 errno = EINTR;
79 return pos;
80 }
76 } 81 }
77 } 82 }
78 return (pos); 83 return pos;
84}
85
86size_t
87atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
88{
89 return atomicio6(f, fd, _s, n, NULL, NULL);
79} 90}
80 91
81/* 92/*
82 * ensure all of data on socket comes through. f==readv || f==writev 93 * ensure all of data on socket comes through. f==readv || f==writev
83 */ 94 */
84size_t 95size_t
85atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd, 96atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
86 const struct iovec *_iov, int iovcnt) 97 const struct iovec *_iov, int iovcnt,
98 int (*cb)(void *, size_t), void *cb_arg)
87{ 99{
88 size_t pos = 0, rem; 100 size_t pos = 0, rem;
89 ssize_t res; 101 ssize_t res;
@@ -137,6 +149,17 @@ atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
137 iov[0].iov_base = ((char *)iov[0].iov_base) + rem; 149 iov[0].iov_base = ((char *)iov[0].iov_base) + rem;
138 iov[0].iov_len -= rem; 150 iov[0].iov_len -= rem;
139 } 151 }
152 if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
153 errno = EINTR;
154 return pos;
155 }
140 } 156 }
141 return pos; 157 return pos;
142} 158}
159
160size_t
161atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
162 const struct iovec *_iov, int iovcnt)
163{
164 return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL);
165}
diff --git a/atomicio.h b/atomicio.h
index 2fcd25d43..0d728ac86 100644
--- a/atomicio.h
+++ b/atomicio.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: atomicio.h,v 1.10 2006/08/03 03:34:41 deraadt Exp $ */ 1/* $OpenBSD: atomicio.h,v 1.11 2010/09/22 22:58:51 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2006 Damien Miller. All rights reserved.
@@ -32,6 +32,9 @@
32/* 32/*
33 * Ensure all of data on socket comes through. f==read || f==vwrite 33 * Ensure all of data on socket comes through. f==read || f==vwrite
34 */ 34 */
35size_t
36atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
37 int (*cb)(void *, size_t), void *);
35size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); 38size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
36 39
37#define vwrite (ssize_t (*)(int, void *, size_t))write 40#define vwrite (ssize_t (*)(int, void *, size_t))write
@@ -39,6 +42,9 @@ size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
39/* 42/*
40 * ensure all of data on socket comes through. f==readv || f==writev 43 * ensure all of data on socket comes through. f==readv || f==writev
41 */ 44 */
45size_t
46atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
47 const struct iovec *_iov, int iovcnt, int (*cb)(void *, size_t), void *);
42size_t atomiciov(ssize_t (*)(int, const struct iovec *, int), 48size_t atomiciov(ssize_t (*)(int, const struct iovec *, int),
43 int, const struct iovec *, int); 49 int, const struct iovec *, int);
44 50
diff --git a/misc.c b/misc.c
index a82e7936e..41c92a82b 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.c,v 1.80 2010/07/21 02:10:58 djm Exp $ */ 1/* $OpenBSD: misc.c,v 1.81 2010/09/22 22:58:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@@ -860,6 +860,70 @@ timingsafe_bcmp(const void *b1, const void *b2, size_t n)
860 ret |= *p1++ ^ *p2++; 860 ret |= *p1++ ^ *p2++;
861 return (ret != 0); 861 return (ret != 0);
862} 862}
863
864void
865bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
866{
867 bw->buflen = buflen;
868 bw->rate = kbps;
869 bw->thresh = bw->rate;
870 bw->lamt = 0;
871 timerclear(&bw->bwstart);
872 timerclear(&bw->bwend);
873}
874
875/* Callback from read/write loop to insert bandwidth-limiting delays */
876void
877bandwidth_limit(struct bwlimit *bw, size_t read_len)
878{
879 u_int64_t waitlen;
880 struct timespec ts, rm;
881
882 if (!timerisset(&bw->bwstart)) {
883 gettimeofday(&bw->bwstart, NULL);
884 return;
885 }
886
887 bw->lamt += read_len;
888 if (bw->lamt < bw->thresh)
889 return;
890
891 gettimeofday(&bw->bwend, NULL);
892 timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
893 if (!timerisset(&bw->bwend))
894 return;
895
896 bw->lamt *= 8;
897 waitlen = (double)1000000L * bw->lamt / bw->rate;
898
899 bw->bwstart.tv_sec = waitlen / 1000000L;
900 bw->bwstart.tv_usec = waitlen % 1000000L;
901
902 if (timercmp(&bw->bwstart, &bw->bwend, >)) {
903 timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
904
905 /* Adjust the wait time */
906 if (bw->bwend.tv_sec) {
907 bw->thresh /= 2;
908 if (bw->thresh < bw->buflen / 4)
909 bw->thresh = bw->buflen / 4;
910 } else if (bw->bwend.tv_usec < 10000) {
911 bw->thresh *= 2;
912 if (bw->thresh > bw->buflen * 8)
913 bw->thresh = bw->buflen * 8;
914 }
915
916 TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
917 while (nanosleep(&ts, &rm) == -1) {
918 if (errno != EINTR)
919 break;
920 ts = rm;
921 }
922 }
923
924 bw->lamt = 0;
925 gettimeofday(&bw->bwstart, NULL);
926}
863void 927void
864sock_set_v6only(int s) 928sock_set_v6only(int s)
865{ 929{
diff --git a/misc.h b/misc.h
index bb799f616..f5aab029b 100644
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: misc.h,v 1.43 2010/07/13 23:13:16 djm Exp $ */ 1/* $OpenBSD: misc.h,v 1.44 2010/09/22 22:58:51 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -80,6 +80,15 @@ void put_u32(void *, u_int32_t)
80void put_u16(void *, u_int16_t) 80void put_u16(void *, u_int16_t)
81 __attribute__((__bounded__( __minbytes__, 1, 2))); 81 __attribute__((__bounded__( __minbytes__, 1, 2)));
82 82
83struct bwlimit {
84 size_t buflen;
85 u_int64_t rate, thresh, lamt;
86 struct timeval bwstart, bwend;
87};
88
89void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t);
90void bandwidth_limit(struct bwlimit *, size_t);
91
83 92
84/* readpass.c */ 93/* readpass.c */
85 94
diff --git a/scp.c b/scp.c
index e07de42f7..a4066c668 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */ 1/* $OpenBSD: scp.c,v 1.167 2010/09/22 22:58:51 djm Exp $ */
2/* 2/*
3 * scp - secure remote copy. This is basically patched BSD rcp which 3 * scp - secure remote copy. This is basically patched BSD rcp which
4 * uses ssh to do the data transfer (instead of using rcmd). 4 * uses ssh to do the data transfer (instead of using rcmd).
@@ -120,13 +120,12 @@ extern char *__progname;
120 120
121int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 121int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
122 122
123void bwlimit(int);
124
125/* Struct for addargs */ 123/* Struct for addargs */
126arglist args; 124arglist args;
127 125
128/* Bandwidth limit */ 126/* Bandwidth limit */
129off_t limit_rate = 0; 127long long limit_kbps = 0;
128struct bwlimit bwlimit;
130 129
131/* Name of current file being transferred. */ 130/* Name of current file being transferred. */
132char *curfile; 131char *curfile;
@@ -312,15 +311,14 @@ void sink(int, char *[]);
312void source(int, char *[]); 311void source(int, char *[]);
313void tolocal(int, char *[]); 312void tolocal(int, char *[]);
314void toremote(char *, int, char *[]); 313void toremote(char *, int, char *[]);
315size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
316void usage(void); 314void usage(void);
317 315
318int 316int
319main(int argc, char **argv) 317main(int argc, char **argv)
320{ 318{
321 int ch, fflag, tflag, status, n; 319 int ch, fflag, tflag, status, n;
322 double speed; 320 char *targ, **newargv;
323 char *targ, *endp, **newargv; 321 const char *errstr;
324 extern char *optarg; 322 extern char *optarg;
325 extern int optind; 323 extern int optind;
326 324
@@ -369,10 +367,12 @@ main(int argc, char **argv)
369 addargs(&args, "-oBatchmode yes"); 367 addargs(&args, "-oBatchmode yes");
370 break; 368 break;
371 case 'l': 369 case 'l':
372 speed = strtod(optarg, &endp); 370 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
373 if (speed <= 0 || *endp != '\0') 371 &errstr);
372 if (errstr != NULL)
374 usage(); 373 usage();
375 limit_rate = speed * 1024; 374 limit_kbps *= 1024; /* kbps */
375 bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN);
376 break; 376 break;
377 case 'p': 377 case 'p':
378 pflag = 1; 378 pflag = 1;
@@ -474,41 +474,16 @@ main(int argc, char **argv)
474 exit(errs != 0); 474 exit(errs != 0);
475} 475}
476 476
477/* 477/* Callback from atomicio6 to update progress meter and limit bandwidth */
478 * atomicio-like wrapper that also applies bandwidth limits and updates 478static int
479 * the progressmeter counter. 479scpio(void *_cnt, size_t s)
480 */
481size_t
482scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c)
483{ 480{
484 u_char *p = (u_char *)_p; 481 off_t *cnt = (off_t *)_cnt;
485 size_t offset; 482
486 ssize_t r; 483 *cnt += s;
487 struct pollfd pfd; 484 if (limit_kbps > 0)
488 485 bandwidth_limit(&bwlimit, s);
489 pfd.fd = fd; 486 return 0;
490 pfd.events = f == read ? POLLIN : POLLOUT;
491 for (offset = 0; offset < l;) {
492 r = f(fd, p + offset, l - offset);
493 if (r == 0) {
494 errno = EPIPE;
495 return offset;
496 }
497 if (r < 0) {
498 if (errno == EINTR)
499 continue;
500 if (errno == EAGAIN || errno == EWOULDBLOCK) {
501 (void)poll(&pfd, 1, -1); /* Ignore errors */
502 continue;
503 }
504 return offset;
505 }
506 offset += (size_t)r;
507 *c += (off_t)r;
508 if (limit_rate)
509 bwlimit(r);
510 }
511 return offset;
512} 487}
513 488
514void 489void
@@ -750,7 +725,7 @@ next: if (fd != -1) {
750 (void)atomicio(vwrite, remout, bp->buf, amt); 725 (void)atomicio(vwrite, remout, bp->buf, amt);
751 continue; 726 continue;
752 } 727 }
753 if (scpio(vwrite, remout, bp->buf, amt, 728 if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
754 &statbytes) != amt) 729 &statbytes) != amt)
755 haderr = errno; 730 haderr = errno;
756 } 731 }
@@ -825,60 +800,6 @@ rsource(char *name, struct stat *statp)
825} 800}
826 801
827void 802void
828bwlimit(int amount)
829{
830 static struct timeval bwstart, bwend;
831 static int lamt, thresh = 16384;
832 u_int64_t waitlen;
833 struct timespec ts, rm;
834
835 if (!timerisset(&bwstart)) {
836 gettimeofday(&bwstart, NULL);
837 return;
838 }
839
840 lamt += amount;
841 if (lamt < thresh)
842 return;
843
844 gettimeofday(&bwend, NULL);
845 timersub(&bwend, &bwstart, &bwend);
846 if (!timerisset(&bwend))
847 return;
848
849 lamt *= 8;
850 waitlen = (double)1000000L * lamt / limit_rate;
851
852 bwstart.tv_sec = waitlen / 1000000L;
853 bwstart.tv_usec = waitlen % 1000000L;
854
855 if (timercmp(&bwstart, &bwend, >)) {
856 timersub(&bwstart, &bwend, &bwend);
857
858 /* Adjust the wait time */
859 if (bwend.tv_sec) {
860 thresh /= 2;
861 if (thresh < 2048)
862 thresh = 2048;
863 } else if (bwend.tv_usec < 10000) {
864 thresh *= 2;
865 if (thresh > COPY_BUFLEN * 4)
866 thresh = COPY_BUFLEN * 4;
867 }
868
869 TIMEVAL_TO_TIMESPEC(&bwend, &ts);
870 while (nanosleep(&ts, &rm) == -1) {
871 if (errno != EINTR)
872 break;
873 ts = rm;
874 }
875 }
876
877 lamt = 0;
878 gettimeofday(&bwstart, NULL);
879}
880
881void
882sink(int argc, char **argv) 803sink(int argc, char **argv)
883{ 804{
884 static BUF buffer; 805 static BUF buffer;
@@ -1071,7 +992,8 @@ bad: run_err("%s: %s", np, strerror(errno));
1071 amt = size - i; 992 amt = size - i;
1072 count += amt; 993 count += amt;
1073 do { 994 do {
1074 j = scpio(read, remin, cp, amt, &statbytes); 995 j = atomicio6(read, remin, cp, amt,
996 scpio, &statbytes);
1075 if (j == 0) { 997 if (j == 0) {
1076 run_err("%s", j != EPIPE ? 998 run_err("%s", j != EPIPE ?
1077 strerror(errno) : 999 strerror(errno) :
diff --git a/sftp-client.c b/sftp-client.c
index 9dab47780..4e009ef25 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.92 2010/07/19 03:16:33 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.93 2010/09/22 22:58:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -76,14 +76,26 @@ struct sftp_conn {
76#define SFTP_EXT_STATVFS 0x00000002 76#define SFTP_EXT_STATVFS 0x00000002
77#define SFTP_EXT_FSTATVFS 0x00000004 77#define SFTP_EXT_FSTATVFS 0x00000004
78 u_int exts; 78 u_int exts;
79 u_int64_t limit_kbps;
80 struct bwlimit bwlimit_in, bwlimit_out;
79}; 81};
80 82
81static char * 83static char *
82get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) 84get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
83 __attribute__((format(printf, 4, 5))); 85 const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
86
87/* ARGSUSED */
88static int
89sftpio(void *_bwlimit, size_t amount)
90{
91 struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
92
93 bandwidth_limit(bwlimit, amount);
94 return 0;
95}
84 96
85static void 97static void
86send_msg(int fd, Buffer *m) 98send_msg(struct sftp_conn *conn, Buffer *m)
87{ 99{
88 u_char mlen[4]; 100 u_char mlen[4];
89 struct iovec iov[2]; 101 struct iovec iov[2];
@@ -98,19 +110,22 @@ send_msg(int fd, Buffer *m)
98 iov[1].iov_base = buffer_ptr(m); 110 iov[1].iov_base = buffer_ptr(m);
99 iov[1].iov_len = buffer_len(m); 111 iov[1].iov_len = buffer_len(m);
100 112
101 if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) 113 if (atomiciov6(writev, conn->fd_out, iov, 2,
114 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
115 buffer_len(m) + sizeof(mlen))
102 fatal("Couldn't send packet: %s", strerror(errno)); 116 fatal("Couldn't send packet: %s", strerror(errno));
103 117
104 buffer_clear(m); 118 buffer_clear(m);
105} 119}
106 120
107static void 121static void
108get_msg(int fd, Buffer *m) 122get_msg(struct sftp_conn *conn, Buffer *m)
109{ 123{
110 u_int msg_len; 124 u_int msg_len;
111 125
112 buffer_append_space(m, 4); 126 buffer_append_space(m, 4);
113 if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { 127 if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
128 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
114 if (errno == EPIPE) 129 if (errno == EPIPE)
115 fatal("Connection closed"); 130 fatal("Connection closed");
116 else 131 else
@@ -122,7 +137,9 @@ get_msg(int fd, Buffer *m)
122 fatal("Received message too long %u", msg_len); 137 fatal("Received message too long %u", msg_len);
123 138
124 buffer_append_space(m, msg_len); 139 buffer_append_space(m, msg_len);
125 if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { 140 if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
141 conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
142 != msg_len) {
126 if (errno == EPIPE) 143 if (errno == EPIPE)
127 fatal("Connection closed"); 144 fatal("Connection closed");
128 else 145 else
@@ -131,7 +148,7 @@ get_msg(int fd, Buffer *m)
131} 148}
132 149
133static void 150static void
134send_string_request(int fd, u_int id, u_int code, char *s, 151send_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
135 u_int len) 152 u_int len)
136{ 153{
137 Buffer msg; 154 Buffer msg;
@@ -140,14 +157,14 @@ send_string_request(int fd, u_int id, u_int code, char *s,
140 buffer_put_char(&msg, code); 157 buffer_put_char(&msg, code);
141 buffer_put_int(&msg, id); 158 buffer_put_int(&msg, id);
142 buffer_put_string(&msg, s, len); 159 buffer_put_string(&msg, s, len);
143 send_msg(fd, &msg); 160 send_msg(conn, &msg);
144 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 161 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
145 buffer_free(&msg); 162 buffer_free(&msg);
146} 163}
147 164
148static void 165static void
149send_string_attrs_request(int fd, u_int id, u_int code, char *s, 166send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
150 u_int len, Attrib *a) 167 char *s, u_int len, Attrib *a)
151{ 168{
152 Buffer msg; 169 Buffer msg;
153 170
@@ -156,19 +173,19 @@ send_string_attrs_request(int fd, u_int id, u_int code, char *s,
156 buffer_put_int(&msg, id); 173 buffer_put_int(&msg, id);
157 buffer_put_string(&msg, s, len); 174 buffer_put_string(&msg, s, len);
158 encode_attrib(&msg, a); 175 encode_attrib(&msg, a);
159 send_msg(fd, &msg); 176 send_msg(conn, &msg);
160 debug3("Sent message fd %d T:%u I:%u", fd, code, id); 177 debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
161 buffer_free(&msg); 178 buffer_free(&msg);
162} 179}
163 180
164static u_int 181static u_int
165get_status(int fd, u_int expected_id) 182get_status(struct sftp_conn *conn, u_int expected_id)
166{ 183{
167 Buffer msg; 184 Buffer msg;
168 u_int type, id, status; 185 u_int type, id, status;
169 186
170 buffer_init(&msg); 187 buffer_init(&msg);
171 get_msg(fd, &msg); 188 get_msg(conn, &msg);
172 type = buffer_get_char(&msg); 189 type = buffer_get_char(&msg);
173 id = buffer_get_int(&msg); 190 id = buffer_get_int(&msg);
174 191
@@ -183,11 +200,12 @@ get_status(int fd, u_int expected_id)
183 200
184 debug3("SSH2_FXP_STATUS %u", status); 201 debug3("SSH2_FXP_STATUS %u", status);
185 202
186 return(status); 203 return status;
187} 204}
188 205
189static char * 206static char *
190get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) 207get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
208 const char *errfmt, ...)
191{ 209{
192 Buffer msg; 210 Buffer msg;
193 u_int type, id; 211 u_int type, id;
@@ -201,7 +219,7 @@ get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
201 va_end(args); 219 va_end(args);
202 220
203 buffer_init(&msg); 221 buffer_init(&msg);
204 get_msg(fd, &msg); 222 get_msg(conn, &msg);
205 type = buffer_get_char(&msg); 223 type = buffer_get_char(&msg);
206 id = buffer_get_int(&msg); 224 id = buffer_get_int(&msg);
207 225
@@ -225,14 +243,14 @@ get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
225} 243}
226 244
227static Attrib * 245static Attrib *
228get_decode_stat(int fd, u_int expected_id, int quiet) 246get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
229{ 247{
230 Buffer msg; 248 Buffer msg;
231 u_int type, id; 249 u_int type, id;
232 Attrib *a; 250 Attrib *a;
233 251
234 buffer_init(&msg); 252 buffer_init(&msg);
235 get_msg(fd, &msg); 253 get_msg(conn, &msg);
236 254
237 type = buffer_get_char(&msg); 255 type = buffer_get_char(&msg);
238 id = buffer_get_int(&msg); 256 id = buffer_get_int(&msg);
@@ -260,14 +278,14 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
260} 278}
261 279
262static int 280static int
263get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id, 281get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
264 int quiet) 282 u_int expected_id, int quiet)
265{ 283{
266 Buffer msg; 284 Buffer msg;
267 u_int type, id, flag; 285 u_int type, id, flag;
268 286
269 buffer_init(&msg); 287 buffer_init(&msg);
270 get_msg(fd, &msg); 288 get_msg(conn, &msg);
271 289
272 type = buffer_get_char(&msg); 290 type = buffer_get_char(&msg);
273 id = buffer_get_int(&msg); 291 id = buffer_get_int(&msg);
@@ -311,21 +329,29 @@ get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
311} 329}
312 330
313struct sftp_conn * 331struct sftp_conn *
314do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 332do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
333 u_int64_t limit_kbps)
315{ 334{
316 u_int type, exts = 0; 335 u_int type;
317 int version;
318 Buffer msg; 336 Buffer msg;
319 struct sftp_conn *ret; 337 struct sftp_conn *ret;
320 338
339 ret = xmalloc(sizeof(*ret));
340 ret->fd_in = fd_in;
341 ret->fd_out = fd_out;
342 ret->transfer_buflen = transfer_buflen;
343 ret->num_requests = num_requests;
344 ret->exts = 0;
345 ret->limit_kbps = 0;
346
321 buffer_init(&msg); 347 buffer_init(&msg);
322 buffer_put_char(&msg, SSH2_FXP_INIT); 348 buffer_put_char(&msg, SSH2_FXP_INIT);
323 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 349 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
324 send_msg(fd_out, &msg); 350 send_msg(ret, &msg);
325 351
326 buffer_clear(&msg); 352 buffer_clear(&msg);
327 353
328 get_msg(fd_in, &msg); 354 get_msg(ret, &msg);
329 355
330 /* Expecting a VERSION reply */ 356 /* Expecting a VERSION reply */
331 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 357 if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
@@ -334,9 +360,9 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
334 buffer_free(&msg); 360 buffer_free(&msg);
335 return(NULL); 361 return(NULL);
336 } 362 }
337 version = buffer_get_int(&msg); 363 ret->version = buffer_get_int(&msg);
338 364
339 debug2("Remote version: %d", version); 365 debug2("Remote version: %u", ret->version);
340 366
341 /* Check for extensions */ 367 /* Check for extensions */
342 while (buffer_len(&msg) > 0) { 368 while (buffer_len(&msg) > 0) {
@@ -346,15 +372,15 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
346 372
347 if (strcmp(name, "posix-rename@openssh.com") == 0 && 373 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
348 strcmp(value, "1") == 0) { 374 strcmp(value, "1") == 0) {
349 exts |= SFTP_EXT_POSIX_RENAME; 375 ret->exts |= SFTP_EXT_POSIX_RENAME;
350 known = 1; 376 known = 1;
351 } else if (strcmp(name, "statvfs@openssh.com") == 0 && 377 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
352 strcmp(value, "2") == 0) { 378 strcmp(value, "2") == 0) {
353 exts |= SFTP_EXT_STATVFS; 379 ret->exts |= SFTP_EXT_STATVFS;
354 known = 1; 380 known = 1;
355 } if (strcmp(name, "fstatvfs@openssh.com") == 0 && 381 } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
356 strcmp(value, "2") == 0) { 382 strcmp(value, "2") == 0) {
357 exts |= SFTP_EXT_FSTATVFS; 383 ret->exts |= SFTP_EXT_FSTATVFS;
358 known = 1; 384 known = 1;
359 } 385 }
360 if (known) { 386 if (known) {
@@ -369,26 +395,25 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
369 395
370 buffer_free(&msg); 396 buffer_free(&msg);
371 397
372 ret = xmalloc(sizeof(*ret));
373 ret->fd_in = fd_in;
374 ret->fd_out = fd_out;
375 ret->transfer_buflen = transfer_buflen;
376 ret->num_requests = num_requests;
377 ret->version = version;
378 ret->msg_id = 1;
379 ret->exts = exts;
380
381 /* Some filexfer v.0 servers don't support large packets */ 398 /* Some filexfer v.0 servers don't support large packets */
382 if (version == 0) 399 if (ret->version == 0)
383 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); 400 ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
384 401
385 return(ret); 402 ret->limit_kbps = limit_kbps;
403 if (ret->limit_kbps > 0) {
404 bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
405 ret->transfer_buflen);
406 bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
407 ret->transfer_buflen);
408 }
409
410 return ret;
386} 411}
387 412
388u_int 413u_int
389sftp_proto_version(struct sftp_conn *conn) 414sftp_proto_version(struct sftp_conn *conn)
390{ 415{
391 return(conn->version); 416 return conn->version;
392} 417}
393 418
394int 419int
@@ -403,16 +428,16 @@ do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
403 buffer_put_char(&msg, SSH2_FXP_CLOSE); 428 buffer_put_char(&msg, SSH2_FXP_CLOSE);
404 buffer_put_int(&msg, id); 429 buffer_put_int(&msg, id);
405 buffer_put_string(&msg, handle, handle_len); 430 buffer_put_string(&msg, handle, handle_len);
406 send_msg(conn->fd_out, &msg); 431 send_msg(conn, &msg);
407 debug3("Sent message SSH2_FXP_CLOSE I:%u", id); 432 debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
408 433
409 status = get_status(conn->fd_in, id); 434 status = get_status(conn, id);
410 if (status != SSH2_FX_OK) 435 if (status != SSH2_FX_OK)
411 error("Couldn't close file: %s", fx2txt(status)); 436 error("Couldn't close file: %s", fx2txt(status));
412 437
413 buffer_free(&msg); 438 buffer_free(&msg);
414 439
415 return(status); 440 return status;
416} 441}
417 442
418 443
@@ -430,14 +455,14 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
430 buffer_put_char(&msg, SSH2_FXP_OPENDIR); 455 buffer_put_char(&msg, SSH2_FXP_OPENDIR);
431 buffer_put_int(&msg, id); 456 buffer_put_int(&msg, id);
432 buffer_put_cstring(&msg, path); 457 buffer_put_cstring(&msg, path);
433 send_msg(conn->fd_out, &msg); 458 send_msg(conn, &msg);
434 459
435 buffer_clear(&msg); 460 buffer_clear(&msg);
436 461
437 handle = get_handle(conn->fd_in, id, &handle_len, 462 handle = get_handle(conn, id, &handle_len,
438 "remote readdir(\"%s\")", path); 463 "remote readdir(\"%s\")", path);
439 if (handle == NULL) 464 if (handle == NULL)
440 return(-1); 465 return -1;
441 466
442 if (dir) { 467 if (dir) {
443 ents = 0; 468 ents = 0;
@@ -454,11 +479,11 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
454 buffer_put_char(&msg, SSH2_FXP_READDIR); 479 buffer_put_char(&msg, SSH2_FXP_READDIR);
455 buffer_put_int(&msg, id); 480 buffer_put_int(&msg, id);
456 buffer_put_string(&msg, handle, handle_len); 481 buffer_put_string(&msg, handle, handle_len);
457 send_msg(conn->fd_out, &msg); 482 send_msg(conn, &msg);
458 483
459 buffer_clear(&msg); 484 buffer_clear(&msg);
460 485
461 get_msg(conn->fd_in, &msg); 486 get_msg(conn, &msg);
462 487
463 type = buffer_get_char(&msg); 488 type = buffer_get_char(&msg);
464 id = buffer_get_int(&msg); 489 id = buffer_get_int(&msg);
@@ -537,7 +562,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
537 **dir = NULL; 562 **dir = NULL;
538 } 563 }
539 564
540 return(0); 565 return 0;
541} 566}
542 567
543int 568int
@@ -566,9 +591,8 @@ do_rm(struct sftp_conn *conn, char *path)
566 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); 591 debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
567 592
568 id = conn->msg_id++; 593 id = conn->msg_id++;
569 send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, 594 send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
570 strlen(path)); 595 status = get_status(conn, id);
571 status = get_status(conn->fd_in, id);
572 if (status != SSH2_FX_OK) 596 if (status != SSH2_FX_OK)
573 error("Couldn't delete file: %s", fx2txt(status)); 597 error("Couldn't delete file: %s", fx2txt(status));
574 return(status); 598 return(status);
@@ -580,10 +604,10 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
580 u_int status, id; 604 u_int status, id;
581 605
582 id = conn->msg_id++; 606 id = conn->msg_id++;
583 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, 607 send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
584 strlen(path), a); 608 strlen(path), a);
585 609
586 status = get_status(conn->fd_in, id); 610 status = get_status(conn, id);
587 if (status != SSH2_FX_OK && printflag) 611 if (status != SSH2_FX_OK && printflag)
588 error("Couldn't create directory: %s", fx2txt(status)); 612 error("Couldn't create directory: %s", fx2txt(status));
589 613
@@ -596,10 +620,10 @@ do_rmdir(struct sftp_conn *conn, char *path)
596 u_int status, id; 620 u_int status, id;
597 621
598 id = conn->msg_id++; 622 id = conn->msg_id++;
599 send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, 623 send_string_request(conn, id, SSH2_FXP_RMDIR, path,
600 strlen(path)); 624 strlen(path));
601 625
602 status = get_status(conn->fd_in, id); 626 status = get_status(conn, id);
603 if (status != SSH2_FX_OK) 627 if (status != SSH2_FX_OK)
604 error("Couldn't remove directory: %s", fx2txt(status)); 628 error("Couldn't remove directory: %s", fx2txt(status));
605 629
@@ -613,11 +637,11 @@ do_stat(struct sftp_conn *conn, char *path, int quiet)
613 637
614 id = conn->msg_id++; 638 id = conn->msg_id++;
615 639
616 send_string_request(conn->fd_out, id, 640 send_string_request(conn, id,
617 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, 641 conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
618 path, strlen(path)); 642 path, strlen(path));
619 643
620 return(get_decode_stat(conn->fd_in, id, quiet)); 644 return(get_decode_stat(conn, id, quiet));
621} 645}
622 646
623Attrib * 647Attrib *
@@ -634,10 +658,10 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet)
634 } 658 }
635 659
636 id = conn->msg_id++; 660 id = conn->msg_id++;
637 send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, 661 send_string_request(conn, id, SSH2_FXP_LSTAT, path,
638 strlen(path)); 662 strlen(path));
639 663
640 return(get_decode_stat(conn->fd_in, id, quiet)); 664 return(get_decode_stat(conn, id, quiet));
641} 665}
642 666
643#ifdef notyet 667#ifdef notyet
@@ -647,10 +671,10 @@ do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
647 u_int id; 671 u_int id;
648 672
649 id = conn->msg_id++; 673 id = conn->msg_id++;
650 send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, 674 send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
651 handle_len); 675 handle_len);
652 676
653 return(get_decode_stat(conn->fd_in, id, quiet)); 677 return(get_decode_stat(conn, id, quiet));
654} 678}
655#endif 679#endif
656 680
@@ -660,10 +684,10 @@ do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
660 u_int status, id; 684 u_int status, id;
661 685
662 id = conn->msg_id++; 686 id = conn->msg_id++;
663 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, 687 send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
664 strlen(path), a); 688 strlen(path), a);
665 689
666 status = get_status(conn->fd_in, id); 690 status = get_status(conn, id);
667 if (status != SSH2_FX_OK) 691 if (status != SSH2_FX_OK)
668 error("Couldn't setstat on \"%s\": %s", path, 692 error("Couldn't setstat on \"%s\": %s", path,
669 fx2txt(status)); 693 fx2txt(status));
@@ -678,10 +702,10 @@ do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
678 u_int status, id; 702 u_int status, id;
679 703
680 id = conn->msg_id++; 704 id = conn->msg_id++;
681 send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, 705 send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
682 handle_len, a); 706 handle_len, a);
683 707
684 status = get_status(conn->fd_in, id); 708 status = get_status(conn, id);
685 if (status != SSH2_FX_OK) 709 if (status != SSH2_FX_OK)
686 error("Couldn't fsetstat: %s", fx2txt(status)); 710 error("Couldn't fsetstat: %s", fx2txt(status));
687 711
@@ -697,12 +721,12 @@ do_realpath(struct sftp_conn *conn, char *path)
697 Attrib *a; 721 Attrib *a;
698 722
699 expected_id = id = conn->msg_id++; 723 expected_id = id = conn->msg_id++;
700 send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path, 724 send_string_request(conn, id, SSH2_FXP_REALPATH, path,
701 strlen(path)); 725 strlen(path));
702 726
703 buffer_init(&msg); 727 buffer_init(&msg);
704 728
705 get_msg(conn->fd_in, &msg); 729 get_msg(conn, &msg);
706 type = buffer_get_char(&msg); 730 type = buffer_get_char(&msg);
707 id = buffer_get_int(&msg); 731 id = buffer_get_int(&msg);
708 732
@@ -756,13 +780,13 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
756 } 780 }
757 buffer_put_cstring(&msg, oldpath); 781 buffer_put_cstring(&msg, oldpath);
758 buffer_put_cstring(&msg, newpath); 782 buffer_put_cstring(&msg, newpath);
759 send_msg(conn->fd_out, &msg); 783 send_msg(conn, &msg);
760 debug3("Sent message %s \"%s\" -> \"%s\"", 784 debug3("Sent message %s \"%s\" -> \"%s\"",
761 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : 785 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
762 "SSH2_FXP_RENAME", oldpath, newpath); 786 "SSH2_FXP_RENAME", oldpath, newpath);
763 buffer_free(&msg); 787 buffer_free(&msg);
764 788
765 status = get_status(conn->fd_in, id); 789 status = get_status(conn, id);
766 if (status != SSH2_FX_OK) 790 if (status != SSH2_FX_OK)
767 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, 791 error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
768 newpath, fx2txt(status)); 792 newpath, fx2txt(status));
@@ -789,12 +813,12 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
789 buffer_put_int(&msg, id); 813 buffer_put_int(&msg, id);
790 buffer_put_cstring(&msg, oldpath); 814 buffer_put_cstring(&msg, oldpath);
791 buffer_put_cstring(&msg, newpath); 815 buffer_put_cstring(&msg, newpath);
792 send_msg(conn->fd_out, &msg); 816 send_msg(conn, &msg);
793 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, 817 debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
794 newpath); 818 newpath);
795 buffer_free(&msg); 819 buffer_free(&msg);
796 820
797 status = get_status(conn->fd_in, id); 821 status = get_status(conn, id);
798 if (status != SSH2_FX_OK) 822 if (status != SSH2_FX_OK)
799 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, 823 error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
800 newpath, fx2txt(status)); 824 newpath, fx2txt(status));
@@ -812,12 +836,11 @@ do_readlink(struct sftp_conn *conn, char *path)
812 Attrib *a; 836 Attrib *a;
813 837
814 expected_id = id = conn->msg_id++; 838 expected_id = id = conn->msg_id++;
815 send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path, 839 send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
816 strlen(path));
817 840
818 buffer_init(&msg); 841 buffer_init(&msg);
819 842
820 get_msg(conn->fd_in, &msg); 843 get_msg(conn, &msg);
821 type = buffer_get_char(&msg); 844 type = buffer_get_char(&msg);
822 id = buffer_get_int(&msg); 845 id = buffer_get_int(&msg);
823 846
@@ -871,10 +894,10 @@ do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
871 buffer_put_int(&msg, id); 894 buffer_put_int(&msg, id);
872 buffer_put_cstring(&msg, "statvfs@openssh.com"); 895 buffer_put_cstring(&msg, "statvfs@openssh.com");
873 buffer_put_cstring(&msg, path); 896 buffer_put_cstring(&msg, path);
874 send_msg(conn->fd_out, &msg); 897 send_msg(conn, &msg);
875 buffer_free(&msg); 898 buffer_free(&msg);
876 899
877 return get_decode_statvfs(conn->fd_in, st, id, quiet); 900 return get_decode_statvfs(conn, st, id, quiet);
878} 901}
879 902
880#ifdef notyet 903#ifdef notyet
@@ -898,16 +921,16 @@ do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
898 buffer_put_int(&msg, id); 921 buffer_put_int(&msg, id);
899 buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 922 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
900 buffer_put_string(&msg, handle, handle_len); 923 buffer_put_string(&msg, handle, handle_len);
901 send_msg(conn->fd_out, &msg); 924 send_msg(conn, &msg);
902 buffer_free(&msg); 925 buffer_free(&msg);
903 926
904 return get_decode_statvfs(conn->fd_in, st, id, quiet); 927 return get_decode_statvfs(conn, st, id, quiet);
905} 928}
906#endif 929#endif
907 930
908static void 931static void
909send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, 932send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
910 char *handle, u_int handle_len) 933 u_int len, char *handle, u_int handle_len)
911{ 934{
912 Buffer msg; 935 Buffer msg;
913 936
@@ -918,7 +941,7 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
918 buffer_put_string(&msg, handle, handle_len); 941 buffer_put_string(&msg, handle, handle_len);
919 buffer_put_int64(&msg, offset); 942 buffer_put_int64(&msg, offset);
920 buffer_put_int(&msg, len); 943 buffer_put_int(&msg, len);
921 send_msg(fd_out, &msg); 944 send_msg(conn, &msg);
922 buffer_free(&msg); 945 buffer_free(&msg);
923} 946}
924 947
@@ -976,10 +999,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
976 buffer_put_int(&msg, SSH2_FXF_READ); 999 buffer_put_int(&msg, SSH2_FXF_READ);
977 attrib_clear(&junk); /* Send empty attributes */ 1000 attrib_clear(&junk); /* Send empty attributes */
978 encode_attrib(&msg, &junk); 1001 encode_attrib(&msg, &junk);
979 send_msg(conn->fd_out, &msg); 1002 send_msg(conn, &msg);
980 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 1003 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
981 1004
982 handle = get_handle(conn->fd_in, id, &handle_len, 1005 handle = get_handle(conn, id, &handle_len,
983 "remote open(\"%s\")", remote_path); 1006 "remote open(\"%s\")", remote_path);
984 if (handle == NULL) { 1007 if (handle == NULL) {
985 buffer_free(&msg); 1008 buffer_free(&msg);
@@ -1032,12 +1055,12 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1032 offset += buflen; 1055 offset += buflen;
1033 num_req++; 1056 num_req++;
1034 TAILQ_INSERT_TAIL(&requests, req, tq); 1057 TAILQ_INSERT_TAIL(&requests, req, tq);
1035 send_read_request(conn->fd_out, req->id, req->offset, 1058 send_read_request(conn, req->id, req->offset,
1036 req->len, handle, handle_len); 1059 req->len, handle, handle_len);
1037 } 1060 }
1038 1061
1039 buffer_clear(&msg); 1062 buffer_clear(&msg);
1040 get_msg(conn->fd_in, &msg); 1063 get_msg(conn, &msg);
1041 type = buffer_get_char(&msg); 1064 type = buffer_get_char(&msg);
1042 id = buffer_get_int(&msg); 1065 id = buffer_get_int(&msg);
1043 debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 1066 debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
@@ -1092,7 +1115,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1092 req->id = conn->msg_id++; 1115 req->id = conn->msg_id++;
1093 req->len -= len; 1116 req->len -= len;
1094 req->offset += len; 1117 req->offset += len;
1095 send_read_request(conn->fd_out, req->id, 1118 send_read_request(conn, req->id,
1096 req->offset, req->len, handle, handle_len); 1119 req->offset, req->len, handle, handle_len);
1097 /* Reduce the request size */ 1120 /* Reduce the request size */
1098 if (len < buflen) 1121 if (len < buflen)
@@ -1327,12 +1350,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1327 buffer_put_cstring(&msg, remote_path); 1350 buffer_put_cstring(&msg, remote_path);
1328 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); 1351 buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
1329 encode_attrib(&msg, &a); 1352 encode_attrib(&msg, &a);
1330 send_msg(conn->fd_out, &msg); 1353 send_msg(conn, &msg);
1331 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 1354 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
1332 1355
1333 buffer_clear(&msg); 1356 buffer_clear(&msg);
1334 1357
1335 handle = get_handle(conn->fd_in, id, &handle_len, 1358 handle = get_handle(conn, id, &handle_len,
1336 "remote open(\"%s\")", remote_path); 1359 "remote open(\"%s\")", remote_path);
1337 if (handle == NULL) { 1360 if (handle == NULL) {
1338 close(local_fd); 1361 close(local_fd);
@@ -1381,7 +1404,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1381 buffer_put_string(&msg, handle, handle_len); 1404 buffer_put_string(&msg, handle, handle_len);
1382 buffer_put_int64(&msg, offset); 1405 buffer_put_int64(&msg, offset);
1383 buffer_put_string(&msg, data, len); 1406 buffer_put_string(&msg, data, len);
1384 send_msg(conn->fd_out, &msg); 1407 send_msg(conn, &msg);
1385 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", 1408 debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
1386 id, (unsigned long long)offset, len); 1409 id, (unsigned long long)offset, len);
1387 } else if (TAILQ_FIRST(&acks) == NULL) 1410 } else if (TAILQ_FIRST(&acks) == NULL)
@@ -1395,7 +1418,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1395 u_int r_id; 1418 u_int r_id;
1396 1419
1397 buffer_clear(&msg); 1420 buffer_clear(&msg);
1398 get_msg(conn->fd_in, &msg); 1421 get_msg(conn, &msg);
1399 type = buffer_get_char(&msg); 1422 type = buffer_get_char(&msg);
1400 r_id = buffer_get_int(&msg); 1423 r_id = buffer_get_int(&msg);
1401 1424
diff --git a/sftp-client.h b/sftp-client.h
index 1d08c4049..145fc38ee 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.19 2010/09/22 22:58:51 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -51,7 +51,7 @@ struct sftp_statvfs {
51 * Initialise a SSH filexfer connection. Returns NULL on error or 51 * Initialise a SSH filexfer connection. Returns NULL on error or
52 * a pointer to a initialized sftp_conn struct on success. 52 * a pointer to a initialized sftp_conn struct on success.
53 */ 53 */
54struct sftp_conn *do_init(int, int, u_int, u_int); 54struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t);
55 55
56u_int sftp_proto_version(struct sftp_conn *); 56u_int sftp_proto_version(struct sftp_conn *);
57 57
diff --git a/sftp.1 b/sftp.1
index 49d88de03..b2a19b72d 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.84 2010/09/19 21:30:05 jmc Exp $ 1.\" $OpenBSD: sftp.1,v 1.85 2010/09/22 22:58:51 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: September 19 2010 $ 25.Dd $Mdocdate: September 22 2010 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -38,6 +38,7 @@
38.Op Fl D Ar sftp_server_path 38.Op Fl D Ar sftp_server_path
39.Op Fl F Ar ssh_config 39.Op Fl F Ar ssh_config
40.Op Fl i Ar identity_file 40.Op Fl i Ar identity_file
41.Op Fl l Ar limit
41.Op Fl o Ar ssh_option 42.Op Fl o Ar ssh_option
42.Op Fl P Ar port 43.Op Fl P Ar port
43.Op Fl R Ar num_requests 44.Op Fl R Ar num_requests
@@ -159,6 +160,8 @@ Selects the file from which the identity (private key) for public key
159authentication is read. 160authentication is read.
160This option is directly passed to 161This option is directly passed to
161.Xr ssh 1 . 162.Xr ssh 1 .
163.It Fl l Ar limit
164Limits the used bandwidth, specified in Kbit/s.
162.It Fl o Ar ssh_option 165.It Fl o Ar ssh_option
163Can be used to pass options to 166Can be used to pass options to
164.Nm ssh 167.Nm ssh
diff --git a/sftp.c b/sftp.c
index 229f12987..8ce7d91fb 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.126 2010/09/22 22:58:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -2073,6 +2073,7 @@ main(int argc, char **argv)
2073 int debug_level = 0, sshver = 2; 2073 int debug_level = 0, sshver = 2;
2074 char *file1 = NULL, *sftp_server = NULL; 2074 char *file1 = NULL, *sftp_server = NULL;
2075 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 2075 char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
2076 const char *errstr;
2076 LogLevel ll = SYSLOG_LEVEL_INFO; 2077 LogLevel ll = SYSLOG_LEVEL_INFO;
2077 arglist args; 2078 arglist args;
2078 extern int optind; 2079 extern int optind;
@@ -2080,6 +2081,7 @@ main(int argc, char **argv)
2080 struct sftp_conn *conn; 2081 struct sftp_conn *conn;
2081 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; 2082 size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
2082 size_t num_requests = DEFAULT_NUM_REQUESTS; 2083 size_t num_requests = DEFAULT_NUM_REQUESTS;
2084 long long limit_kbps = 0;
2083 2085
2084 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 2086 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2085 sanitise_stdfd(); 2087 sanitise_stdfd();
@@ -2097,7 +2099,7 @@ main(int argc, char **argv)
2097 infile = stdin; 2099 infile = stdin;
2098 2100
2099 while ((ch = getopt(argc, argv, 2101 while ((ch = getopt(argc, argv,
2100 "1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { 2102 "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
2101 switch (ch) { 2103 switch (ch) {
2102 /* Passed through to ssh(1) */ 2104 /* Passed through to ssh(1) */
2103 case '4': 2105 case '4':
@@ -2158,6 +2160,13 @@ main(int argc, char **argv)
2158 case 'D': 2160 case 'D':
2159 sftp_direct = optarg; 2161 sftp_direct = optarg;
2160 break; 2162 break;
2163 case 'l':
2164 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
2165 &errstr);
2166 if (errstr != NULL)
2167 usage();
2168 limit_kbps *= 1024; /* kbps */
2169 break;
2161 case 'r': 2170 case 'r':
2162 global_rflag = 1; 2171 global_rflag = 1;
2163 break; 2172 break;
@@ -2235,7 +2244,7 @@ main(int argc, char **argv)
2235 } 2244 }
2236 freeargs(&args); 2245 freeargs(&args);
2237 2246
2238 conn = do_init(in, out, copy_buffer_len, num_requests); 2247 conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
2239 if (conn == NULL) 2248 if (conn == NULL)
2240 fatal("Couldn't initialise connection to server"); 2249 fatal("Couldn't initialise connection to server");
2241 2250