summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2007-06-16 08:58:34 +0000
committerMarc Espie <espie@cvs.openbsd.org>2007-06-16 08:58:34 +0000
commit5ba6b2ba2df8fc791ddaed159225dbbcdf674cf5 (patch)
tree6fcf6a2b18e6258d84d0a2dddcd8eaa4d5a2563a
parent3c0ffca3b0a9e275fb40c1bb21cb96e0da507384 (diff)
implement a `keep-alive' option that sends bytes over an inactive
connection. The FTP protocol provides us with a NOOP operation that is perfectly suitable for that, and so far servers are happy with it. Sending the command slowly is an idea I borrowed from spamd. No change for people not using the option, so it can't break normal ftp. okay beck@, jmc@
-rw-r--r--usr.bin/ftp/extern.h3
-rw-r--r--usr.bin/ftp/ftp.118
-rw-r--r--usr.bin/ftp/ftp.c79
-rw-r--r--usr.bin/ftp/main.c26
4 files changed, 114 insertions, 12 deletions
diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h
index 864913387d6..8fefe122e5b 100644
--- a/usr.bin/ftp/extern.h
+++ b/usr.bin/ftp/extern.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: extern.h,v 1.30 2007/06/13 13:52:26 pyr Exp $ */
+/* $OpenBSD: extern.h,v 1.31 2007/06/16 08:58:33 espie Exp $ */
/* $NetBSD: extern.h,v 1.17 1997/08/18 10:20:19 lukem Exp $ */
/*
@@ -208,6 +208,7 @@ extern int proxy;
extern char reply_string[];
extern off_t restart_point;
extern int NCMDS;
+extern int keep_alive_timeout;
extern char *__progname; /* from crt0.o */
diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1
index b6dfe8fae35..ffeae81112b 100644
--- a/usr.bin/ftp/ftp.1
+++ b/usr.bin/ftp/ftp.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ftp.1,v 1.60 2007/06/13 18:43:16 jmc Exp $
+.\" $OpenBSD: ftp.1,v 1.61 2007/06/16 08:58:33 espie Exp $
.\" $NetBSD: ftp.1,v 1.22 1997/08/18 10:20:22 lukem Exp $
.\"
.\" Copyright (c) 1985, 1989, 1990, 1993
@@ -30,7 +30,7 @@
.\"
.\" @(#)ftp.1 8.3 (Berkeley) 10/9/94
.\"
-.Dd $Mdocdate: June 13 2007 $
+.Dd $Mdocdate: June 16 2007 $
.Dt FTP 1
.Os
.Sh NAME
@@ -40,6 +40,7 @@
.Nm ftp
.Op Fl 46AadEegimnptVv
.Op Fl c Ar cookie
+.Op Fl k Ar seconds
.Op Fl P Ar port
.Op Fl r Ar seconds
.Op Ar host Op Ar port
@@ -134,6 +135,19 @@ Disables file name globbing.
.It Fl i
Turns off interactive prompting during
multiple file transfers.
+.It Fl k Ar seconds
+Sends a byte after each
+.Ar seconds
+period over the control connection during long transfers,
+so that incorrectly configured network equipment won't
+agressively drop it.
+The FTP protocol supports a
+.Dv NOOP
+command that can be used for that purpose.
+This assumes the FTP server can deal with extra commands coming over
+the control connection during a transfer.
+Well-behaved servers queue those commands, and process them after the
+transfer.
.It Fl m
Causes
.Nm
diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c
index 252b27585f2..3a9cf82bd69 100644
--- a/usr.bin/ftp/ftp.c
+++ b/usr.bin/ftp/ftp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ftp.c,v 1.66 2007/03/06 05:16:01 beck Exp $ */
+/* $OpenBSD: ftp.c,v 1.67 2007/06/16 08:58:33 espie Exp $ */
/* $NetBSD: ftp.c,v 1.27 1997/08/18 10:20:23 lukem Exp $ */
/*
@@ -60,7 +60,7 @@
*/
#if !defined(lint) && !defined(SMALL)
-static const char rcsid[] = "$OpenBSD: ftp.c,v 1.66 2007/03/06 05:16:01 beck Exp $";
+static const char rcsid[] = "$OpenBSD: ftp.c,v 1.67 2007/06/16 08:58:33 espie Exp $";
#endif /* not lint and not SMALL */
#include <sys/types.h>
@@ -325,6 +325,73 @@ command(const char *fmt, ...)
return (r);
}
+int keep_alive_timeout = 0; /* 0 -> no timeout */
+
+static int full_noops_sent = 0;
+static time_t last_timestamp = 0; /* 0 -> no measurement yet */
+static char noop[] = "NOOP\r\n";
+#define NOOP_LENGTH (sizeof noop - 1)
+static int current_nop_pos = 0; /* 0 -> no noop started */
+
+/* to achieve keep alive, we send noop one byte at a time */
+void
+send_noop_char()
+{
+ if (debug)
+ fprintf(ttyout, "---> %c\n", noop[current_nop_pos]);
+ fputc(noop[current_nop_pos++], cout);
+ (void)fflush(cout);
+ if (current_nop_pos >= NOOP_LENGTH) {
+ full_noops_sent++;
+ current_nop_pos = 0;
+ }
+}
+
+void
+may_reset_noop_timeout()
+{
+ if (keep_alive_timeout != 0)
+ last_timestamp = time(NULL);
+}
+
+void
+may_receive_noop_ack()
+{
+ int i;
+
+ /* finish sending last incomplete noop */
+ if (current_nop_pos != 0) {
+ fputs(&(noop[current_nop_pos]), cout);
+ if (debug)
+ fprintf(ttyout, "---> %s\n", &(noop[current_nop_pos]));
+ (void)fflush(cout);
+ current_nop_pos = 0;
+ full_noops_sent++;
+ }
+ /* and get the replies */
+ for (i = 0; i < full_noops_sent; i++)
+ (void)getreply(0);
+
+ full_noops_sent = 0;
+}
+
+void
+may_send_noop_char()
+{
+ if (keep_alive_timeout != 0) {
+ if (last_timestamp != 0) {
+ time_t t = time(NULL);
+
+ if (t - last_timestamp >= keep_alive_timeout) {
+ last_timestamp = t;
+ send_noop_char();
+ }
+ } else {
+ last_timestamp = time(NULL);
+ }
+ }
+}
+
char reply_string[BUFSIZ]; /* first line of previous reply */
int
@@ -631,6 +698,7 @@ sendrequest(const char *cmd, const char *local, const char *remote,
if (dout == NULL)
goto abort;
progressmeter(-1);
+ may_reset_noop_timeout();
oldintp = signal(SIGPIPE, SIG_IGN);
switch (curtype) {
@@ -638,6 +706,7 @@ sendrequest(const char *cmd, const char *local, const char *remote,
case TYPE_L:
errno = d = 0;
while ((c = read(fileno(fin), buf, sizeof(buf))) > 0) {
+ may_send_noop_char();
bytes += c;
for (bufp = buf; c > 0; c -= d, bufp += d)
if ((d = write(fileno(dout), bufp, (size_t)c))
@@ -668,6 +737,7 @@ sendrequest(const char *cmd, const char *local, const char *remote,
case TYPE_A:
while ((c = fgetc(fin)) != EOF) {
+ may_send_noop_char();
if (c == '\n') {
while (hash && (!progress || filesize < 0) &&
(bytes >= hashbytes)) {
@@ -710,6 +780,7 @@ sendrequest(const char *cmd, const char *local, const char *remote,
(*closefunc)(fin);
(void)fclose(dout);
(void)getreply(0);
+ may_receive_noop_ack();
(void)signal(SIGINT, oldintr);
(void)signal(SIGINFO, oldinti);
if (oldintp)
@@ -938,6 +1009,7 @@ recvrequest(const char *cmd, const char * volatile local, const char *remote,
preserve = 0;
}
progressmeter(-1);
+ may_reset_noop_timeout();
switch (curtype) {
case TYPE_I:
@@ -956,6 +1028,7 @@ recvrequest(const char *cmd, const char * volatile local, const char *remote,
ssize_t wr;
size_t rd = c;
+ may_send_noop_char();
d = 0;
do {
wr = write(fileno(fout), buf + d, rd);
@@ -1018,6 +1091,7 @@ done:
}
}
while ((c = fgetc(din)) != EOF) {
+ may_send_noop_char();
if (c == '\n')
bare_lfs++;
while (c == '\r') {
@@ -1077,6 +1151,7 @@ break2:
(void)signal(SIGPIPE, oldintp);
(void)fclose(din);
(void)getreply(0);
+ may_receive_noop_ack();
if (bytes >= 0 && is_retr) {
if (bytes > 0)
ptransfer(0);
diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c
index 43ce223e1a9..c83ad2440c2 100644
--- a/usr.bin/ftp/main.c
+++ b/usr.bin/ftp/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.64 2007/06/13 18:43:16 jmc Exp $ */
+/* $OpenBSD: main.c,v 1.65 2007/06/16 08:58:33 espie Exp $ */
/* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
/*
@@ -66,7 +66,7 @@ static const char copyright[] =
#endif /* not lint */
#if !defined(lint) && !defined(SMALL)
-static const char rcsid[] = "$OpenBSD: main.c,v 1.64 2007/06/13 18:43:16 jmc Exp $";
+static const char rcsid[] = "$OpenBSD: main.c,v 1.65 2007/06/16 08:58:33 espie Exp $";
#endif /* not lint and not SMALL */
/*
@@ -181,7 +181,7 @@ main(volatile int argc, char *argv[])
cookiefile = getenv("http_cookies");
#endif
- while ((ch = getopt(argc, argv, "46Aac:dEegimno:pP:r:tvV")) != -1) {
+ while ((ch = getopt(argc, argv, "46Aac:dEegik:mno:pP:r:tvV")) != -1) {
switch (ch) {
case '4':
family = PF_INET;
@@ -227,6 +227,15 @@ main(volatile int argc, char *argv[])
interactive = 0;
break;
+ case 'k':
+ keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
+ &errstr);
+ if (errstr != NULL) {
+ warnx("keep alive amount is %s: %s", errstr,
+ optarg);
+ usage();
+ }
+ break;
case 'm':
progress = -1;
break;
@@ -252,9 +261,11 @@ main(volatile int argc, char *argv[])
case 'r':
retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
- if (errstr != NULL)
- errx(1, "retry amount is %s: %s", errstr,
+ if (errstr != NULL) {
+ warnx("retry amount is %s: %s", errstr,
optarg);
+ usage();
+ }
break;
case 't':
@@ -746,8 +757,9 @@ void
usage(void)
{
(void)fprintf(stderr,
- "usage: %s [-46AadEegimnptVv] [-c cookie] [-P port] "
- "[-r seconds] [host [port]]\n"
+ "usage: %s [-46AadEegimnptVv] [-c cookie] [-k seconds] "
+ "[-P port] [-r seconds]\n"
+ " [host [port]]\n"
" %s [-o output] ftp://[user:password@]host[:port]/file[/]\n"
" %s [-o output] http://host[:port]/file\n"
#ifndef SMALL