Stop duplicating the plain text representation of the path regex
[privoxy.git] / gateway.c
index 2eec995..b83d10d 100644 (file)
--- a/gateway.c
+++ b/gateway.c
@@ -1,4 +1,4 @@
-const char gateway_rcs[] = "$Id: gateway.c,v 1.21 2007/07/28 12:30:03 fabiankeil Exp $";
+const char gateway_rcs[] = "$Id: gateway.c,v 1.24 2008/02/04 14:56:29 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.c,v $
@@ -34,6 +34,16 @@ const char gateway_rcs[] = "$Id: gateway.c,v 1.21 2007/07/28 12:30:03 fabiankeil
  *
  * Revisions   :
  *    $Log: gateway.c,v $
+ *    Revision 1.24  2008/02/04 14:56:29  fabiankeil
+ *    - Fix a compiler warning.
+ *    - Stop assuming that htonl(INADDR_NONE) equals INADDR_NONE.
+ *
+ *    Revision 1.23  2008/02/04 13:11:35  fabiankeil
+ *    Remember the cause of the SOCKS5 error for the CGI message.
+ *
+ *    Revision 1.22  2008/02/03 13:46:15  fabiankeil
+ *    Add SOCKS5 support. Patch #1862863 by Eric M. Hopper with minor changes.
+ *
  *    Revision 1.21  2007/07/28 12:30:03  fabiankeil
  *    Modified patch from Song Weijia (#1762559) to
  *    fix socks requests on big-endian platforms.
@@ -305,7 +315,7 @@ static jb_socket socks4_connect(const struct forward_spec * fwd,
                                 int target_port,
                                 struct client_state *csp)
 {
-   int web_server_addr;
+   unsigned int web_server_addr;
    char buf[BUFFER_SIZE];
    struct socks_op    *c = (struct socks_op    *)buf;
    struct socks_reply *s = (struct socks_reply *)buf;
@@ -345,13 +355,17 @@ static jb_socket socks4_connect(const struct forward_spec * fwd,
    switch (fwd->type)
    {
       case SOCKS_4:
-         web_server_addr = htonl(resolve_hostname_to_ip(target_host));
+         web_server_addr = resolve_hostname_to_ip(target_host);
          if (web_server_addr == INADDR_NONE)
          {
             errstr = "could not resolve target host";
             log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s %s", errstr, target_host);
             err = 1;
          }
+         else
+         {
+            web_server_addr = htonl(web_server_addr);
+         }
          break;
       case SOCKS_4A:
          web_server_addr = 0x00000001;
@@ -471,6 +485,45 @@ static jb_socket socks4_connect(const struct forward_spec * fwd,
 
 }
 
+/*********************************************************************
+ *
+ * Function    :  translate_socks5_error
+ *
+ * Description :  Translates a SOCKS errors to a string.
+ *
+ * Parameters  :
+ *          1  :  socks_error = The error code to translate.
+ *
+ * Returns     :  The string translation.
+ *
+ *********************************************************************/
+static const char *translate_socks5_error(int socks_error)
+{
+   switch (socks_error)
+   {
+      /* XXX: these should be more descriptive */
+      case SOCKS5_REQUEST_FAILED:
+         return "SOCKS5 request failed";
+      case SOCKS5_REQUEST_DENIED:
+         return "SOCKS5 request denied";
+      case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
+         return "SOCKS5 network unreachable";
+      case SOCKS5_REQUEST_HOST_UNREACHABLE:
+         return "SOCKS5 host unreachable";
+      case SOCKS5_REQUEST_CONNECTION_REFUSEDD:
+         return "SOCKS5 connection refused";
+      case SOCKS5_REQUEST_TTL_EXPIRED:
+         return "SOCKS5 TTL expired";
+      case SOCKS5_REQUEST_PROTOCOL_ERROR:
+         return "SOCKS5 client protocol error";
+      case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
+         return "SOCKS5 domain names unsupported";
+      case SOCKS5_REQUEST_GRANTED:
+         return "everything's peachy";
+      default:
+         return "SOCKS5 negotiation protocol error";
+   }
+}
 
 /*********************************************************************
  *
@@ -497,42 +550,53 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
                                 struct client_state *csp)
 {
    int err = 0;
-   char cbuf[BUFFER_SIZE];
-   char sbuf[BUFFER_SIZE];
+   char cbuf[300];
+   char sbuf[30];
    size_t client_pos = 0;
    int server_size = 0;
    size_t hostlen = 0;
    jb_socket sfd;
+   const char *errstr = NULL;
 
+   assert(fwd->gateway_host);
    if ((fwd->gateway_host == NULL) || (*fwd->gateway_host == '\0'))
    {
-      log_error(LOG_LEVEL_CONNECT, "socks5_connect: NULL gateway host specified");
+      errstr = "NULL gateway host specified";
       err = 1;
    }
 
    if (fwd->gateway_port <= 0)
    {
-      log_error(LOG_LEVEL_CONNECT, "socks5_connect: invalid gateway port specified");
+      /*
+       * XXX: currently this can't happen because in
+       * case of invalid gateway ports we use the defaults.
+       * Of course we really shouldn't do that.
+       */
+      errstr = "invalid gateway port specified";
       err = 1;
    }
 
    hostlen = strlen(target_host);
    if (hostlen > 255)
    {
-      log_error(LOG_LEVEL_CONNECT, "socks5_connect: target host name is longer than 255 characters.");
+      errstr = "target host name is longer than 255 characters";
       err = 1;
    }
 
    if (fwd->type != SOCKS_5)
    {
       /* Should never get here */
-      log_error(LOG_LEVEL_FATAL, "SOCKS5 impossible internal error - bad SOCKS type.");
+      log_error(LOG_LEVEL_FATAL,
+         "SOCKS5 impossible internal error - bad SOCKS type");
       err = 1;
    }
 
    if (err)
    {
       errno = EINVAL;
+      assert(errstr != NULL);
+      log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
+      csp->error_message = strdup(errstr);
       return(JB_INVALID_SOCKET);
    }
 
@@ -541,6 +605,9 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
 
    if (sfd == JB_INVALID_SOCKET)
    {
+      errstr = "socks5 server unreachable";
+      log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
+      csp->error_message = strdup(errstr);
       return(JB_INVALID_SOCKET);
    }
 
@@ -551,37 +618,42 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
 
    if (write_socket(sfd, cbuf, client_pos))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation write failed...");
+      errstr = "SOCKS5 negotiation write failed";
+      csp->error_message = strdup(errstr);
+      log_error(LOG_LEVEL_CONNECT, "%s", errstr);
       close_socket(sfd);
       return(JB_INVALID_SOCKET);
    }
 
    if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation read failed...");
+      errstr = "SOCKS5 negotiation read failed";
       err = 1;
    }
 
    if (!err && (sbuf[0] != '\x05'))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol version error");
+      errstr = "SOCKS5 negotiation protocol version error";
       err = 1;
    }
 
    if (!err && (sbuf[1] == '\xff'))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 authentication required");
