Skip to content

Commit 8aa3f14

Browse files
committed
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.
1 parent ac285b4 commit 8aa3f14

File tree

5 files changed

+221
-2
lines changed

5 files changed

+221
-2
lines changed

include/curl/curl.h

+10
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ typedef enum {
202202
CURL_LAST /* never use! */
203203
} CURLcode;
204204

205+
typedef enum {
206+
CURLPROXY_HTTP = 0,
207+
CURLPROXY_SOCKS4 = 4,
208+
CURLPROXY_SOCKS5 = 5
209+
} curl_proxytype;
210+
205211
/* this was the error code 50 in 7.7.3 and a few earlier versions, this
206212
is no longer used by libcurl but is instead #defined here only to not
207213
make programs break */
@@ -576,6 +582,10 @@ typedef enum {
576582
/* Explicitly allow insecure SSL connects */
577583
CINIT(SSL_INSECURE, LONG, 101),
578584

585+
/* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
586+
CURLPROXY_SOCKS4 and CURLPROXY_SOCKS5. */
587+
CINIT(PROXYTYPE, LONG, 102),
588+
579589
CURLOPT_LASTENTRY /* the last unused */
580590
} CURLoption;
581591

lib/http.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ CURLcode Curl_http_connect(struct connectdata *conn)
420420
* has occured, can we start talking SSL
421421
*/
422422

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

426426
/* either HTTPS over proxy, OR explicitly asked for a tunnel */

lib/url.c

+207
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ CURLcode Curl_open(struct SessionHandle **curl)
282282

283283
data->set.proxyport = 1080;
284284

285+
data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
286+
285287
/* create an array with connection data struct pointers */
286288
data->state.numconnects = 5; /* hard-coded right now */
287289
data->state.connects = (struct connectdata **)
@@ -1053,6 +1055,13 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
10531055
data->set.ssl.allow_insecure = va_arg(param, long)?TRUE:FALSE;
10541056
break;
10551057

1058+
case CURLOPT_PROXYTYPE:
1059+
/*
1060+
* Set proxy type. HTTP/SOCKS4/SOCKS5
1061+
*/
1062+
data->set.proxytype = va_arg(param, long);
1063+
break;
1064+
10561065
default:
10571066
/* unknown tag and its companion, just ignore: */
10581067
return CURLE_FAILED_INIT; /* correct this */
@@ -1327,6 +1336,189 @@ ConnectionStore(struct SessionHandle *data,
13271336
return i;
13281337
}
13291338

