- 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@
This commit is contained in:
Damien Miller 2010-09-24 22:15:11 +10:00
parent 7fe2b1fec3
commit 65e42f87fe
10 changed files with 281 additions and 212 deletions

View File

@ -44,6 +44,16 @@
ssh_config.5: format the kexalgorithms in a more consistent ssh_config.5: format the kexalgorithms in a more consistent
(prettier!) way (prettier!) way
ok djm ok djm
- 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@
20100910 20100910
- (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact - (dtucker) [openbsd-compat/port-linux.c] Check is_selinux_enabled for exact

View File

@ -1,4 +1,4 @@
/* $OpenBSD: atomicio.c,v 1.25 2007/06/25 12:02:27 dtucker Exp $ */ /* $OpenBSD: atomicio.c,v 1.26 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2006 Damien Miller. All rights reserved. * Copyright (c) 2006 Damien Miller. All rights reserved.
* Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
@ -48,7 +48,8 @@
* ensure all of data on socket comes through. f==read || f==vwrite * ensure all of data on socket comes through. f==read || f==vwrite
*/ */
size_t size_t
atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n) atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
int (*cb)(void *, size_t), void *cb_arg)
{ {
char *s = _s; char *s = _s;
size_t pos = 0; size_t pos = 0;
@ -73,17 +74,28 @@ atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
return pos; return pos;
default: default:
pos += (size_t)res; pos += (size_t)res;
if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
errno = EINTR;
return pos;
}
} }
} }
return (pos); return pos;
}
size_t
atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
{
return atomicio6(f, fd, _s, n, NULL, NULL);
} }
/* /*
* ensure all of data on socket comes through. f==readv || f==writev * ensure all of data on socket comes through. f==readv || f==writev
*/ */
size_t size_t
atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd, atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt) const struct iovec *_iov, int iovcnt,
int (*cb)(void *, size_t), void *cb_arg)
{ {
size_t pos = 0, rem; size_t pos = 0, rem;
ssize_t res; ssize_t res;
@ -137,6 +149,17 @@ atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
iov[0].iov_base = ((char *)iov[0].iov_base) + rem; iov[0].iov_base = ((char *)iov[0].iov_base) + rem;
iov[0].iov_len -= rem; iov[0].iov_len -= rem;
} }
if (cb != NULL && cb(cb_arg, (size_t)res) == -1) {
errno = EINTR;
return pos;
}
} }
return pos; return pos;
} }
size_t
atomiciov(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt)
{
return atomiciov6(f, fd, _iov, iovcnt, NULL, NULL);
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: atomicio.h,v 1.10 2006/08/03 03:34:41 deraadt Exp $ */ /* $OpenBSD: atomicio.h,v 1.11 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2006 Damien Miller. All rights reserved. * Copyright (c) 2006 Damien Miller. All rights reserved.
@ -32,6 +32,9 @@
/* /*
* Ensure all of data on socket comes through. f==read || f==vwrite * Ensure all of data on socket comes through. f==read || f==vwrite
*/ */
size_t
atomicio6(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n,
int (*cb)(void *, size_t), void *);
size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
#define vwrite (ssize_t (*)(int, void *, size_t))write #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);
/* /*
* ensure all of data on socket comes through. f==readv || f==writev * ensure all of data on socket comes through. f==readv || f==writev
*/ */
size_t
atomiciov6(ssize_t (*f) (int, const struct iovec *, int), int fd,
const struct iovec *_iov, int iovcnt, int (*cb)(void *, size_t), void *);
size_t atomiciov(ssize_t (*)(int, const struct iovec *, int), size_t atomiciov(ssize_t (*)(int, const struct iovec *, int),
int, const struct iovec *, int); int, const struct iovec *, int);

66
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.80 2010/07/21 02:10:58 djm Exp $ */ /* $OpenBSD: misc.c,v 1.81 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005,2006 Damien Miller. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
@ -860,6 +860,70 @@ timingsafe_bcmp(const void *b1, const void *b2, size_t n)
ret |= *p1++ ^ *p2++; ret |= *p1++ ^ *p2++;
return (ret != 0); return (ret != 0);
} }
void
bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
{
bw->buflen = buflen;
bw->rate = kbps;
bw->thresh = bw->rate;
bw->lamt = 0;
timerclear(&bw->bwstart);
timerclear(&bw->bwend);
}
/* Callback from read/write loop to insert bandwidth-limiting delays */
void
bandwidth_limit(struct bwlimit *bw, size_t read_len)
{
u_int64_t waitlen;
struct timespec ts, rm;
if (!timerisset(&bw->bwstart)) {
gettimeofday(&bw->bwstart, NULL);
return;
}
bw->lamt += read_len;
if (bw->lamt < bw->thresh)
return;
gettimeofday(&bw->bwend, NULL);
timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
if (!timerisset(&bw->bwend))
return;
bw->lamt *= 8;
waitlen = (double)1000000L * bw->lamt / bw->rate;
bw->bwstart.tv_sec = waitlen / 1000000L;
bw->bwstart.tv_usec = waitlen % 1000000L;
if (timercmp(&bw->bwstart, &bw->bwend, >)) {
timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
/* Adjust the wait time */
if (bw->bwend.tv_sec) {
bw->thresh /= 2;
if (bw->thresh < bw->buflen / 4)
bw->thresh = bw->buflen / 4;
} else if (bw->bwend.tv_usec < 10000) {
bw->thresh *= 2;
if (bw->thresh > bw->buflen * 8)
bw->thresh = bw->buflen * 8;
}
TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
while (nanosleep(&ts, &rm) == -1) {
if (errno != EINTR)
break;
ts = rm;
}
}
bw->lamt = 0;
gettimeofday(&bw->bwstart, NULL);
}
void void
sock_set_v6only(int s) sock_set_v6only(int s)
{ {

11
misc.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.43 2010/07/13 23:13:16 djm Exp $ */ /* $OpenBSD: misc.h,v 1.44 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -80,6 +80,15 @@ void put_u32(void *, u_int32_t)
void put_u16(void *, u_int16_t) void put_u16(void *, u_int16_t)
__attribute__((__bounded__( __minbytes__, 1, 2))); __attribute__((__bounded__( __minbytes__, 1, 2)));
struct bwlimit {
size_t buflen;
u_int64_t rate, thresh, lamt;
struct timeval bwstart, bwend;
};
void bandwidth_limit_init(struct bwlimit *, u_int64_t, size_t);
void bandwidth_limit(struct bwlimit *, size_t);
/* readpass.c */ /* readpass.c */

120
scp.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: scp.c,v 1.166 2010/07/01 13:06:59 millert Exp $ */ /* $OpenBSD: scp.c,v 1.167 2010/09/22 22:58:51 djm Exp $ */
/* /*
* scp - secure remote copy. This is basically patched BSD rcp which * scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd). * uses ssh to do the data transfer (instead of using rcmd).
@ -120,13 +120,12 @@ extern char *__progname;
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout);
void bwlimit(int);
/* Struct for addargs */ /* Struct for addargs */
arglist args; arglist args;
/* Bandwidth limit */ /* Bandwidth limit */
off_t limit_rate = 0; long long limit_kbps = 0;
struct bwlimit bwlimit;
/* Name of current file being transferred. */ /* Name of current file being transferred. */
char *curfile; char *curfile;
@ -312,15 +311,14 @@ void sink(int, char *[]);
void source(int, char *[]); void source(int, char *[]);
void tolocal(int, char *[]); void tolocal(int, char *[]);
void toremote(char *, int, char *[]); void toremote(char *, int, char *[]);
size_t scpio(ssize_t (*)(int, void *, size_t), int, void *, size_t, off_t *);
void usage(void); void usage(void);
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int ch, fflag, tflag, status, n; int ch, fflag, tflag, status, n;
double speed; char *targ, **newargv;
char *targ, *endp, **newargv; const char *errstr;
extern char *optarg; extern char *optarg;
extern int optind; extern int optind;
@ -369,10 +367,12 @@ main(int argc, char **argv)
addargs(&args, "-oBatchmode yes"); addargs(&args, "-oBatchmode yes");
break; break;
case 'l': case 'l':
speed = strtod(optarg, &endp); limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
if (speed <= 0 || *endp != '\0') &errstr);
if (errstr != NULL)
usage(); usage();
limit_rate = speed * 1024; limit_kbps *= 1024; /* kbps */
bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN);
break; break;
case 'p': case 'p':
pflag = 1; pflag = 1;
@ -474,41 +474,16 @@ main(int argc, char **argv)
exit(errs != 0); exit(errs != 0);
} }
/* /* Callback from atomicio6 to update progress meter and limit bandwidth */
* atomicio-like wrapper that also applies bandwidth limits and updates static int
* the progressmeter counter. scpio(void *_cnt, size_t s)
*/
size_t
scpio(ssize_t (*f)(int, void *, size_t), int fd, void *_p, size_t l, off_t *c)
{ {
u_char *p = (u_char *)_p; off_t *cnt = (off_t *)_cnt;
size_t offset;
ssize_t r;
struct pollfd pfd;
pfd.fd = fd; *cnt += s;
pfd.events = f == read ? POLLIN : POLLOUT; if (limit_kbps > 0)
for (offset = 0; offset < l;) { bandwidth_limit(&bwlimit, s);
r = f(fd, p + offset, l - offset); return 0;
if (r == 0) {
errno = EPIPE;
return offset;
}
if (r < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK) {
(void)poll(&pfd, 1, -1); /* Ignore errors */
continue;
}
return offset;
}
offset += (size_t)r;
*c += (off_t)r;
if (limit_rate)
bwlimit(r);
}
return offset;
} }
void void
@ -750,7 +725,7 @@ next: if (fd != -1) {
(void)atomicio(vwrite, remout, bp->buf, amt); (void)atomicio(vwrite, remout, bp->buf, amt);
continue; continue;
} }
if (scpio(vwrite, remout, bp->buf, amt, if (atomicio6(vwrite, remout, bp->buf, amt, scpio,
&statbytes) != amt) &statbytes) != amt)
haderr = errno; haderr = errno;
} }
@ -824,60 +799,6 @@ rsource(char *name, struct stat *statp)
(void) response(); (void) response();
} }
void
bwlimit(int amount)
{
static struct timeval bwstart, bwend;
static int lamt, thresh = 16384;
u_int64_t waitlen;
struct timespec ts, rm;
if (!timerisset(&bwstart)) {
gettimeofday(&bwstart, NULL);
return;
}
lamt += amount;
if (lamt < thresh)
return;
gettimeofday(&bwend, NULL);
timersub(&bwend, &bwstart, &bwend);
if (!timerisset(&bwend))
return;
lamt *= 8;
waitlen = (double)1000000L * lamt / limit_rate;
bwstart.tv_sec = waitlen / 1000000L;
bwstart.tv_usec = waitlen % 1000000L;
if (timercmp(&bwstart, &bwend, >)) {
timersub(&bwstart, &bwend, &bwend);
/* Adjust the wait time */
if (bwend.tv_sec) {
thresh /= 2;
if (thresh < 2048)
thresh = 2048;
} else if (bwend.tv_usec < 10000) {
thresh *= 2;
if (thresh > COPY_BUFLEN * 4)
thresh = COPY_BUFLEN * 4;
}
TIMEVAL_TO_TIMESPEC(&bwend, &ts);
while (nanosleep(&ts, &rm) == -1) {
if (errno != EINTR)
break;
ts = rm;
}
}
lamt = 0;
gettimeofday(&bwstart, NULL);
}
void void
sink(int argc, char **argv) sink(int argc, char **argv)
{ {
@ -1071,7 +992,8 @@ bad: run_err("%s: %s", np, strerror(errno));
amt = size - i; amt = size - i;
count += amt; count += amt;
do { do {
j = scpio(read, remin, cp, amt, &statbytes); j = atomicio6(read, remin, cp, amt,
scpio, &statbytes);
if (j == 0) { if (j == 0) {
run_err("%s", j != EPIPE ? run_err("%s", j != EPIPE ?
strerror(errno) : strerror(errno) :

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.c,v 1.92 2010/07/19 03:16:33 djm Exp $ */ /* $OpenBSD: sftp-client.c,v 1.93 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
* *
@ -76,14 +76,26 @@ struct sftp_conn {
#define SFTP_EXT_STATVFS 0x00000002 #define SFTP_EXT_STATVFS 0x00000002
#define SFTP_EXT_FSTATVFS 0x00000004 #define SFTP_EXT_FSTATVFS 0x00000004
u_int exts; u_int exts;
u_int64_t limit_kbps;
struct bwlimit bwlimit_in, bwlimit_out;
}; };
static char * static char *
get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
__attribute__((format(printf, 4, 5))); const char *errfmt, ...) __attribute__((format(printf, 4, 5)));
/* ARGSUSED */
static int
sftpio(void *_bwlimit, size_t amount)
{
struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit;
bandwidth_limit(bwlimit, amount);
return 0;
}
static void static void
send_msg(int fd, Buffer *m) send_msg(struct sftp_conn *conn, Buffer *m)
{ {
u_char mlen[4]; u_char mlen[4];
struct iovec iov[2]; struct iovec iov[2];
@ -98,19 +110,22 @@ send_msg(int fd, Buffer *m)
iov[1].iov_base = buffer_ptr(m); iov[1].iov_base = buffer_ptr(m);
iov[1].iov_len = buffer_len(m); iov[1].iov_len = buffer_len(m);
if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen)) if (atomiciov6(writev, conn->fd_out, iov, 2,
conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) !=
buffer_len(m) + sizeof(mlen))
fatal("Couldn't send packet: %s", strerror(errno)); fatal("Couldn't send packet: %s", strerror(errno));
buffer_clear(m); buffer_clear(m);
} }
static void static void
get_msg(int fd, Buffer *m) get_msg(struct sftp_conn *conn, Buffer *m)
{ {
u_int msg_len; u_int msg_len;
buffer_append_space(m, 4); buffer_append_space(m, 4);
if (atomicio(read, fd, buffer_ptr(m), 4) != 4) { if (atomicio6(read, conn->fd_in, buffer_ptr(m), 4,
conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) {
if (errno == EPIPE) if (errno == EPIPE)
fatal("Connection closed"); fatal("Connection closed");
else else
@ -122,7 +137,9 @@ get_msg(int fd, Buffer *m)
fatal("Received message too long %u", msg_len); fatal("Received message too long %u", msg_len);
buffer_append_space(m, msg_len); buffer_append_space(m, msg_len);
if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) { if (atomicio6(read, conn->fd_in, buffer_ptr(m), msg_len,
conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in)
!= msg_len) {
if (errno == EPIPE) if (errno == EPIPE)
fatal("Connection closed"); fatal("Connection closed");
else else
@ -131,7 +148,7 @@ get_msg(int fd, Buffer *m)
} }
static void static void
send_string_request(int fd, u_int id, u_int code, char *s, send_string_request(struct sftp_conn *conn, u_int id, u_int code, char *s,
u_int len) u_int len)
{ {
Buffer msg; Buffer msg;
@ -140,14 +157,14 @@ send_string_request(int fd, u_int id, u_int code, char *s,
buffer_put_char(&msg, code); buffer_put_char(&msg, code);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_string(&msg, s, len); buffer_put_string(&msg, s, len);
send_msg(fd, &msg); send_msg(conn, &msg);
debug3("Sent message fd %d T:%u I:%u", fd, code, id); debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
buffer_free(&msg); buffer_free(&msg);
} }
static void static void
send_string_attrs_request(int fd, u_int id, u_int code, char *s, send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code,
u_int len, Attrib *a) char *s, u_int len, Attrib *a)
{ {
Buffer msg; Buffer msg;
@ -156,19 +173,19 @@ send_string_attrs_request(int fd, u_int id, u_int code, char *s,
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_string(&msg, s, len); buffer_put_string(&msg, s, len);
encode_attrib(&msg, a); encode_attrib(&msg, a);
send_msg(fd, &msg); send_msg(conn, &msg);
debug3("Sent message fd %d T:%u I:%u", fd, code, id); debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id);
buffer_free(&msg); buffer_free(&msg);
} }
static u_int static u_int
get_status(int fd, u_int expected_id) get_status(struct sftp_conn *conn, u_int expected_id)
{ {
Buffer msg; Buffer msg;
u_int type, id, status; u_int type, id, status;
buffer_init(&msg); buffer_init(&msg);
get_msg(fd, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -183,11 +200,12 @@ get_status(int fd, u_int expected_id)
debug3("SSH2_FXP_STATUS %u", status); debug3("SSH2_FXP_STATUS %u", status);
return(status); return status;
} }
static char * static char *
get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...) get_handle(struct sftp_conn *conn, u_int expected_id, u_int *len,
const char *errfmt, ...)
{ {
Buffer msg; Buffer msg;
u_int type, id; u_int type, id;
@ -201,7 +219,7 @@ get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
va_end(args); va_end(args);
buffer_init(&msg); buffer_init(&msg);
get_msg(fd, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -225,14 +243,14 @@ get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
} }
static Attrib * static Attrib *
get_decode_stat(int fd, u_int expected_id, int quiet) get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet)
{ {
Buffer msg; Buffer msg;
u_int type, id; u_int type, id;
Attrib *a; Attrib *a;
buffer_init(&msg); buffer_init(&msg);
get_msg(fd, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -260,14 +278,14 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
} }
static int static int
get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id, get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st,
int quiet) u_int expected_id, int quiet)
{ {
Buffer msg; Buffer msg;
u_int type, id, flag; u_int type, id, flag;
buffer_init(&msg); buffer_init(&msg);
get_msg(fd, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -311,21 +329,29 @@ get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
} }
struct sftp_conn * struct sftp_conn *
do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
u_int64_t limit_kbps)
{ {
u_int type, exts = 0; u_int type;
int version;
Buffer msg; Buffer msg;
struct sftp_conn *ret; struct sftp_conn *ret;
ret = xmalloc(sizeof(*ret));
ret->fd_in = fd_in;
ret->fd_out = fd_out;
ret->transfer_buflen = transfer_buflen;
ret->num_requests = num_requests;
ret->exts = 0;
ret->limit_kbps = 0;
buffer_init(&msg); buffer_init(&msg);
buffer_put_char(&msg, SSH2_FXP_INIT); buffer_put_char(&msg, SSH2_FXP_INIT);
buffer_put_int(&msg, SSH2_FILEXFER_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
send_msg(fd_out, &msg); send_msg(ret, &msg);
buffer_clear(&msg); buffer_clear(&msg);
get_msg(fd_in, &msg); get_msg(ret, &msg);
/* Expecting a VERSION reply */ /* Expecting a VERSION reply */
if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) { 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)
buffer_free(&msg); buffer_free(&msg);
return(NULL); return(NULL);
} }
version = buffer_get_int(&msg); ret->version = buffer_get_int(&msg);
debug2("Remote version: %d", version); debug2("Remote version: %u", ret->version);
/* Check for extensions */ /* Check for extensions */
while (buffer_len(&msg) > 0) { 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)
if (strcmp(name, "posix-rename@openssh.com") == 0 && if (strcmp(name, "posix-rename@openssh.com") == 0 &&
strcmp(value, "1") == 0) { strcmp(value, "1") == 0) {
exts |= SFTP_EXT_POSIX_RENAME; ret->exts |= SFTP_EXT_POSIX_RENAME;
known = 1; known = 1;
} else if (strcmp(name, "statvfs@openssh.com") == 0 && } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
strcmp(value, "2") == 0) { strcmp(value, "2") == 0) {
exts |= SFTP_EXT_STATVFS; ret->exts |= SFTP_EXT_STATVFS;
known = 1; known = 1;
} if (strcmp(name, "fstatvfs@openssh.com") == 0 && } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
strcmp(value, "2") == 0) { strcmp(value, "2") == 0) {
exts |= SFTP_EXT_FSTATVFS; ret->exts |= SFTP_EXT_FSTATVFS;
known = 1; known = 1;
} }
if (known) { if (known) {
@ -369,26 +395,25 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
buffer_free(&msg); buffer_free(&msg);
ret = xmalloc(sizeof(*ret));
ret->fd_in = fd_in;
ret->fd_out = fd_out;
ret->transfer_buflen = transfer_buflen;
ret->num_requests = num_requests;
ret->version = version;
ret->msg_id = 1;
ret->exts = exts;
/* Some filexfer v.0 servers don't support large packets */ /* Some filexfer v.0 servers don't support large packets */
if (version == 0) if (ret->version == 0)
ret->transfer_buflen = MIN(ret->transfer_buflen, 20480); ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
return(ret); ret->limit_kbps = limit_kbps;
if (ret->limit_kbps > 0) {
bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps,
ret->transfer_buflen);
bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps,
ret->transfer_buflen);
}
return ret;
} }
u_int u_int
sftp_proto_version(struct sftp_conn *conn) sftp_proto_version(struct sftp_conn *conn)
{ {
return(conn->version); return conn->version;
} }
int int
@ -403,16 +428,16 @@ do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
buffer_put_char(&msg, SSH2_FXP_CLOSE); buffer_put_char(&msg, SSH2_FXP_CLOSE);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_string(&msg, handle, handle_len); buffer_put_string(&msg, handle, handle_len);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message SSH2_FXP_CLOSE I:%u", id); debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't close file: %s", fx2txt(status)); error("Couldn't close file: %s", fx2txt(status));
buffer_free(&msg); buffer_free(&msg);
return(status); return status;
} }
@ -430,14 +455,14 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
buffer_put_char(&msg, SSH2_FXP_OPENDIR); buffer_put_char(&msg, SSH2_FXP_OPENDIR);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_cstring(&msg, path); buffer_put_cstring(&msg, path);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
buffer_clear(&msg); buffer_clear(&msg);
handle = get_handle(conn->fd_in, id, &handle_len, handle = get_handle(conn, id, &handle_len,
"remote readdir(\"%s\")", path); "remote readdir(\"%s\")", path);
if (handle == NULL) if (handle == NULL)
return(-1); return -1;
if (dir) { if (dir) {
ents = 0; ents = 0;
@ -454,11 +479,11 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
buffer_put_char(&msg, SSH2_FXP_READDIR); buffer_put_char(&msg, SSH2_FXP_READDIR);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_string(&msg, handle, handle_len); buffer_put_string(&msg, handle, handle_len);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
buffer_clear(&msg); buffer_clear(&msg);
get_msg(conn->fd_in, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -537,7 +562,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
**dir = NULL; **dir = NULL;
} }
return(0); return 0;
} }
int int
@ -566,9 +591,8 @@ do_rm(struct sftp_conn *conn, char *path)
debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_REMOVE, path, send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path));
strlen(path)); status = get_status(conn, id);
status = get_status(conn->fd_in, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't delete file: %s", fx2txt(status)); error("Couldn't delete file: %s", fx2txt(status));
return(status); return(status);
@ -580,10 +604,10 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
u_int status, id; u_int status, id;
id = conn->msg_id++; id = conn->msg_id++;
send_string_attrs_request(conn->fd_out, id, SSH2_FXP_MKDIR, path, send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path,
strlen(path), a); strlen(path), a);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK && printflag) if (status != SSH2_FX_OK && printflag)
error("Couldn't create directory: %s", fx2txt(status)); error("Couldn't create directory: %s", fx2txt(status));
@ -596,10 +620,10 @@ do_rmdir(struct sftp_conn *conn, char *path)
u_int status, id; u_int status, id;
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_RMDIR, path, send_string_request(conn, id, SSH2_FXP_RMDIR, path,
strlen(path)); strlen(path));
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't remove directory: %s", fx2txt(status)); error("Couldn't remove directory: %s", fx2txt(status));
@ -613,11 +637,11 @@ do_stat(struct sftp_conn *conn, char *path, int quiet)
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn->fd_out, id, send_string_request(conn, id,
conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
path, strlen(path)); path, strlen(path));
return(get_decode_stat(conn->fd_in, id, quiet)); return(get_decode_stat(conn, id, quiet));
} }
Attrib * Attrib *
@ -634,10 +658,10 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet)
} }
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_LSTAT, path, send_string_request(conn, id, SSH2_FXP_LSTAT, path,
strlen(path)); strlen(path));
return(get_decode_stat(conn->fd_in, id, quiet)); return(get_decode_stat(conn, id, quiet));
} }
#ifdef notyet #ifdef notyet
@ -647,10 +671,10 @@ do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
u_int id; u_int id;
id = conn->msg_id++; id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_FSTAT, handle, send_string_request(conn, id, SSH2_FXP_FSTAT, handle,
handle_len); handle_len);
return(get_decode_stat(conn->fd_in, id, quiet)); return(get_decode_stat(conn, id, quiet));
} }
#endif #endif
@ -660,10 +684,10 @@ do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
u_int status, id; u_int status, id;
id = conn->msg_id++; id = conn->msg_id++;
send_string_attrs_request(conn->fd_out, id, SSH2_FXP_SETSTAT, path, send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path,
strlen(path), a); strlen(path), a);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't setstat on \"%s\": %s", path, error("Couldn't setstat on \"%s\": %s", path,
fx2txt(status)); fx2txt(status));
@ -678,10 +702,10 @@ do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
u_int status, id; u_int status, id;
id = conn->msg_id++; id = conn->msg_id++;
send_string_attrs_request(conn->fd_out, id, SSH2_FXP_FSETSTAT, handle, send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle,
handle_len, a); handle_len, a);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't fsetstat: %s", fx2txt(status)); error("Couldn't fsetstat: %s", fx2txt(status));
@ -697,12 +721,12 @@ do_realpath(struct sftp_conn *conn, char *path)
Attrib *a; Attrib *a;
expected_id = id = conn->msg_id++; expected_id = id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_REALPATH, path, send_string_request(conn, id, SSH2_FXP_REALPATH, path,
strlen(path)); strlen(path));
buffer_init(&msg); buffer_init(&msg);
get_msg(conn->fd_in, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -756,13 +780,13 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
} }
buffer_put_cstring(&msg, oldpath); buffer_put_cstring(&msg, oldpath);
buffer_put_cstring(&msg, newpath); buffer_put_cstring(&msg, newpath);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message %s \"%s\" -> \"%s\"", debug3("Sent message %s \"%s\" -> \"%s\"",
(conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" : (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
"SSH2_FXP_RENAME", oldpath, newpath); "SSH2_FXP_RENAME", oldpath, newpath);
buffer_free(&msg); buffer_free(&msg);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
newpath, fx2txt(status)); newpath, fx2txt(status));
@ -789,12 +813,12 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_cstring(&msg, oldpath); buffer_put_cstring(&msg, oldpath);
buffer_put_cstring(&msg, newpath); buffer_put_cstring(&msg, newpath);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
newpath); newpath);
buffer_free(&msg); buffer_free(&msg);
status = get_status(conn->fd_in, id); status = get_status(conn, id);
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
newpath, fx2txt(status)); newpath, fx2txt(status));
@ -812,12 +836,11 @@ do_readlink(struct sftp_conn *conn, char *path)
Attrib *a; Attrib *a;
expected_id = id = conn->msg_id++; expected_id = id = conn->msg_id++;
send_string_request(conn->fd_out, id, SSH2_FXP_READLINK, path, send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path));
strlen(path));
buffer_init(&msg); buffer_init(&msg);
get_msg(conn->fd_in, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
@ -871,10 +894,10 @@ do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_cstring(&msg, "statvfs@openssh.com"); buffer_put_cstring(&msg, "statvfs@openssh.com");
buffer_put_cstring(&msg, path); buffer_put_cstring(&msg, path);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
buffer_free(&msg); buffer_free(&msg);
return get_decode_statvfs(conn->fd_in, st, id, quiet); return get_decode_statvfs(conn, st, id, quiet);
} }
#ifdef notyet #ifdef notyet
@ -898,16 +921,16 @@ do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_cstring(&msg, "fstatvfs@openssh.com"); buffer_put_cstring(&msg, "fstatvfs@openssh.com");
buffer_put_string(&msg, handle, handle_len); buffer_put_string(&msg, handle, handle_len);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
buffer_free(&msg); buffer_free(&msg);
return get_decode_statvfs(conn->fd_in, st, id, quiet); return get_decode_statvfs(conn, st, id, quiet);
} }
#endif #endif
static void static void
send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
char *handle, u_int handle_len) u_int len, char *handle, u_int handle_len)
{ {
Buffer msg; Buffer msg;
@ -918,7 +941,7 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
buffer_put_string(&msg, handle, handle_len); buffer_put_string(&msg, handle, handle_len);
buffer_put_int64(&msg, offset); buffer_put_int64(&msg, offset);
buffer_put_int(&msg, len); buffer_put_int(&msg, len);
send_msg(fd_out, &msg); send_msg(conn, &msg);
buffer_free(&msg); buffer_free(&msg);
} }
@ -976,10 +999,10 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
buffer_put_int(&msg, SSH2_FXF_READ); buffer_put_int(&msg, SSH2_FXF_READ);
attrib_clear(&junk); /* Send empty attributes */ attrib_clear(&junk); /* Send empty attributes */
encode_attrib(&msg, &junk); encode_attrib(&msg, &junk);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
handle = get_handle(conn->fd_in, id, &handle_len, handle = get_handle(conn, id, &handle_len,
"remote open(\"%s\")", remote_path); "remote open(\"%s\")", remote_path);
if (handle == NULL) { if (handle == NULL) {
buffer_free(&msg); buffer_free(&msg);
@ -1032,12 +1055,12 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
offset += buflen; offset += buflen;
num_req++; num_req++;
TAILQ_INSERT_TAIL(&requests, req, tq); TAILQ_INSERT_TAIL(&requests, req, tq);
send_read_request(conn->fd_out, req->id, req->offset, send_read_request(conn, req->id, req->offset,
req->len, handle, handle_len); req->len, handle, handle_len);
} }
buffer_clear(&msg); buffer_clear(&msg);
get_msg(conn->fd_in, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
id = buffer_get_int(&msg); id = buffer_get_int(&msg);
debug3("Received reply T:%u I:%u R:%d", type, id, max_req); 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,
req->id = conn->msg_id++; req->id = conn->msg_id++;
req->len -= len; req->len -= len;
req->offset += len; req->offset += len;
send_read_request(conn->fd_out, req->id, send_read_request(conn, req->id,
req->offset, req->len, handle, handle_len); req->offset, req->len, handle, handle_len);
/* Reduce the request size */ /* Reduce the request size */
if (len < buflen) if (len < buflen)
@ -1327,12 +1350,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
buffer_put_cstring(&msg, remote_path); buffer_put_cstring(&msg, remote_path);
buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC); buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
encode_attrib(&msg, &a); encode_attrib(&msg, &a);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
buffer_clear(&msg); buffer_clear(&msg);
handle = get_handle(conn->fd_in, id, &handle_len, handle = get_handle(conn, id, &handle_len,
"remote open(\"%s\")", remote_path); "remote open(\"%s\")", remote_path);
if (handle == NULL) { if (handle == NULL) {
close(local_fd); close(local_fd);
@ -1381,7 +1404,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
buffer_put_string(&msg, handle, handle_len); buffer_put_string(&msg, handle, handle_len);
buffer_put_int64(&msg, offset); buffer_put_int64(&msg, offset);
buffer_put_string(&msg, data, len); buffer_put_string(&msg, data, len);
send_msg(conn->fd_out, &msg); send_msg(conn, &msg);
debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
id, (unsigned long long)offset, len); id, (unsigned long long)offset, len);
} else if (TAILQ_FIRST(&acks) == NULL) } else if (TAILQ_FIRST(&acks) == NULL)
@ -1395,7 +1418,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
u_int r_id; u_int r_id;
buffer_clear(&msg); buffer_clear(&msg);
get_msg(conn->fd_in, &msg); get_msg(conn, &msg);
type = buffer_get_char(&msg); type = buffer_get_char(&msg);
r_id = buffer_get_int(&msg); r_id = buffer_get_int(&msg);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-client.h,v 1.18 2009/08/18 18:36:20 djm Exp $ */ /* $OpenBSD: sftp-client.h,v 1.19 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@ -51,7 +51,7 @@ struct sftp_statvfs {
* Initialise a SSH filexfer connection. Returns NULL on error or * Initialise a SSH filexfer connection. Returns NULL on error or
* a pointer to a initialized sftp_conn struct on success. * a pointer to a initialized sftp_conn struct on success.
*/ */
struct sftp_conn *do_init(int, int, u_int, u_int); struct sftp_conn *do_init(int, int, u_int, u_int, u_int64_t);
u_int sftp_proto_version(struct sftp_conn *); u_int sftp_proto_version(struct sftp_conn *);

