From: Fabian Keil Date: Mon, 14 Dec 2020 12:48:08 +0000 (+0100) Subject: Bring back the select()-based fallback code X-Git-Tag: v_3_0_30~269^2 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=b2418db26b2b4b04d74c388b652c450f3b5dd6d2 Bring back the select()-based fallback code As reported by Lee it's stille needed on Windows. This reverts commit 9126f0c935e4bb64a87be7c3a1855b7768791142. --- diff --git a/jbsockets.c b/jbsockets.c index 06202a94..f3f5750e 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -196,7 +196,12 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client char service[6]; int retval; jb_socket fd; +#ifdef HAVE_POLL struct pollfd poll_fd[1]; +#else + fd_set wfds; + struct timeval timeout; +#endif #if !defined(_WIN32) && !defined(__BEOS__) int flags; #endif @@ -280,6 +285,20 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client continue; } +#ifndef HAVE_POLL +#ifndef _WIN32 + if (fd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Server socket number too high to use select(): %d >= %d", + fd, FD_SETSIZE); + close_socket(fd); + freeaddrinfo(result); + return JB_INVALID_SOCKET; + } +#endif +#endif + #ifdef FEATURE_EXTERNAL_FILTERS mark_socket_for_close_on_execute(fd); #endif @@ -327,6 +346,7 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client } #endif /* !defined(_WIN32) && !defined(__BEOS__) */ +#ifdef HAVE_POLL poll_fd[0].fd = fd; poll_fd[0].events = POLLOUT; @@ -350,6 +370,18 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client } } else if (retval > 0) +#else + /* wait for connection to complete */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = 30; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */ + if ((select((int)fd + 1, NULL, &wfds, NULL, &timeout) > 0) + && FD_ISSET(fd, &wfds)) +#endif { socklen_t optlen = sizeof(socket_error); if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &optlen)) @@ -407,7 +439,12 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli struct sockaddr_in inaddr; jb_socket fd; unsigned int addr; +#ifdef HAVE_POLL struct pollfd poll_fd[1]; +#else + fd_set wfds; + struct timeval tv[1]; +#endif #if !defined(_WIN32) && !defined(__BEOS__) int flags; #endif @@ -465,6 +502,19 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli return(JB_INVALID_SOCKET); } +#ifndef HAVE_POLL +#ifndef _WIN32 + if (fd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Server socket number too high to use select(): %d >= %d", + fd, FD_SETSIZE); + close_socket(fd); + return JB_INVALID_SOCKET; + } +#endif +#endif + set_no_delay_flag(fd); #if !defined(_WIN32) && !defined(__BEOS__) @@ -504,10 +554,22 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli } #endif /* !defined(_WIN32) && !defined(__BEOS__) */ +#ifdef HAVE_POLL poll_fd[0].fd = fd; poll_fd[0].events = POLLOUT; if (poll(poll_fd, 1, 30000) <= 0) +#else + /* wait for connection to complete */ + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + tv->tv_sec = 30; + tv->tv_usec = 0; + + /* MS Windows uses int, not SOCKET, for the 1st arg of select(). Weird! */ + if (select((int)fd + 1, NULL, &wfds, NULL, tv) <= 0) +#endif { close_socket(fd); return(JB_INVALID_SOCKET); @@ -684,12 +746,25 @@ int data_is_available(jb_socket fd, int seconds_to_wait) { int n; char buf[10]; +#ifdef HAVE_POLL struct pollfd poll_fd[1]; poll_fd[0].fd = fd; poll_fd[0].events = POLLIN; n = poll(poll_fd, 1, seconds_to_wait * 1000); +#else + fd_set rfds; + struct timeval timeout; + + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = seconds_to_wait; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + n = select(fd+1, &rfds, NULL, NULL, &timeout); +#endif /* * XXX: Do we care about the different error conditions? @@ -1199,24 +1274,41 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) int retval; int i; int max_selected_socket; +#ifdef HAVE_POLL struct pollfd poll_fds[MAX_LISTENING_SOCKETS]; nfds_t polled_sockets; +#else + fd_set selected_fds; +#endif jb_socket fd; const char *host_addr; size_t listen_addr_size; c_length = sizeof(client); +#ifdef HAVE_POLL memset(poll_fds, 0, sizeof(poll_fds)); polled_sockets = 0; +#else + /* + * Wait for a connection on any socket. + * Return immediately if no socket is listening. + * XXX: Why not treat this as fatal error? + */ + FD_ZERO(&selected_fds); +#endif max_selected_socket = 0; for (i = 0; i < MAX_LISTENING_SOCKETS; i++) { if (JB_INVALID_SOCKET != fds[i]) { +#ifdef HAVE_POLL poll_fds[i].fd = fds[i]; poll_fds[i].events = POLLIN; polled_sockets++; +#else + FD_SET(fds[i], &selected_fds); +#endif if (max_selected_socket < fds[i] + 1) { max_selected_socket = fds[i] + 1; @@ -1229,29 +1321,38 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) } do { +#ifdef HAVE_POLL retval = poll(poll_fds, polled_sockets, -1); +#else + retval = select(max_selected_socket, &selected_fds, NULL, NULL, NULL); +#endif } while (retval < 0 && errno == EINTR); if (retval <= 0) { if (0 == retval) { log_error(LOG_LEVEL_ERROR, - "Waiting on new client failed because poll(2) returned 0." + "Waiting on new client failed because select(2) returned 0." " This should not happen."); } else { log_error(LOG_LEVEL_ERROR, - "Waiting on new client failed because of problems in poll(2): " + "Waiting on new client failed because of problems in select(2): " "%s.", strerror(errno)); } return 0; } +#ifdef HAVE_POLL for (i = 0; i < MAX_LISTENING_SOCKETS && (poll_fds[i].revents == 0); i++); +#else + for (i = 0; i < MAX_LISTENING_SOCKETS && !FD_ISSET(fds[i], &selected_fds); + i++); +#endif if (i >= MAX_LISTENING_SOCKETS) { log_error(LOG_LEVEL_ERROR, - "poll(2) reported connected clients (number = %u, " + "select(2) reported connected clients (number = %u, " "descriptor boundary = %u), but none found.", retval, max_selected_socket); return 0; @@ -1288,6 +1389,19 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) } #endif +#ifndef HAVE_POLL +#ifndef _WIN32 + if (afd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_ERROR, + "Client socket number too high to use select(): %d >= %d", + afd, FD_SETSIZE); + close_socket(afd); + return 0; + } +#endif +#endif + #ifdef FEATURE_EXTERNAL_FILTERS mark_socket_for_close_on_execute(afd); #endif @@ -1468,6 +1582,7 @@ int socket_is_still_alive(jb_socket sfd) { char buf[10]; int no_data_waiting; +#ifdef HAVE_POLL int poll_result; struct pollfd poll_fd[1]; @@ -1483,6 +1598,23 @@ int socket_is_still_alive(jb_socket sfd) return FALSE; } no_data_waiting = !(poll_fd[0].revents & POLLIN); +#else + fd_set readable_fds; + struct timeval timeout; + int ret; + + memset(&timeout, '\0', sizeof(timeout)); + FD_ZERO(&readable_fds); + FD_SET(sfd, &readable_fds); + + ret = select((int)sfd+1, &readable_fds, NULL, NULL, &timeout); + if (ret < 0) + { + log_error(LOG_LEVEL_CONNECT, "select() on socket %d failed: %E", sfd); + return FALSE; + } + no_data_waiting = !FD_ISSET(sfd, &readable_fds); +#endif /* def HAVE_POLL */ return (no_data_waiting || (1 == recv(sfd, buf, 1, MSG_PEEK))); } diff --git a/jcc.c b/jcc.c index ca5943cf..8d99b1f9 100644 --- a/jcc.c +++ b/jcc.c @@ -90,6 +90,11 @@ #else #include #endif /* def __GLIBC__ */ +#else +# ifndef FD_ZERO +# include +# endif +#warning poll() appears to be unavailable. Your platform will become unsupported in the future. #endif /* HAVE_POLL */ #endif @@ -2580,7 +2585,13 @@ static void handle_established_connection(struct client_state *csp) char *hdr; char *p; int n; +#ifdef HAVE_POLL struct pollfd poll_fds[2]; +#else + fd_set rfds; + jb_socket maxfd; + struct timeval timeout; +#endif int server_body; int ms_iis5_hack = 0; unsigned long long byte_count = 0; @@ -2619,6 +2630,11 @@ static void handle_established_connection(struct client_state *csp) http = csp->http; +#ifndef HAVE_POLL + maxfd = (csp->cfd > csp->server_connection.sfd) ? + csp->cfd : csp->server_connection.sfd; +#endif + /* pass data between the client and server * until one or the other shuts down the connection. */ @@ -2632,6 +2648,22 @@ static void handle_established_connection(struct client_state *csp) for (;;) { +#ifndef HAVE_POLL + FD_ZERO(&rfds); +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (!watch_client_socket) + { + maxfd = csp->server_connection.sfd; + } + else +#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ + { + FD_SET(csp->cfd, &rfds); + } + + FD_SET(csp->server_connection.sfd, &rfds); +#endif /* ndef HAVE_POLL */ + #ifdef FEATURE_CONNECTION_KEEP_ALIVE if ((csp->flags & CSP_FLAG_CHUNKED) && !(csp->flags & CSP_FLAG_CONTENT_LENGTH_SET) @@ -2675,6 +2707,7 @@ static void handle_established_connection(struct client_state *csp) } #endif /* FEATURE_CONNECTION_KEEP_ALIVE */ +#ifdef HAVE_POLL poll_fds[0].fd = csp->cfd; #ifdef FEATURE_CONNECTION_KEEP_ALIVE if (!watch_client_socket) @@ -2695,6 +2728,11 @@ static void handle_established_connection(struct client_state *csp) poll_fds[1].fd = csp->server_connection.sfd; poll_fds[1].events = POLLIN; n = poll(poll_fds, 2, csp->config->socket_timeout * 1000); +#else + timeout.tv_sec = csp->config->socket_timeout; + timeout.tv_usec = 0; + n = select((int)maxfd + 1, &rfds, NULL, NULL, &timeout); +#endif /* def HAVE_POLL */ /*server or client not responding in timeout */ if (n == 0) @@ -2713,7 +2751,11 @@ static void handle_established_connection(struct client_state *csp) } else if (n < 0) { +#ifdef HAVE_POLL log_error(LOG_LEVEL_ERROR, "poll() failed!: %E"); +#else + log_error(LOG_LEVEL_ERROR, "select() failed!: %E"); +#endif mark_server_socket_tainted(csp); #ifdef FEATURE_HTTPS_INSPECTION close_client_and_server_ssl_connections(csp); @@ -2730,6 +2772,7 @@ static void handle_established_connection(struct client_state *csp) * XXX: Make sure the client doesn't use pipelining * behind Privoxy's back. */ +#ifdef HAVE_POLL if ((poll_fds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) != 0) { log_error(LOG_LEVEL_CONNECT, @@ -2741,6 +2784,9 @@ static void handle_established_connection(struct client_state *csp) } if (poll_fds[0].revents != 0) +#else + if (FD_ISSET(csp->cfd, &rfds)) +#endif /* def HAVE_POLL*/ { int max_bytes_to_read = (int)csp->receive_buffer_size; @@ -2751,7 +2797,7 @@ static void handle_established_connection(struct client_state *csp) { /* * If the next request is already waiting, we have - * to stop poll()ing the client socket. Otherwise + * to stop select()ing the client socket. Otherwise * we would always return right away and get nothing * else done. */ @@ -2869,7 +2915,11 @@ static void handle_established_connection(struct client_state *csp) * If `hdr' is null, then it's the header otherwise it's the body. * FIXME: Does `hdr' really mean `host'? No. */ +#ifdef HAVE_POLL if (poll_fds[1].revents != 0) +#else + if (FD_ISSET(csp->server_connection.sfd, &rfds)) +#endif /* HAVE_POLL */ { #ifdef FEATURE_CONNECTION_KEEP_ALIVE /* @@ -5299,6 +5349,17 @@ static jb_socket bind_port_helper(const char *haddr, int hport, int backlog) return JB_INVALID_SOCKET; } +#ifndef HAVE_POLL +#ifndef _WIN32 + if (bfd >= FD_SETSIZE) + { + log_error(LOG_LEVEL_FATAL, + "Bind socket number too high to use select(): %d >= %d", + bfd, FD_SETSIZE); + } +#endif +#endif + if (haddr == NULL) { log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses", diff --git a/loadcfg.c b/loadcfg.c index 9b39f54c..dbed0a38 100644 --- a/loadcfg.c +++ b/loadcfg.c @@ -1460,13 +1460,41 @@ struct configuration_spec * load_config(void) { int max_client_connections = parse_numeric_value(cmd, arg); +#if !defined(_WIN32) && !defined(HAVE_POLL) + /* + * Reject values below 1 for obvious reasons and values above + * FD_SETSIZE/2 because Privoxy needs two sockets to serve + * client connections that need forwarding. + * + * We ignore the fact that the first three file descriptors + * are usually set to /dev/null, one is used for logging + * and yet another file descriptor is required to load + * config files. + */ + if ((max_client_connections < 1) || (FD_SETSIZE/2 < max_client_connections)) + { + log_error(LOG_LEVEL_FATAL, "max-client-connections value %d" + " is invalid. Value needs to be above 1 and below %d" + " (FD_SETSIZE/2).", max_client_connections, FD_SETSIZE/2); + } +#else + /* + * The Windows libc uses FD_SETSIZE for an array used + * by select(), but has no problems with file descriptors + * above the limit as long as no more than FD_SETSIZE are + * passed to select(). + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms739169%28v=vs.85%29.aspx + * + * On platforms were we use poll() we don't have to enforce + * an upper connection limit either. + */ if (max_client_connections < 1) { log_error(LOG_LEVEL_FATAL, "max-client-connections value" " has to be a number above 1. %d is invalid.", max_client_connections); } - +#endif config->max_client_connections = max_client_connections; break; }