Declare 3.0.25 'beta'
[privoxy.git] / jbsockets.c
index 983f0c1..c1a4080 100644 (file)
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.112 2011/12/31 14:43:20 fabiankeil Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.135 2016/01/16 12:33:35 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.112 2011/12/31 14:43:20 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.112 2011/12/31 14:43:20 fabia
 
 #endif
 
-#ifdef FEATURE_CONNECTION_KEEP_ALIVE
 #ifdef HAVE_POLL
 #ifdef __GLIBC__
 #include <sys/poll.h>
@@ -91,7 +90,6 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.112 2011/12/31 14:43:20 fabia
 #include <poll.h>
 #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.112 2011/12/31 14:43:20 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 <netdb.h> in included unconditionally there.
+ *      as <netdb.h> 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
@@ -1124,6 +1221,7 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
    int max_selected_socket;
    fd_set selected_fds;
    jb_socket fd;
+   size_t listen_addr_size;
 
    c_length = sizeof(client);
 
@@ -1198,22 +1296,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)
@@ -1228,6 +1349,25 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
    csp->ip_addr_long = ntohl(client.sin_addr.s_addr);
 #endif /* def HAVE_RFC2553 */
 
+   /*
+    * Save the name and port of the accepting socket for later lookup.
+    *
+    * The string needs space for strlen(...) + 7 characters:
+    * strlen(haddr[i]) + 1 (':') + 5 (port digits) + 1 ('\0')
+    */
+   listen_addr_size = strlen(csp->config->haddr[i]) + 7;
+   csp->listen_addr_str = malloc_or_die(listen_addr_size);
+   retval = snprintf(csp->listen_addr_str, listen_addr_size,
+      "%s:%d", csp->config->haddr[i], csp->config->hport[i]);
+   if ((-1 == retval) || listen_addr_size <= retval)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Server name (%s) and port number (%d) ASCII decimal representation"
+         "don't fit into %d bytes",
+         csp->config->haddr[i], csp->config->hport[i], listen_addr_size);
+      return 0;
+   }
+
    return 1;
 
 }
@@ -1342,18 +1482,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 +1542,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