+      errstr = "SOCKS5 authentication required";
       err = 1;
    }
 
    if (!err && (sbuf[1] != '\x00'))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error");
+      errstr = "SOCKS5 negotiation protocol error";
       err = 1;
    }
 
    if (err)
    {
+      assert(errstr != NULL);
+      log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
+      csp->error_message = strdup(errstr);
       close_socket(sfd);
       errno = EINVAL;
       return(JB_INVALID_SOCKET);
@@ -593,14 +665,18 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
    cbuf[client_pos++] = '\x00'; /* Reserved, must be 0x00 */
    cbuf[client_pos++] = '\x03'; /* Address is domain name */
    cbuf[client_pos++] = (char)(hostlen & 0xffu);
-   strncpy(cbuf + client_pos, target_host, 0xffu);
+   assert(sizeof(cbuf) - client_pos > 255);
+   /* Using strncpy because we really want the nul byte padding. */
+   strncpy(cbuf + client_pos, target_host, sizeof(cbuf) - client_pos);
    client_pos += (hostlen & 0xffu);
    cbuf[client_pos++] = (char)((target_port >> 8) & 0xffu);
    cbuf[client_pos++] = (char)((target_port     ) & 0xffu);
 
    if (write_socket(sfd, cbuf, client_pos))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation write failed...");
+      errstr = "SOCKS5 negotiation read failed";
+      csp->error_message = strdup(errstr);
+      log_error(LOG_LEVEL_CONNECT, "%s", errstr);
       close_socket(sfd);
       errno = EINVAL;
       return(JB_INVALID_SOCKET);
@@ -609,63 +685,47 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
    server_size = read_socket(sfd, sbuf, sizeof(sbuf));
    if (server_size < 3)
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation read failed...");
+      errstr = "SOCKS5 negotiation read failed";
       err = 1;
    }
+   else if (server_size > 20)
+   {
+      /* This is somewhat unexpected but doesn't realy matter. */
+      log_error(LOG_LEVEL_CONNECT, "socks5_connect: read %d bytes "
+         "from socks server. Would have accepted up to %d.",
+         server_size, sizeof(sbuf));
+   }
 
    if (!err && (sbuf[0] != '\x05'))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol version error");
+      errstr = "SOCKS5 negotiation protocol version error";
       err = 1;
    }
 
    if (!err && (sbuf[2] != '\x00'))
    {
-      log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error");
+      errstr = "SOCKS5 negotiation protocol error";
       err = 1;
    }
 
    if (!err)
    {
-      switch (sbuf[1])
+      if (sbuf[1] == SOCKS5_REQUEST_GRANTED)
       {
-         case SOCKS5_REQUEST_GRANTED:
-            return(sfd);
-            break;
-         case SOCKS5_REQUEST_FAILED:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request failed");
-            break;
-         case SOCKS5_REQUEST_DENIED:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request denied");
-            break;
-         case SOCKS5_REQUEST_NETWORK_UNREACHABLE:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - network unreachable");
-            break;
-         case SOCKS5_REQUEST_HOST_UNREACHABLE:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - host unreachable");
-            break;
-         case SOCKS5_REQUEST_CONNECTION_REFUSEDD:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - connection refused");
-            break;
-         case SOCKS5_REQUEST_TTL_EXPIRED:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - TTL expired");
-            break;
-         case SOCKS5_REQUEST_PROTOCOL_ERROR:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - client protocol error");
-            break;
-         case SOCKS5_REQUEST_BAD_ADDRESS_TYPE:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 request - domain names unsupported");
-            break;
-         default:
-            log_error(LOG_LEVEL_CONNECT, "SOCKS5 negotiation protocol error");
-            break;
+         return(sfd);
       }
+      errstr = translate_socks5_error(sbuf[1]);
       err = 1;
    }
 
+   assert(errstr != NULL);
+   csp->error_message = strdup(errstr);
+   log_error(LOG_LEVEL_CONNECT, "socks5_connect: %s", errstr);
    close_socket(sfd);
    errno = EINVAL;
+
    return(JB_INVALID_SOCKET);
+
 }
 
 /*