Eliminate another instance of the old and well-known

DoS bug that the select(2)/accept(2) pair is called on
a socket that is in the blocking I/O mode.  The bug is
triggered if a selected connection dies before the accept(2)
leading to the accept(2) blocking virtually forever.

MFC after:	1 week
This commit is contained in:
Yaroslav Tykhiy 2001-11-19 21:52:03 +00:00
parent 3e9fbbe1df
commit 4cd48bace6

View File

@ -1663,6 +1663,7 @@ dataconn(name, size, mode)
*sizebuf = '\0';
if (pdata >= 0) {
union sockunion from;
int flags;
int s, fromlen = ctrl_addr.su_len;
struct timeval timeout;
fd_set set;
@ -1673,15 +1674,27 @@ dataconn(name, size, mode)
timeout.tv_usec = 0;
timeout.tv_sec = 120;
if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 ||
(s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) {
reply(425, "Can't open data connection.");
(void) close(pdata);
pdata = -1;
return (NULL);
}
/*
* Granted a socket is in the blocking I/O mode,
* accept() will block after a successful select()
* if the selected connection dies in between.
* Therefore set the non-blocking I/O flag here.
*/
if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1)
goto pdata_err;
if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) <= 0 ||
(s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0)
goto pdata_err;
(void) close(pdata);
pdata = s;
/*
* Unset the blocking I/O flag on the child socket
* again so stdio can work on it.
*/
if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1)
goto pdata_err;
#ifdef IP_TOS
if (from.su_family == AF_INET)
{
@ -1693,6 +1706,11 @@ dataconn(name, size, mode)
reply(150, "Opening %s mode data connection for '%s'%s.",
type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
return (fdopen(pdata, mode));
pdata_err:
reply(425, "Can't open data connection.");
(void) close(pdata);
pdata = -1;
return (NULL);
}
if (data >= 0) {
reply(125, "Using existing data connection for '%s'%s.",