diff options
| author | Daniel Stenberg <daniel@haxx.se> | 2002-08-30 09:20:11 +0000 | 
|---|---|---|
| committer | Daniel Stenberg <daniel@haxx.se> | 2002-08-30 09:20:11 +0000 | 
| commit | 8aa3f143035ad982acb6e596a8653ac41a7860cc (patch) | |
| tree | e6d92edfc7883f0ffe14904dc151b4483e4d0e74 /lib | |
| parent | ac285b453e0db7c4cd327f4dc4845871ce26f7fa (diff) | |
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.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/http.c | 2 | ||||
| -rw-r--r-- | lib/url.c | 207 | ||||
| -rw-r--r-- | lib/urldata.h | 2 | 
3 files changed, 210 insertions, 1 deletions
diff --git a/lib/http.c b/lib/http.c index 66fa234ec..08a5536dc 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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 */ @@ -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; diff --git a/lib/urldata.h b/lib/urldata.h index 6acb8d816..5a93150b2 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 */  | 
