X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=blobdiff_plain;f=jbsockets.c;h=48f43c922b7819fb0ca428662be4b4929763b9fb;hp=76eddf4780451bc0a48117189668f2fcfa4d1daf;hb=67a4fa138becd0e2fe5adca3383e4efa03472c93;hpb=c19d6c89f0b349ec9d31d2a2853f4c08fb31b506 diff --git a/jbsockets.c b/jbsockets.c index 76eddf47..48f43c92 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -1,4 +1,4 @@ -const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.111 2011/12/10 17:26:30 fabiankeil Exp $"; +const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.134 2015/11/06 13:37:35 fabiankeil Exp $"; /********************************************************************* * * File : $Source: /cvsroot/ijbswa/current/jbsockets.c,v $ @@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.111 2011/12/10 17:26:30 fabia * OS-independent. Contains #ifdefs to make this work * on many platforms. * - * Copyright : Written by and Copyright (C) 2001-2011 the + * Copyright : Written by and Copyright (C) 2001-2016 the * Privoxy team. http://www.privoxy.org/ * * Based on the Internet Junkbuster originally written @@ -83,7 +83,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.111 2011/12/10 17:26:30 fabia #endif -#ifdef FEATURE_CONNECTION_KEEP_ALIVE #ifdef HAVE_POLL #ifdef __GLIBC__ #include @@ -91,7 +90,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.111 2011/12/10 17:26:30 fabia #include #endif /* def __GLIBC__ */ #endif /* HAVE_POLL */ -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ #include "project.h" @@ -101,6 +99,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.111 2011/12/10 17:26:30 fabia #include "jbsockets.h" #include "filters.h" #include "errlog.h" +#include "miscutil.h" /* Mac OSX doesn't define AI_NUMERICSESRV */ #ifndef AI_NUMERICSERV @@ -124,6 +123,33 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct client_state *csp); #endif +/********************************************************************* + * + * Function : set_no_delay_flag + * + * Description : Disables TCP coalescence for the given socket. + * + * Parameters : + * 1 : fd = The file descriptor to operate on + * + * Returns : void + * + *********************************************************************/ +static void set_no_delay_flag(int fd) +{ +#ifdef TCP_NODELAY + int mi = 1; + + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof(int))) + { + log_error(LOG_LEVEL_ERROR, + "Failed to disable TCP coalescence for socket %d", fd); + } +#else +#warning set_no_delay_flag() is a nop due to lack of TCP_NODELAY +#endif /* def TCP_NODELAY */ +} + /********************************************************************* * * Function : connect_to @@ -133,7 +159,7 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli * * Parameters : * 1 : host = hostname to connect to - * 2 : portnum = port to connent on (XXX: should be unsigned) + * 2 : portnum = port to connect to (XXX: should be unsigned) * 3 : csp = Current client state (buffers, headers, etc...) * * Returns : JB_INVALID_SOCKET => failure, else it is the socket @@ -234,14 +260,7 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client return(JB_INVALID_SOCKET); } - csp->http->host_ip_addr_str = malloc(NI_MAXHOST); - if (NULL == csp->http->host_ip_addr_str) - { - freeaddrinfo(result); - log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the server IP address."); - return JB_INVALID_SOCKET; - } + csp->http->host_ip_addr_str = malloc_or_die(NI_MAXHOST); for (rp = result; rp != NULL; rp = rp->ai_next) { @@ -280,12 +299,23 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client continue; } -#ifdef TCP_NODELAY - { /* turn off TCP coalescence */ - int mi = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); +#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 /* def TCP_NODELAY */ +#endif + +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + + set_no_delay_flag(fd); #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) @@ -462,18 +492,27 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli return(JB_INVALID_SOCKET); } -#ifdef TCP_NODELAY - { /* turn off TCP coalescence */ - int mi = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int)); +#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 /* def TCP_NODELAY */ +#endif + + set_no_delay_flag(fd); #if !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) if ((flags = fcntl(fd, F_GETFL, 0)) != -1) { flags |= O_NDELAY; fcntl(fd, F_SETFL, flags); +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif } #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */ @@ -703,6 +742,77 @@ void close_socket(jb_socket fd) #else close(fd); #endif +} + + +/********************************************************************* + * + * Function : drain_and_close_socket + * + * Description : Closes a TCP/IP socket after draining unread data + * + * Parameters : + * 1 : fd = file descriptor of the socket to be closed + * + * Returns : void + * + *********************************************************************/ +void drain_and_close_socket(jb_socket fd) +{ +#ifdef FEATURE_CONNECTION_KEEP_ALIVE + if (socket_is_still_alive(fd)) +#endif + { + int bytes_drained_total = 0; + int bytes_drained; + +#ifdef HAVE_SHUTDOWN +/* Apparently Windows has shutdown() but not SHUT_WR. */ +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + if (0 != shutdown(fd, SHUT_WR)) + { + log_error(LOG_LEVEL_CONNECT, "Failed to shutdown socket %d: %E", fd); + } +#endif +#define ARBITRARY_DRAIN_LIMIT 10000 + do + { + char drainage[500]; + + if (!data_is_available(fd, 0)) + { + /* + * If there is no data available right now, don't try + * to drain the socket as read_socket() could block. + */ + break; + } + + bytes_drained = read_socket(fd, drainage, sizeof(drainage)); + if (bytes_drained < 0) + { + log_error(LOG_LEVEL_CONNECT, "Failed to drain socket %d: %E", fd); + } + else if (bytes_drained > 0) + { + bytes_drained_total += bytes_drained; + if (bytes_drained_total > ARBITRARY_DRAIN_LIMIT) + { + log_error(LOG_LEVEL_CONNECT, "Giving up draining socket %d", fd); + break; + } + } + } while (bytes_drained > 0); + if (bytes_drained_total != 0) + { + log_error(LOG_LEVEL_CONNECT, + "Drained %d bytes before closing socket %d", bytes_drained_total, fd); + } + } + + close_socket(fd); } @@ -830,6 +940,10 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) #endif } +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(fd); +#endif + #ifndef _WIN32 /* * This is not needed for Win32 - in fact, it stops @@ -899,6 +1013,7 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd) { if (errno != EINTR) { + close_socket(fd); return(-1); } } @@ -943,7 +1058,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **port, struct sockaddr_in server; struct hostent *host = NULL; #endif /* HAVE_RFC2553 */ -#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) +#if defined(_WIN32) || defined(__OS2__) || defined(AMIGA) /* according to accept_connection() this fixes a warning. */ int s_length, s_length_provided; #else @@ -981,27 +1096,15 @@ void get_host_information(jb_socket afd, char **ip_address, char **port, * configured with --disable-ipv6-support. * The proper fix is to not use NI_MAXSERV in * that case. It works by accident on other platforms - * as in included unconditionally there. + * as is included unconditionally there. */ #ifndef NI_MAXSERV #define NI_MAXSERV 32 #endif - *port = malloc(NI_MAXSERV); - if (NULL == *port) - { - log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the client's port."); - return; - } + *port = malloc_or_die(NI_MAXSERV); + #ifdef HAVE_RFC2553 - *ip_address = malloc(NI_MAXHOST); - if (NULL == *ip_address) - { - log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the client's IP address."); - freez(*port); - return; - } + *ip_address = malloc_or_die(NI_MAXHOST); retval = getnameinfo((struct sockaddr *) &server, s_length, *ip_address, NI_MAXHOST, *port, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV); @@ -1027,13 +1130,7 @@ void get_host_information(jb_socket afd, char **ip_address, char **port, } #ifdef HAVE_RFC2553 - *hostname = malloc(NI_MAXHOST); - if (NULL == *hostname) - { - log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the client's hostname."); - return; - } + *hostname = malloc_or_die(NI_MAXHOST); retval = getnameinfo((struct sockaddr *) &server, s_length, *hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD); if (retval) @@ -1113,7 +1210,7 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) struct sockaddr_in client; #endif jb_socket afd; -#if defined(_WIN32) || defined(__OS2__) || defined(__APPLE_CC__) || defined(AMIGA) +#if defined(_WIN32) || defined(__OS2__) || defined(AMIGA) /* Wierdness - fix a warning. */ int c_length; #else @@ -1198,22 +1295,45 @@ int accept_connection(struct client_state * csp, jb_socket fds[]) setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &af_options, sizeof(af_options)); #endif afd = accept (fd, (struct sockaddr *) &client, &c_length); - } while (afd < 1 && errno == EINTR); + } while (afd < 0 && errno == EINTR); if (afd < 0) { return 0; } #endif - csp->cfd = afd; -#ifdef HAVE_RFC2553 - csp->ip_addr_str = malloc(NI_MAXHOST); - if (NULL == csp->ip_addr_str) +#ifdef SO_LINGER + { + struct linger linger_options; + linger_options.l_onoff = 1; + linger_options.l_linger = 5; + if (0 != setsockopt(afd, SOL_SOCKET, SO_LINGER, &linger_options, sizeof(linger_options))) + { + log_error(LOG_LEVEL_ERROR, "Setting SO_LINGER on socket %d failed.", afd); + } + } +#endif + +#ifndef _WIN32 + if (afd >= FD_SETSIZE) { log_error(LOG_LEVEL_ERROR, - "Out of memory while getting the client's IP address."); + "Client socket number too high to use select(): %d >= %d", + afd, FD_SETSIZE); + close_socket(afd); return 0; } +#endif + +#ifdef FEATURE_EXTERNAL_FILTERS + mark_socket_for_close_on_execute(afd); +#endif + + set_no_delay_flag(afd); + + csp->cfd = afd; +#ifdef HAVE_RFC2553 + csp->ip_addr_str = malloc_or_die(NI_MAXHOST); retval = getnameinfo((struct sockaddr *) &client, c_length, csp->ip_addr_str, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (!csp->ip_addr_str || retval) @@ -1342,18 +1462,13 @@ unsigned long resolve_hostname_to_ip(const char *host) log_error(LOG_LEVEL_ERROR, "hostname %s resolves to unknown address type.", host); return(INADDR_NONE); } - memcpy( - (char *) &inaddr.sin_addr, - (char *) hostp->h_addr, - sizeof(inaddr.sin_addr) - ); + memcpy((char *)&inaddr.sin_addr, (char *)hostp->h_addr, sizeof(inaddr.sin_addr)); } return(inaddr.sin_addr.s_addr); } -#ifdef FEATURE_CONNECTION_KEEP_ALIVE /********************************************************************* * * Function : socket_is_still_alive @@ -1407,9 +1522,44 @@ int socket_is_still_alive(jb_socket sfd) return (no_data_waiting || (1 == recv(sfd, buf, 1, MSG_PEEK))); } -#endif /* def FEATURE_CONNECTION_KEEP_ALIVE */ +#ifdef FEATURE_EXTERNAL_FILTERS +/********************************************************************* + * + * Function : mark_socket_for_close_on_execute + * + * Description : Marks a socket for close on execute. + * + * Used so that external filters have no direct + * access to sockets they shouldn't care about. + * + * Not implemented for all platforms. + * + * Parameters : + * 1 : fd = The socket to mark + * + * Returns : void. + * + *********************************************************************/ +void mark_socket_for_close_on_execute(jb_socket fd) +{ +#ifdef FEATURE_PTHREAD + int ret; + + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (ret == -1) + { + log_error(LOG_LEVEL_ERROR, + "fcntl(%d, F_SETFD, FD_CLOEXEC) failed", fd); + } +#else +#warning "Sockets will be visible to external filters" +#endif +} +#endif /* def FEATURE_EXTERNAL_FILTERS */ + /* Local Variables: tab-width: 3