1339+
/*
1340+
* This function logs in to a SOCKS5 proxy and sends the specifies the final
1341+
* desitination server.
1342+
*/
1343+
static int handleSock5Proxy(
1344+
const char *proxy_name,
1345+
const char *proxy_password,
1346+
struct connectdata *conn,
1347+
int sock)
1348+
{
1349+
unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
1350+
int actualread;
1351+
int written;
1352+
CURLcode result;
1353+
1354+
Curl_nonblock(sock, FALSE);
1355+
1356+
socksreq[0] = 5; /* version */
1357+
socksreq[1] = (char)(proxy_name[0] ? 2 : 1); /* number of methods (below) */
1358+
socksreq[2] = 0; /* no authentication */
1359+
socksreq[3] = 2; /* username/password */
1360+
1361+
result = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
1362+
&written);
1363+
if ((result != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
1364+
failf(conn->data, "Unable to send initial SOCKS5 request.");
1365+
return 1;
1366+
}
1367+
1368+
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
1369+
if ((result != CURLE_OK) || (actualread != 2)) {
1370+
failf(conn->data, "Unable to receive initial SOCKS5 response.");
1371+
return 1;
1372+
}
1373+
1374+
if (socksreq[0] != 5) {
1375+
failf(conn->data, "Received invalid version in initial SOCKS5 response.");
1376+
return 1;
1377+
}
1378+
if (socksreq[1] == 0) {
1379+
/* Nothing to do, no authentication needed */
1380+
;
1381+
}
1382+
else if (socksreq[1] == 2) {
1383+
/* Needs user name and password */
1384+
int userlen, pwlen, len;
1385+
1386+
userlen = strlen(proxy_name);
1387+
pwlen = strlen(proxy_password);
1388+
1389+
/* username/password request looks like
1390+
* +----+------+----------+------+----------+
1391+
* |VER | ULEN | UNAME | PLEN | PASSWD |
1392+
* +----+------+----------+------+----------+
1393+
* | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
1394+
* +----+------+----------+------+----------+
1395+
*/
1396+
len = 0;
1397+
socksreq[len++] = 1; /* username/pw subnegotiation version */
1398+
socksreq[len++] = (char) userlen;
1399+
memcpy(socksreq + len, proxy_name, (int) userlen);
1400+
len += userlen;
1401+
socksreq[len++] = (char) pwlen;
1402+
memcpy(socksreq + len, proxy_password, (int) pwlen);
1403+
len += pwlen;
1404+
1405+
result = Curl_write(conn, sock, (char *)socksreq, len, &written);
1406+
if ((result != CURLE_OK) || (len != written)) {
1407+
failf(conn->data, "Failed to send SOCKS5 sub-negotiation request.");
1408+
return 1;
1409+
}
1410+
1411+
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
1412+
if ((result != CURLE_OK) || (actualread != 2)) {
1413+
failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response.");
1414+
return 1;
1415+
}
1416+
1417+
if ((socksreq[0] != 5) || /* version */
1418+
(socksreq[1] != 0)) { /* status */
1419+
failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).",
1420+
socksreq[0], socksreq[1]);
1421+
return 1;
1422+
}
1423+
1424+
/* Everything is good so far, user was authenticated! */
1425+
}
1426+
else {
1427+
/* error */
1428+
if (socksreq[1] == 1) {
1429+
failf(conn->data,
1430+
"SOCKS5 GSSAPI per-message authentication is not supported.");
1431+
return 1;
1432+
}
1433+
else if (socksreq[1] == 255) {
1434+
if (proxy_name[0] == 0) {
1435+
failf(conn->data,
1436+
"No authentication method was acceptable. (It is quite likely"
1437+
" that the SOCKS5 server wanted a username/password, since none"
1438+
" was supplied to the server on this connection.)");
1439+
}
1440+
else {
1441+
failf(conn->data, "No authentication method was acceptable.");
1442+
}
1443+
return 1;
1444+
}
1445+
else {
1446+
failf(conn->data,
1447+
"Undocumented SOCKS5 mode attempted to be used by server.");
1448+
return 1;
1449+
}
1450+
}
1451+
1452+
/* Authentication is complete, now specify destination to the proxy */
1453+
socksreq[0] = 5; /* version (SOCKS5) */
1454+
socksreq[1] = 1; /* connect */
1455+
socksreq[2] = 0; /* must be zero */
1456+
socksreq[3] = 1; /* IPv4 = 1 */
1457+
1458+
{
1459+
Curl_addrinfo *hp;
1460+
hp = Curl_resolv(conn->data, conn->hostname, conn->remote_port);
1461+
/*
1462+
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
1463+
* returns a Curl_addrinfo pointer that may not always look the same.
1464+
*/
1465+
#ifndef ENABLE_IPV6
1466+
if (hp && hp->h_addr_list[0]) {
1467+
socksreq[4] = ((char*)hp->h_addr_list[0])[0];
1468+
socksreq[5] = ((char*)hp->h_addr_list[0])[1];
1469+
socksreq[6] = ((char*)hp->h_addr_list[0])[2];
1470+
socksreq[7] = ((char*)hp->h_addr_list[0])[3];
1471+
}
1472+
else {
1473+
failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.",
1474+
conn->hostname);
1475+
return 1;
1476+
}
1477+
#else
1478+
failf(conn->data,
1479+
"%s:%d has an internal error an needs to be fixed to work",
1480+
__FILE__, __LINE__);
1481+
return 1;
1482+
#endif
1483+
}
1484+
1485+
*((unsigned short*)&socksreq[8]) = htons(conn->remote_port);
1486+
1487+
{
1488+
const int packetsize = 10;
1489+
1490+
result = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
1491+
if ((result != CURLE_OK) || (written != packetsize)) {
1492+
failf(conn->data, "Failed to send SOCKS5 connect request.");
1493+
return 1;
1494+
}
1495+
1496+
result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
1497+
if ((result != CURLE_OK) || (actualread != packetsize)) {
1498+
failf(conn->data, "Failed to receive SOCKS5 connect request ack.");
1499+
return 1;
1500+
}
1501+
1502+
if (socksreq[0] != 5) { /* version */
1503+
failf(conn->data,
1504+
"SOCKS5 reply has wrong version, version should be 5.");
1505+
return 1;
1506+
}
1507+
if (socksreq[1] != 0) { /* Anything besides 0 is an error */
1508+
failf(conn->data,
1509+
"Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
1510+
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
1511+
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
1512+
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
1513+
socksreq[1]);
1514+
return 1;
1515+
}
1516+
}
1517+
1518+
Curl_nonblock(sock, TRUE);
1519+
return 0; /* Proxy was successful! */
1520+
}
1521+
13301522
static CURLcode ConnectPlease(struct connectdata *conn,
13311523
Curl_addrinfo *hostaddr,
13321524
bool *connected)
@@ -1356,6 +1548,21 @@ static CURLcode ConnectPlease(struct connectdata *conn,
13561548
conn->serv_addr.sin_family = hostaddr->h_addrtype;
13571549
conn->serv_addr.sin_port = htons((unsigned short)conn->port);
13581550
#endif
1551+
1552+
if (conn->data->set.proxytype == CURLPROXY_SOCKS5) {
1553+
return handleSock5Proxy(conn->data->state.proxyuser,
1554+
conn->data->state.proxypasswd,
1555+
conn,
1556+
conn->firstsocket) ?
1557+
CURLE_COULDNT_CONNECT : CURLE_OK;
1558+
}
1559+
else if (conn->data->set.proxytype == CURLPROXY_HTTP) {
1560+
/* do nothing here. handled later. */
1561+
}
1562+
else {
1563+
failf(conn->data, "unknown proxytype option given");
1564+
return CURLE_COULDNT_CONNECT;
1565+
}
13591566
}
13601567

13611568
return result;

lib/urldata.h

+2
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,8 @@ struct UserDefined {
645645
char *krb4_level; /* what security level */
646646
struct ssl_config_data ssl; /* user defined SSL stuff */
647647

648+
curl_proxytype proxytype; /* what kind of proxy that is in use */
649+
648650
int dns_cache_timeout; /* DNS cache timeout */
649651
long buffer_size; /* size of receive buffer to use */
650652

src/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2698,7 +2698,7 @@ operate(struct Configurable *config, int argc, char *argv[])
26982698
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
26992699
curl_easy_setopt(curl, CURLOPT_TIMEOUT, config->timeout);
27002700
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, config->postfields);
2701-
2701+
27022702
/* new in libcurl 7.2: */
27032703
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, config->postfieldsize);
27042704

0 commit comments

Comments
 (0)