Allow to bind to multiple separate addresses.
[privoxy.git] / jcc.c
diff --git a/jcc.c b/jcc.c
index ab29cfa..271b1d1 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.344 2011/03/27 13:58:09 fabiankeil Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.357 2011/07/17 13:31:02 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -151,7 +151,9 @@ static void serve(struct client_state *csp);
 static void usage(const char *myname);
 #endif
 static void initialize_mutexes(void);
-static jb_socket bind_port_helper(struct configuration_spec *config);
+static jb_socket bind_port_helper(const char *haddr, int hport);
+static void bind_ports_helper(struct configuration_spec *config, jb_socket sockets[]);
+static void close_ports_helper(jb_socket sockets[]);
 static void listen_loop(void);
 
 #ifdef AMIGA
@@ -1599,8 +1601,8 @@ static void chat(struct client_state *csp)
          csp->action->string[ACTION_STRING_LIMIT_CONNECT];
       assert(NULL != acceptable_connect_ports);
       log_error(LOG_LEVEL_INFO, "Request from %s marked for blocking. "
-         "limit-connect{%s} doesn't allow CONNECT requests to port %d.",
-         csp->ip_addr_str, acceptable_connect_ports, csp->http->port);
+         "limit-connect{%s} doesn't allow CONNECT requests to %s",
+         csp->ip_addr_str, acceptable_connect_ports, csp->http->hostport);
       csp->action->flags |= ACTION_BLOCK;
       http->ssl = 0;
    }
@@ -2061,6 +2063,20 @@ static void chat(struct client_state *csp)
                   {
                      csp->content_length = (size_t)(csp->iob->eod - csp->iob->cur);
                   }
+#ifdef FEATURE_COMPRESSION
+                  else if ((csp->flags & CSP_FLAG_CLIENT_SUPPORTS_DEFLATE)
+                     && (csp->content_length > LOWER_LENGTH_LIMIT_FOR_COMPRESSION))
+                  {
+                     char *compressed_content = compress_buffer(p,
+                        (size_t *)&csp->content_length, csp->config->compression_level);
+                     if (compressed_content != NULL)
+                     {
+                        freez(p);
+                        p = compressed_content;
+                        csp->flags |= CSP_FLAG_BUFFERED_CONTENT_DEFLATED;
+                     }
+                  }
+#endif
 
                   if (JB_ERR_OK != update_server_headers(csp))
                   {
@@ -2863,7 +2879,6 @@ int main(int argc, char **argv)
 #ifdef unix
    struct passwd *pw = NULL;
    struct group *grp = NULL;
-   char *p;
    int do_chroot = 0;
    char *pre_chroot_nslookup_to_load_resolver = NULL;
 #endif
@@ -2902,7 +2917,7 @@ int main(int argc, char **argv)
             pName++;
          exit( (install_service(pName)) ? 0 : 1 );
       }
-      else if (strncmp(argv[argc_pos], "--uninstall", 11) == 0)
+      else if (strncmp(argv[argc_pos], "--uninstall", 11) == 0)
       {
          const char *pName = argv[argc_pos] + 11;
          if (*pName == ':')
@@ -2948,23 +2963,39 @@ int main(int argc, char **argv)
 
       else if (strcmp(argv[argc_pos], "--user" ) == 0)
       {
+         char *user_arg;
+         char *group_name;
+
          if (++argc_pos == argc) usage(argv[argc_pos]);
 
-         if ((NULL != (p = strchr(argv[argc_pos], '.'))) && *(p + 1) != '0')
+         user_arg = strdup(argv[argc_pos]);
+         if (NULL == user_arg)
+         {
+            log_error(LOG_LEVEL_FATAL,
+               "Out of memory splitting --user argument '%s'.", argv[argc_pos]);
+         }
+         group_name = strchr(user_arg, '.');
+         if (NULL != group_name)
          {
-            *p++ = '\0';
-            if (NULL == (grp = getgrnam(p)))
+            /* Nul-terminate the user name */
+            *group_name = '\0';
+
+            /* Skip the former delimiter to actually reach the group name */
+            group_name++;
+
+            grp = getgrnam(group_name);
+            if (NULL == grp)
             {
-               log_error(LOG_LEVEL_FATAL, "Group %s not found.", p);
+               log_error(LOG_LEVEL_FATAL, "Group '%s' not found.", group_name);
             }
          }
-
-         if (NULL == (pw = getpwnam(argv[argc_pos])))
+         pw = getpwnam(user_arg);
+         if (NULL == pw)
          {
-            log_error(LOG_LEVEL_FATAL, "User %s not found.", argv[argc_pos]);
+            log_error(LOG_LEVEL_FATAL, "User '%s' not found.", user_arg);
          }
 
-         if (p != NULL) *--p = '\0';
+         freez(user_arg);
       }
 
       else if (strcmp(argv[argc_pos], "--pre-chroot-nslookup" ) == 0)
@@ -3296,29 +3327,30 @@ int main(int argc, char **argv)
  *                on failure.
  *
  * Parameters  :
- *          1  :  config = Privoxy configuration.  Specifies port
- *                         to bind to.
+ *          1  :  haddr = Host addres to bind to. Use NULL to bind to
+ *                        INADDR_ANY.
+ *          2  :  hport = Specifies port to bind to.
  *
  * Returns     :  Port that was opened.
  *
  *********************************************************************/
-static jb_socket bind_port_helper(struct configuration_spec * config)
+static jb_socket bind_port_helper(const char *haddr, int hport)
 {
    int result;
    jb_socket bfd;
 
-   if (config->haddr == NULL)
+   if (haddr == NULL)
    {
       log_error(LOG_LEVEL_INFO, "Listening on port %d on all IP addresses",
-                config->hport);
+                hport);
    }
    else
    {
       log_error(LOG_LEVEL_INFO, "Listening on port %d on IP address %s",
-                config->hport, config->haddr);
+                hport, haddr);
    }
 
