Implement a timeout on the accept(2) in dataconn() and the

connect(2) in xconnect() by temporarily setting O_NONBLOCK
on the socket and using xpoll() to wait for the operation
to succeed.
The timeout used is the '-q quittime' argument (defaults to
60s for accept(2), and the system default for connect(2)).
Idea inspired by discussion with Chuck Cranor.
This may (indirectly) fix various problems with timeouts
in active mode through broken firewalls.

Implement xpoll() as a wrapper around poll(2), to make it
easier to replace on systems without a functional poll(2).
Unconditionally use xpoll() instead of conditionally using
select(2) or poll(2).
This commit is contained in:
lukem 2005-04-11 01:43:31 +00:00
parent 47e0ea84da
commit 50ff8d4548
5 changed files with 150 additions and 68 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.64 2005/02/09 23:17:27 christos Exp $ */
/* $NetBSD: extern.h,v 1.65 2005/04/11 01:43:31 lukem Exp $ */
/*-
* Copyright (c) 1996-2005 The NetBSD Foundation, Inc.
@ -250,6 +250,7 @@ void usage(void);
void user(int, char **);
int xconnect(int, const struct sockaddr *, int);
int xlisten(int, int);
int xpoll(struct pollfd *, int, int);
void *xmalloc(size_t);
StringList *xsl_init(void);
void xsl_add(StringList *, char *);

View File

@ -1,7 +1,7 @@
/* $NetBSD: ftp.c,v 1.126 2004/07/20 10:40:22 lukem Exp $ */
/* $NetBSD: ftp.c,v 1.127 2005/04/11 01:43:31 lukem Exp $ */
/*-
* Copyright (c) 1996-2004 The NetBSD Foundation, Inc.
* Copyright (c) 1996-2005 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -99,7 +99,7 @@
#if 0
static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94";
#else
__RCSID("$NetBSD: ftp.c,v 1.126 2004/07/20 10:40:22 lukem Exp $");
__RCSID("$NetBSD: ftp.c,v 1.127 2005/04/11 01:43:31 lukem Exp $");
#endif
#endif /* not lint */
@ -118,6 +118,7 @@ __RCSID("$NetBSD: ftp.c,v 1.126 2004/07/20 10:40:22 lukem Exp $");
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
@ -125,9 +126,6 @@ __RCSID("$NetBSD: ftp.c,v 1.126 2004/07/20 10:40:22 lukem Exp $");
#include <time.h>
#include <unistd.h>
#include <stdarg.h>
#ifndef USE_SELECT
#include <poll.h>
#endif
#include "ftp_var.h"
@ -539,39 +537,10 @@ getreply(int expecteof)
static int
empty(FILE *cin, FILE *din, int sec)
{
int nr;
int nfd = 0;
#ifdef USE_SELECT
struct timeval t;
fd_set rmask;
FD_ZERO(&rmask);
if (cin) {
if (nfd < fileno(cin))
nfd = fileno(cin);
FD_SET(fileno(cin), &rmask);
}
if (din) {
if (nfd < fileno(din))
nfd = fileno(din);
FD_SET(fileno(din), &rmask);
}
t.tv_sec = (long) sec;
t.tv_usec = 0;
if ((nr = select(nfd, &rmask, NULL, NULL, &t)) <= 0)
return nr;
nr = 0;
if (cin)
nr |= FD_ISSET(fileno(cin), &rmask) ? 1 : 0;
if (din)
nr |= FD_ISSET(fileno(din), &rmask) ? 2 : 0;
#else
struct pollfd pfd[2];
int nr, nfd;
struct pollfd pfd[2];
nfd = 0;
if (cin) {
pfd[nfd].fd = fileno(cin);
pfd[nfd++].events = POLLIN;
@ -582,7 +551,7 @@ empty(FILE *cin, FILE *din, int sec)
pfd[nfd++].events = POLLIN;
}
if ((nr = poll(pfd, nfd, sec * 1000)) <= 0)
if ((nr = xpoll(pfd, nfd, sec * 1000)) <= 0)
return nr;
nr = 0;
@ -591,7 +560,6 @@ empty(FILE *cin, FILE *din, int sec)
nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0;
if (din)
nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0;
#endif
return nr;
}
@ -1704,7 +1672,8 @@ initconn(void)
#endif
return (0);
bad:
(void)close(data), data = -1;
(void)close(data);
data = -1;
if (tmpno)
sendport = 1;
return (1);
@ -1713,20 +1682,61 @@ initconn(void)
FILE *
dataconn(const char *lmode)
{
struct sockinet from;
int s, fromlen = myctladdr.su_len;
struct sockinet from;
int s, fromlen, flags, rv, timeout;
struct timeval endtime, now, td;
struct pollfd pfd[1];
if (passivemode)
if (passivemode) /* passive data connection */
return (fdopen(data, lmode));
s = accept(data, (struct sockaddr *) &from.si_su, &fromlen);
if (s < 0) {
warn("accept");
(void)close(data), data = -1;
return (NULL);
/* active mode data connection */
if ((flags = fcntl(data, F_GETFL, 0)) == -1)
goto dataconn_failed; /* get current socket flags */
if (fcntl(data, F_SETFL, flags | O_NONBLOCK) == -1)
goto dataconn_failed; /* set non-blocking connect */
/* NOTE: we now must restore socket flags on successful exit */
/* limit time waiting on listening socket */
pfd[0].fd = data;
pfd[0].events = POLLIN;
(void)gettimeofday(&endtime, NULL); /* determine end time */
endtime.tv_sec += (quit_time > 0) ? quit_time: 60;
/* without -q, default to 60s */
do {
(void)gettimeofday(&now, NULL);
timersub(&endtime, &now, &td);
timeout = td.tv_sec * 1000 + td.tv_usec/1000;
if (timeout < 0)
timeout = 0;
rv = xpoll(pfd, 1, timeout);
} while (rv == -1 && errno == EINTR); /* loop until poll ! EINTR */
if (rv == -1) {
warn("poll waiting before accept");
goto dataconn_failed;
}
if (rv == 0) {
warn("poll timeout waiting before accept")
goto dataconn_failed;
}
/* (non-blocking) accept the connection */
fromlen = myctladdr.su_len;
do {
s = accept(data, (struct sockaddr *) &from.si_su, &fromlen);
} while (s == -1 && errno == EINTR); /* loop until accept ! EINTR */
if (s == -1) {
warn("accept");
goto dataconn_failed;
}
(void)close(data);
data = s;
if (fcntl(data, F_SETFL, flags) == -1) /* restore socket flags */
goto dataconn_failed;
#ifdef IPTOS_THROUGHPUT
if (from.su_family == AF_INET) {
int tos = IPTOS_THROUGHPUT;
@ -1738,6 +1748,11 @@ dataconn(const char *lmode)
}
#endif
return (fdopen(data, lmode));
dataconn_failed:
(void)close(data);
data = -1;
return (NULL);
}
void

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftp_var.h,v 1.69 2005/01/03 09:50:09 lukem Exp $ */
/* $NetBSD: ftp_var.h,v 1.70 2005/04/11 01:43:31 lukem Exp $ */
/*-
* Copyright (c) 1996-2005 The NetBSD Foundation, Inc.
@ -112,6 +112,8 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <setjmp.h>
#include <stringlist.h>

View File

@ -1,4 +1,4 @@
/* $NetBSD: util.c,v 1.117 2005/01/03 09:50:09 lukem Exp $ */
/* $NetBSD: util.c,v 1.118 2005/04/11 01:43:31 lukem Exp $ */
/*-
* Copyright (c) 1997-2005 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: util.c,v 1.117 2005/01/03 09:50:09 lukem Exp $");
__RCSID("$NetBSD: util.c,v 1.118 2005/04/11 01:43:31 lukem Exp $");
#endif /* not lint */
/*
@ -1243,28 +1243,82 @@ isipv6addr(const char *addr)
/*
* Internal version of connect(2); sets socket buffer sizes first and
* handles the syscall being interrupted.
* supports a connection timeout using a non-blocking connect(2) with
* a poll(2).
* Socket fcntl flags are temporarily updated to include O_NONBLOCK;
* these will not be reverted on connection failure.
* Returns -1 upon failure (with errno set to the problem), or 0 on success.
*/
int
xconnect(int sock, const struct sockaddr *name, int namelen)
{
int rv;
int flags, rv, timeout, error;
struct timeval endtime, now, td;
struct pollfd pfd[1];
setupsockbufsize(sock);
rv = connect(sock, name, namelen);
if (rv == -1 && errno == EINTR) {
fd_set connfd;
FD_ZERO(&connfd);
FD_SET(sock, &connfd);
do {
rv = select(sock + 1, NULL, &connfd, NULL, NULL);
} while (rv == -1 && errno == EINTR);
if (rv > 0)
rv = 0;
if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
return -1; /* get current socket flags */
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
return -1; /* set non-blocking connect */
/* NOTE: we now must restore socket flags on successful exit */
pfd[0].fd = sock;
pfd[0].events = POLLIN|POLLOUT;
if (quit_time > 0) { /* want a non default timeout */
(void)gettimeofday(&endtime, NULL);
endtime.tv_sec += quit_time; /* determine end time */
}
return (rv);
rv = connect(sock, name, namelen); /* inititate the connection */
if (rv == -1) { /* connection error */
if (errno != EINPROGRESS) /* error isn't "please wait" */
return -1;
/* connect EINPROGRESS; wait */
do {
if (quit_time > 0) { /* determine timeout */
(void)gettimeofday(&now, NULL);
timersub(&endtime, &now, &td);
timeout = td.tv_sec * 1000 + td.tv_usec/1000;
if (timeout < 0)
timeout = 0;
} else {
timeout = INFTIM;
}
pfd[0].revents = 0;
rv = xpoll(pfd, 1, timeout);
/* loop until poll ! EINTR */
} while (rv == -1 && errno == EINTR);
if (rv == 0) { /* poll (connect) timed out */
errno = ETIMEDOUT;
return -1;
}
if (rv == -1) { /* poll error */
return -1;
} else if (pfd[0].revents & (POLLIN|POLLOUT)) {
rv = sizeof(error); /* ok, or pending error */
if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
&error, &rv) == -1)
return -1; /* Solaris pending error */
if (error != 0) {
errno = error; /* BSD pending error */
return -1;
}
} else {
errno = EBADF; /* this shouldn't happen ... */
return -1;
}
}
if (fcntl(sock, F_SETFL, flags) == -1) /* restore socket flags */
return -1;
return 0;
}
/*
@ -1278,6 +1332,16 @@ xlisten(int sock, int backlog)
return (listen(sock, backlog));
}
/*
* Internal version of poll(2), to allow reimplementation by select(2)
* on platforms without the form.
*/
int
xpoll(struct pollfd *fds, int nfds, int timeout)
{
return poll(fds, nfds, timeout);
}
/*
* malloc() with inbuilt error checking
*/

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.45 2005/04/10 03:13:23 lukem Exp $ */
/* $NetBSD: version.h,v 1.46 2005/04/11 01:43:31 lukem Exp $ */
/*-
* Copyright (c) 1999-2005 The NetBSD Foundation, Inc.
* All rights reserved.
@ -40,5 +40,5 @@
#endif
#ifndef FTP_VERSION
#define FTP_VERSION "20050410"
#define FTP_VERSION "20050411"
#endif