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:
parent
47e0ea84da
commit
50ff8d4548
|
@ -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 *);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue