@@ -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+
13301522static 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 ;
0 commit comments