-   result = bind_port(config->haddr, config->hport, &bfd);
+   result = bind_port(haddr, hport, &bfd);
 
    if (result < 0)
    {
@@ -3328,29 +3360,96 @@ static jb_socket bind_port_helper(struct configuration_spec * config)
             log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: "
                "There may be another Privoxy or some other "
                "proxy running on port %d",
-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY",
-                      config->hport, config->hport);
+               (NULL != haddr) ? haddr : "INADDR_ANY", hport, hport);
 
          case -2 :
             log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: " 
                "The hostname is not resolvable",
-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
+               (NULL != haddr) ? haddr : "INADDR_ANY", hport);
 
          default :
             log_error(LOG_LEVEL_FATAL, "can't bind to %s:%d: %E",
-               (NULL != config->haddr) ? config->haddr : "INADDR_ANY", config->hport);
+               (NULL != haddr) ? haddr : "INADDR_ANY", hport);
       }
 
       /* shouldn't get here */
       return JB_INVALID_SOCKET;
    }
 
-   config->need_bind = 0;
-
    return bfd;
 }
 
 
+/*********************************************************************
+ *
+ * Function    :  bind_ports_helper
+ *
+ * Description :  Bind the listen ports.  Handles logging, and aborts
+ *                on failure.
+ *
+ * Parameters  :
+ *          1  :  config = Privoxy configuration.  Specifies ports
+ *                         to bind to.
+ *          2  :  sockets = Preallocated array of opened sockets
+ *                          corresponding to specification in config.
+ *                          All non-opened sockets will be set to
+ *                          JB_INVALID_SOCKET.
+ *
+ * Returns     :  Nothing. Inspect sockets argument.
+ *
+ *********************************************************************/
+static void bind_ports_helper(struct configuration_spec * config,
+                              jb_socket sockets[])
+{
+   int i;
+
+   config->need_bind = 1;
+
+   for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
+   {
+      sockets[i] = JB_INVALID_SOCKET;
+
+      if (config->hport[i])
+      {
+         sockets[i] = bind_port_helper(config->haddr[i], config->hport[i]);
+         if (JB_INVALID_SOCKET != sockets[i])
+         {
+            config->need_bind = 0;
+         }
+      }
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  close_ports_helper
+ *
+ * Description :  Close listenings ports.
+ *
+ * Parameters  :
+ *          1  :  sockets = Array of opened and non-opened sockets to
+ *                          close. All sockets will be set to
+ *                          JB_INVALID_SOCKET.
+ *
+ * Returns     :  Nothing.
+ *
+ *********************************************************************/
+static void close_ports_helper(jb_socket sockets[])
+{
+   int i;
+
+   for (i = 0; i < MAX_LISTENING_SOCKETS; i++)
+   {
+      if (JB_INVALID_SOCKET != sockets[i])
+      {
+         close_socket(sockets[i]);
+      }
+      sockets[i] = JB_INVALID_SOCKET;
+   }
+}
+
+
 #ifdef _WIN32
 /* Without this simple workaround we get this compiler warning from _beginthread
  *     warning C4028: formal parameter 1 different from declaration
@@ -3377,7 +3476,7 @@ static void listen_loop(void)
 {
    struct client_states *csp_list = NULL;
    struct client_state *csp = NULL;
-   jb_socket bfd;
+   jb_socket bfds[MAX_LISTENING_SOCKETS];
    struct configuration_spec *config;
    unsigned int active_threads = 0;
 
@@ -3391,7 +3490,7 @@ static void listen_loop(void)
    initialize_reusable_connections();
 #endif /* def FEATURE_CONNECTION_SHARING */
 
-   bfd = bind_port_helper(config);
+   bind_ports_helper(config, bfds);
 
 #ifdef FEATURE_GRACEFUL_TERMINATION
    while (!g_terminate)
@@ -3436,7 +3535,7 @@ static void listen_loop(void)
 
       log_error(LOG_LEVEL_CONNECT, "Listening for new connections ... ");
 
-      if (!accept_connection(csp, bfd))
+      if (!accept_connection(csp, bfds))
       {
          log_error(LOG_LEVEL_CONNECT, "accept failed: %E");
 
@@ -3461,7 +3560,7 @@ static void listen_loop(void)
 
       csp->config = config = load_config();
 
-      if ( config->need_bind )
+      if (config->need_bind)
       {
          /*
           * Since we were listening to the "old port", we will not see
@@ -3476,9 +3575,9 @@ static void listen_loop(void)
           * that this will hurt people's feelings.
           */
 
-         close_socket(bfd);
+         close_ports_helper(bfds);
 
-         bfd = bind_port_helper(config);
+         bind_ports_helper(config, bfds);
       }
 
 #ifdef FEATURE_TOGGLE
@@ -3497,7 +3596,8 @@ static void listen_loop(void)
 #ifdef FEATURE_ACL
       if (block_acl(NULL,csp))
       {
-         log_error(LOG_LEVEL_CONNECT, "Connection from %s dropped due to ACL", csp->ip_addr_str);
+         log_error(LOG_LEVEL_CONNECT,
+            "Connection from %s on socket %d dropped due to ACL", csp->ip_addr_str, csp->cfd);
          close_socket(csp->cfd);
          freez(csp->ip_addr_str);
          freez(csp_list);