Bump copyright
[privoxy.git] / jbsockets.c
index 0517ee0..3a862f5 100644 (file)
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.123 2013/03/06 21:06:18 diem Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.140 2017/05/25 11:16:56 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -8,7 +8,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.123 2013/03/06 21:06:18 diem
  *                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-2017 the
  *                Privoxy team. http://www.privoxy.org/
  *
  *                Based on the Internet Junkbuster originally written
@@ -50,6 +50,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.123 2013/03/06 21:06:18 diem
 #ifndef STRICT
 #define STRICT
 #endif
+#include <winsock2.h>
 #include <windows.h>
 #include <sys/timeb.h>
 #include <io.h>
@@ -99,6 +100,7 @@ const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.123 2013/03/06 21:06:18 diem
 #include "jbsockets.h"
 #include "filters.h"
 #include "errlog.h"
+#include "miscutil.h"
 
 /* Mac OSX doesn't define AI_NUMERICSESRV */
 #ifndef AI_NUMERICSERV
@@ -122,6 +124,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
@@ -182,8 +211,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__) && !defined(AMIGA) && !defined(__OS2__)
    int   flags;
 #endif
@@ -232,14 +265,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)
    {
@@ -278,6 +304,7 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
          continue;
       }
 
+#ifndef HAVE_POLL
 #ifndef _WIN32
       if (fd >= FD_SETSIZE)
       {
@@ -285,16 +312,17 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
             "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 TCP_NODELAY
-      {  /* turn off TCP coalescence */
-         int mi = 1;
-         setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int));
-      }
-#endif /* def TCP_NODELAY */
+#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)
@@ -341,6 +369,12 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
       }
 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
 
+#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);
@@ -351,6 +385,7 @@ static jb_socket rfc2553_connect_to(const char *host, int portnum, struct client
       /* 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))
@@ -408,8 +443,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__) && !defined(AMIGA) && !defined(__OS2__)
    int   flags;
 #endif
@@ -471,6 +510,7 @@ 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)
    {
@@ -480,20 +520,19 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli
       close_socket(fd);
       return JB_INVALID_SOCKET;
    }
+#endif
 #endif
 
-#ifdef TCP_NODELAY
-   {  /* turn off TCP coalescence */
-      int mi = 1;
-      setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &mi, sizeof (int));
-   }
-#endif /* def TCP_NODELAY */
+   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__) */
 
@@ -529,6 +568,12 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli
    }
 #endif /* !defined(_WIN32) && !defined(__BEOS__) && !defined(AMIGA) && !defined(__OS2__) */
 
+#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);
@@ -538,6 +583,7 @@ static jb_socket no_rfc2553_connect_to(const char *host, int portnum, struct cli
 
    /* 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);
@@ -574,6 +620,14 @@ int write_socket(jb_socket fd, const char *buf, size_t len)
       return 0;
    }
 
+#ifdef FUZZ
+   if (!daemon_mode && fd <= 3)
+   {
+      log_error(LOG_LEVEL_WRITING, "Pretending to write to socket %d: %N", fd, len, buf);
+      return 0;
+   }
+#endif
+
    log_error(LOG_LEVEL_WRITING, "to socket %d: %N", fd, len, buf);
 
 #if defined(_WIN32)
@@ -675,10 +729,18 @@ int read_socket(jb_socket fd, char *buf, int len)
  *********************************************************************/
 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;
-   int n;
 
    memset(&timeout, 0, sizeof(timeout));
    timeout.tv_sec = seconds_to_wait;
@@ -692,6 +754,7 @@ int data_is_available(jb_socket fd, int seconds_to_wait)
    FD_SET(fd, &rfds);
 
    n = select(fd+1, &rfds, NULL, NULL, &timeout);
+#endif
 
    /*
     * XXX: Do we care about the different error conditions?
@@ -921,6 +984,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
@@ -990,6 +1057,7 @@ int bind_port(const char *hostnam, int portnum, jb_socket *pfd)
    {
       if (errno != EINTR)
       {
+         close_socket(fd);
          return(-1);
       }
    }
@@ -1077,22 +1145,10 @@ void get_host_information(jb_socket afd, char **ip_address, char **port,
 #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);
@@ -1118,13 +1174,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)
@@ -1213,23 +1263,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;
@@ -1242,7 +1310,11 @@ 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)
    {
@@ -1260,8 +1332,12 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
       }
       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,
@@ -1289,7 +1365,7 @@ 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;
@@ -1301,13 +1377,14 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
       struct linger linger_options;
       linger_options.l_onoff  = 1;
       linger_options.l_linger = 5;
-      if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_options, sizeof(linger_options)))
+      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 HAVE_POLL
 #ifndef _WIN32
    if (afd >= FD_SETSIZE)
    {
@@ -1318,16 +1395,17 @@ int accept_connection(struct client_state * csp, jb_socket fds[])
       return 0;
    }
 #endif
+#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(NI_MAXHOST);
-   if (NULL == csp->ip_addr_str)
-   {
-      log_error(LOG_LEVEL_ERROR,
-         "Out of memory while getting the client's IP address.");
-      return 0;
-   }
+   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,6 +1420,26 @@ 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')
+    */
+   host_addr = (csp->config->haddr[i] != NULL) ? csp->config->haddr[i] : "";
+   listen_addr_size = strlen(host_addr) + 7;
+   csp->listen_addr_str = malloc_or_die(listen_addr_size);
+   retval = snprintf(csp->listen_addr_str, listen_addr_size,
+      "%s:%d", host_addr, 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",
+         host_addr, csp->config->hport[i], listen_addr_size);
+      return 0;
+   }
+
    return 1;
 
 }
@@ -1518,6 +1616,42 @@ int socket_is_still_alive(jb_socket sfd)
 }
 
 
+#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