Commit 8aa3f143 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

SOCKS5 support added (contributed by a still unnamed person). Not properly

working for "IPv6 enabled" libcurls yet, but should be pretty easy for
someone to adjust.
parent ac285b45
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -202,6 +202,12 @@ typedef enum {
  CURL_LAST /* never use! */
} CURLcode;

typedef enum {
  CURLPROXY_HTTP = 0,
  CURLPROXY_SOCKS4 = 4,
  CURLPROXY_SOCKS5 = 5
} curl_proxytype;

/* this was the error code 50 in 7.7.3 and a few earlier versions, this
   is no longer used by libcurl but is instead #defined here only to not
   make programs break */
@@ -576,6 +582,10 @@ typedef enum {
  /* Explicitly allow insecure SSL connects */
  CINIT(SSL_INSECURE, LONG, 101),

  /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
     CURLPROXY_SOCKS4 and CURLPROXY_SOCKS5. */
  CINIT(PROXYTYPE, LONG, 102), 

  CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

+1 −1
Original line number Diff line number Diff line
@@ -420,7 +420,7 @@ CURLcode Curl_http_connect(struct connectdata *conn)
   * has occured, can we start talking SSL
   */

  if(data->change.proxy &&
  if(data->change.proxy && (data->set.proxytype == CURLPROXY_HTTP) &&
     ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {

    /* either HTTPS over proxy, OR explicitly asked for a tunnel */
+207 −0
Original line number Diff line number Diff line
@@ -282,6 +282,8 @@ CURLcode Curl_open(struct SessionHandle **curl)

  data->set.proxyport = 1080;
  
  data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */

  /* create an array with connection data struct pointers */
  data->state.numconnects = 5; /* hard-coded right now */
  data->state.connects = (struct connectdata **)
@@ -1053,6 +1055,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
    data->set.ssl.allow_insecure = va_arg(param, long)?TRUE:FALSE;
    break;

  case CURLOPT_PROXYTYPE:
    /*
     * Set proxy type. HTTP/SOCKS4/SOCKS5
     */
    data->set.proxytype = va_arg(param, long);
    break;

  default:
    /* unknown tag and its companion, just ignore: */
    return CURLE_FAILED_INIT; /* correct this */
@@ -1327,6 +1336,189 @@ ConnectionStore(struct SessionHandle *data,
  return i;
}

/*
 * This function logs in to a SOCKS5 proxy and sends the specifies the final
 * desitination server.
 */
static int handleSock5Proxy(
    const char *proxy_name,
    const char *proxy_password,
    struct connectdata *conn,
    int sock)
{
  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
  int actualread;
  int written;
  CURLcode result;

  Curl_nonblock(sock, FALSE);

  socksreq[0] = 5; /* version */
  socksreq[1] = (char)(proxy_name[0] ? 2 : 1); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 2; /* username/password */

  result = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
                      &written);
  if ((result != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
    failf(conn->data, "Unable to send initial SOCKS5 request.");
    return 1;
  }

  result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
  if ((result != CURLE_OK) || (actualread != 2)) {
    failf(conn->data, "Unable to receive initial SOCKS5 response.");
    return 1;
  }

  if (socksreq[0] != 5) {
    failf(conn->data, "Received invalid version in initial SOCKS5 response.");
    return 1;
  }
  if (socksreq[1] == 0) {
    /* Nothing to do, no authentication needed */
    ;
  }
  else if (socksreq[1] == 2) {
    /* Needs user name and password */
    int userlen, pwlen, len;

    userlen = strlen(proxy_name);
    pwlen = strlen(proxy_password);

    /*   username/password request looks like
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    len = 0;
    socksreq[len++] = 1;    /* username/pw subnegotiation version */
    socksreq[len++] = (char) userlen;
    memcpy(socksreq + len, proxy_name, (int) userlen);
    len += userlen;
    socksreq[len++] = (char) pwlen;
    memcpy(socksreq + len, proxy_password, (int) pwlen);
    len += pwlen;

    result = Curl_write(conn, sock, (char *)socksreq, len, &written);
    if ((result != CURLE_OK) || (len != written)) {
      failf(conn->data, "Failed to send SOCKS5 sub-negotiation request.");
      return 1;
    }

    result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
    if ((result != CURLE_OK) || (actualread != 2)) {
      failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response.");
      return 1;
    }

    if ((socksreq[0] != 5) || /* version */
        (socksreq[1] != 0)) { /* status */
      failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      return 1;
    }

    /* Everything is good so far, user was authenticated! */
  }
  else {
    /* error */
    if (socksreq[1] == 1) {
      failf(conn->data,
            "SOCKS5 GSSAPI per-message authentication is not supported.");
      return 1;
    }
    else if (socksreq[1] == 255) {
      if (proxy_name[0] == 0) {
        failf(conn->data,
              "No authentication method was acceptable. (It is quite likely"
              " that the SOCKS5 server wanted a username/password, since none"
              " was supplied to the server on this connection.)");
      }
      else {            
        failf(conn->data, "No authentication method was acceptable.");
      }
      return 1;
    }
    else {
      failf(conn->data,
            "Undocumented SOCKS5 mode attempted to be used by server.");
      return 1;
    }
  }

  /* Authentication is complete, now specify destination to the proxy */
  socksreq[0] = 5; /* version (SOCKS5) */
  socksreq[1] = 1; /* connect */
  socksreq[2] = 0; /* must be zero */
  socksreq[3] = 1; /* IPv4 = 1 */
    
  {
    Curl_addrinfo *hp;
    hp = Curl_resolv(conn->data, conn->hostname, conn->remote_port);
    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
#ifndef ENABLE_IPV6
    if (hp && hp->h_addr_list[0]) {
      socksreq[4] = ((char*)hp->h_addr_list[0])[0];
      socksreq[5] = ((char*)hp->h_addr_list[0])[1];
      socksreq[6] = ((char*)hp->h_addr_list[0])[2];
      socksreq[7] = ((char*)hp->h_addr_list[0])[3];
    }
    else {
      failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.",
            conn->hostname);
      return 1;
    }
#else
    failf(conn->data,
          "%s:%d has an internal error an needs to be fixed to work",
          __FILE__, __LINE__);
    return 1;
#endif
  }

  *((unsigned short*)&socksreq[8]) = htons(conn->remote_port);

  {
    const int packetsize = 10;

    result = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
    if ((result != CURLE_OK) || (written != packetsize)) {
      failf(conn->data, "Failed to send SOCKS5 connect request.");
      return 1;
    }

    result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
    if ((result != CURLE_OK) || (actualread != packetsize)) {
      failf(conn->data, "Failed to receive SOCKS5 connect request ack.");
      return 1;
    }

    if (socksreq[0] != 5) { /* version */
      failf(conn->data,
            "SOCKS5 reply has wrong version, version should be 5.");
      return 1;
    }
    if (socksreq[1] != 0) { /* Anything besides 0 is an error */
        failf(conn->data,
              "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
              (unsigned char)socksreq[4], (unsigned char)socksreq[5],
              (unsigned char)socksreq[6], (unsigned char)socksreq[7],
              (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
              socksreq[1]);
        return 1;
    }
  }

  Curl_nonblock(sock, TRUE);
  return 0; /* Proxy was successful! */
}

static CURLcode ConnectPlease(struct connectdata *conn,
                              Curl_addrinfo *hostaddr,
                              bool *connected)
@@ -1356,6 +1548,21 @@ static CURLcode ConnectPlease(struct connectdata *conn,
    conn->serv_addr.sin_family = hostaddr->h_addrtype;
    conn->serv_addr.sin_port = htons((unsigned short)conn->port);
#endif

    if (conn->data->set.proxytype == CURLPROXY_SOCKS5) {
      return handleSock5Proxy(conn->data->state.proxyuser,
                              conn->data->state.proxypasswd,
                              conn,
                              conn->firstsocket) ?
        CURLE_COULDNT_CONNECT : CURLE_OK;
    }
    else if (conn->data->set.proxytype == CURLPROXY_HTTP) {
      /* do nothing here. handled later. */
    }
    else {
      failf(conn->data, "unknown proxytype option given");
      return CURLE_COULDNT_CONNECT; 
    }
  }

  return result;
+2 −0
Original line number Diff line number Diff line
@@ -645,6 +645,8 @@ struct UserDefined {
  char *krb4_level; /* what security level */
  struct ssl_config_data ssl;  /* user defined SSL stuff */

  curl_proxytype proxytype; /* what kind of proxy that is in use */

  int dns_cache_timeout; /* DNS cache timeout */
  long buffer_size;      /* size of receive buffer to use */
  
+1 −1

File changed.

Contains only whitespace changes.