7
sftp.1
View File

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp.1,v 1.84 2010/09/19 21:30:05 jmc Exp $ .\" $OpenBSD: sftp.1,v 1.85 2010/09/22 22:58:51 djm Exp $
.\" .\"
.\" Copyright (c) 2001 Damien Miller. All rights reserved. .\" Copyright (c) 2001 Damien Miller. All rights reserved.
.\" .\"
@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: September 19 2010 $ .Dd $Mdocdate: September 22 2010 $
.Dt SFTP 1 .Dt SFTP 1
.Os .Os
.Sh NAME .Sh NAME
@ -38,6 +38,7 @@
.Op Fl D Ar sftp_server_path .Op Fl D Ar sftp_server_path
.Op Fl F Ar ssh_config .Op Fl F Ar ssh_config
.Op Fl i Ar identity_file .Op Fl i Ar identity_file
.Op Fl l Ar limit
.Op Fl o Ar ssh_option .Op Fl o Ar ssh_option
.Op Fl P Ar port .Op Fl P Ar port
.Op Fl R Ar num_requests .Op Fl R Ar num_requests
@ -159,6 +160,8 @@ Selects the file from which the identity (private key) for public key
authentication is read. authentication is read.
This option is directly passed to This option is directly passed to
.Xr ssh 1 . .Xr ssh 1 .
.It Fl l Ar limit
Limits the used bandwidth, specified in Kbit/s.
.It Fl o Ar ssh_option .It Fl o Ar ssh_option
Can be used to pass options to Can be used to pass options to
.Nm ssh .Nm ssh

