aboutsummaryrefslogtreecommitdiff
path: root/lib/url.c
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2002-08-30 09:20:11 +0000
committerDaniel Stenberg <daniel@haxx.se>2002-08-30 09:20:11 +0000
commit8aa3f143035ad982acb6e596a8653ac41a7860cc (patch)
treee6d92edfc7883f0ffe14904dc151b4483e4d0e74 /lib/url.c
parentac285b453e0db7c4cd327f4dc4845871ce26f7fa (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/url.c')
-rw-r--r--lib/url.c207
1 files changed, 207 insertions, 0 deletions
diff --git a/lib/url.c b/lib/url.c
index 43a7e400b..c9aa3042a 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -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;