15
sftp.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp.c,v 1.125 2010/06/18 00:58:39 djm Exp $ */ /* $OpenBSD: sftp.c,v 1.126 2010/09/22 22:58:51 djm Exp $ */
/* /*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
* *
@ -2073,6 +2073,7 @@ main(int argc, char **argv)
int debug_level = 0, sshver = 2; int debug_level = 0, sshver = 2;
char *file1 = NULL, *sftp_server = NULL; char *file1 = NULL, *sftp_server = NULL;
char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
const char *errstr;
LogLevel ll = SYSLOG_LEVEL_INFO; LogLevel ll = SYSLOG_LEVEL_INFO;
arglist args; arglist args;
extern int optind; extern int optind;
@ -2080,6 +2081,7 @@ main(int argc, char **argv)
struct sftp_conn *conn; struct sftp_conn *conn;
size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; size_t copy_buffer_len = DEFAULT_COPY_BUFLEN;
size_t num_requests = DEFAULT_NUM_REQUESTS; size_t num_requests = DEFAULT_NUM_REQUESTS;
long long limit_kbps = 0;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd(); sanitise_stdfd();
@ -2097,7 +2099,7 @@ main(int argc, char **argv)
infile = stdin; infile = stdin;
while ((ch = getopt(argc, argv, while ((ch = getopt(argc, argv,
"1246hpqrvCc:D:i:o:s:S:b:B:F:P:R:")) != -1) { "1246hpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) {
switch (ch) { switch (ch) {
/* Passed through to ssh(1) */ /* Passed through to ssh(1) */
case '4': case '4':
@ -2158,6 +2160,13 @@ main(int argc, char **argv)
case 'D': case 'D':
sftp_direct = optarg; sftp_direct = optarg;
break; break;
case 'l':
limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
&errstr);
if (errstr != NULL)
usage();
limit_kbps *= 1024; /* kbps */
break;
case 'r': case 'r':
global_rflag = 1; global_rflag = 1;
break; break;
@ -2235,7 +2244,7 @@ main(int argc, char **argv)
} }
freeargs(&args); freeargs(&args);
conn = do_init(in, out, copy_buffer_len, num_requests); conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps);
if (conn == NULL) if (conn == NULL)
fatal("Couldn't initialise connection to server"); fatal("Couldn't initialise connection to server");