1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: socks.c,v 1.2 2007/03/15 19:22:13 andy Exp $
22 ***************************************************************************/
43 /* The last #include file should be: */
47 * Helper read-from-socket functions. Does the same as Curl_read() but it
48 * blocks until all bytes amount of buffersize will be read. No more, no less.
50 * This is STUPID BLOCKING behaviour which we frown upon, but right now this
53 static int blockread_all(struct connectdata
*conn
, /* connection data */
54 curl_socket_t sockfd
, /* read from this socket */
55 char *buf
, /* store read data here */
56 ssize_t buffersize
, /* max amount to read */
57 ssize_t
*n
, /* amount bytes read */
58 long conn_timeout
) /* timeout for data wait
70 /* calculating how long connection is establishing */
71 conntime
= Curl_tvdiff(tvnow
, conn
->created
);
72 if(conntime
> conn_timeout
) {
73 /* we already got the timeout */
77 if(Curl_select(sockfd
, CURL_SOCKET_BAD
,
78 (int)(conn_timeout
- conntime
)) <= 0) {
82 result
= Curl_read(conn
, sockfd
, buf
, buffersize
, &nread
);
86 if(buffersize
== nread
) {
100 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
101 * destination server.
104 * http://socks.permeo.com/protocol/socks4.protocol
107 * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
108 * Nonsupport "Identification Protocol (RFC1413)"
110 CURLcode
Curl_SOCKS4(const char *proxy_name
,
111 struct connectdata
*conn
)
113 unsigned char socksreq
[262]; /* room for SOCKS4 request incl. user id */
116 curl_socket_t sock
= conn
->sock
[FIRSTSOCKET
];
118 struct SessionHandle
*data
= conn
->data
;
121 if(data
->set
.timeout
&& data
->set
.connecttimeout
) {
122 if (data
->set
.timeout
< data
->set
.connecttimeout
)
123 timeout
= data
->set
.timeout
*1000;
125 timeout
= data
->set
.connecttimeout
*1000;
127 else if(data
->set
.timeout
)
128 timeout
= data
->set
.timeout
*1000;
129 else if(data
->set
.connecttimeout
)
130 timeout
= data
->set
.connecttimeout
*1000;
132 timeout
= DEFAULT_CONNECT_TIMEOUT
;
134 Curl_nonblock(sock
, FALSE
);
137 * Compose socks4 request
141 * +----+----+----+----+----+----+----+----+----+----+....+----+
142 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
143 * +----+----+----+----+----+----+----+----+----+----+....+----+
144 * # of bytes: 1 1 2 4 variable 1
147 socksreq
[0] = 4; /* version (SOCKS4) */
148 socksreq
[1] = 1; /* connect */
149 *((unsigned short*)&socksreq
[2]) = htons(conn
->remote_port
);
153 struct Curl_dns_entry
*dns
;
154 Curl_addrinfo
*hp
=NULL
;
157 rc
= Curl_resolv(conn
, conn
->host
.name
, (int)conn
->remote_port
, &dns
);
159 if(rc
== CURLRESOLV_ERROR
)
160 return CURLE_COULDNT_RESOLVE_PROXY
;
162 if(rc
== CURLRESOLV_PENDING
)
163 /* this requires that we're in "wait for resolve" state */
164 rc
= Curl_wait_for_resolv(conn
, &dns
);
167 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
168 * returns a Curl_addrinfo pointer that may not always look the same.
174 unsigned short ip
[4];
175 Curl_printable_address(hp
, buf
, sizeof(buf
));
177 if(4 == sscanf( buf
, "%hu.%hu.%hu.%hu",
178 &ip
[0], &ip
[1], &ip
[2], &ip
[3])) {
180 socksreq
[4] = (unsigned char)ip
[0];
181 socksreq
[5] = (unsigned char)ip
[1];
182 socksreq
[6] = (unsigned char)ip
[2];
183 socksreq
[7] = (unsigned char)ip
[3];
186 hp
= NULL
; /* fail! */
188 Curl_resolv_unlock(data
, dns
); /* not used anymore from now on */
192 failf(data
, "Failed to resolve \"%s\" for SOCKS4 connect.",
194 return CURLE_COULDNT_RESOLVE_HOST
;
199 * This is currently not supporting "Identification Protocol (RFC1413)".
201 socksreq
[8] = 0; /* ensure empty userid is NUL-terminated */
203 strlcat((char*)socksreq
+ 8, proxy_name
, sizeof(socksreq
) - 8);
212 (int)strlen((char*)socksreq
+ 8); /* size including NUL */
215 code
= Curl_write(conn
, sock
, (char *)socksreq
, packetsize
, &written
);
216 if ((code
!= CURLE_OK
) || (written
!= packetsize
)) {
217 failf(data
, "Failed to send SOCKS4 connect request.");
218 return CURLE_COULDNT_CONNECT
;
221 packetsize
= 8; /* receive data size */
223 /* Receive response */
224 result
= blockread_all(conn
, sock
, (char *)socksreq
, packetsize
,
225 &actualread
, timeout
);
226 if ((result
!= CURLE_OK
) || (actualread
!= packetsize
)) {
227 failf(data
, "Failed to receive SOCKS4 connect request ack.");
228 return CURLE_COULDNT_CONNECT
;
234 * +----+----+----+----+----+----+----+----+
235 * | VN | CD | DSTPORT | DSTIP |
236 * +----+----+----+----+----+----+----+----+
237 * # of bytes: 1 1 2 4
239 * VN is the version of the reply code and should be 0. CD is the result
240 * code with one of the following values:
242 * 90: request granted
243 * 91: request rejected or failed
244 * 92: request rejected because SOCKS server cannot connect to
245 * identd on the client
246 * 93: request rejected because the client program and identd
247 * report different user-ids
250 /* wrong version ? */
251 if (socksreq
[0] != 0) {
253 "SOCKS4 reply has wrong version, version should be 4.");
254 return CURLE_COULDNT_CONNECT
;
261 infof(data
, "SOCKS4 request granted.\n");
265 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
266 ", request rejected or failed.",
267 (unsigned char)socksreq
[4], (unsigned char)socksreq
[5],
268 (unsigned char)socksreq
[6], (unsigned char)socksreq
[7],
269 (unsigned int)ntohs(*(unsigned short*)(&socksreq
[8])),
271 return CURLE_COULDNT_CONNECT
;
274 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
275 ", request rejected because SOCKS server cannot connect to "
276 "identd on the client.",
277 (unsigned char)socksreq
[4], (unsigned char)socksreq
[5],
278 (unsigned char)socksreq
[6], (unsigned char)socksreq
[7],
279 (unsigned int)ntohs(*(unsigned short*)(&socksreq
[8])),
281 return CURLE_COULDNT_CONNECT
;
284 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
285 ", request rejected because the client program and identd "
286 "report different user-ids.",
287 (unsigned char)socksreq
[4], (unsigned char)socksreq
[5],
288 (unsigned char)socksreq
[6], (unsigned char)socksreq
[7],
289 (unsigned int)ntohs(*(unsigned short*)(&socksreq
[8])),
291 return CURLE_COULDNT_CONNECT
;
294 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
296 (unsigned char)socksreq
[4], (unsigned char)socksreq
[5],
297 (unsigned char)socksreq
[6], (unsigned char)socksreq
[7],
298 (unsigned int)ntohs(*(unsigned short*)(&socksreq
[8])),
300 return CURLE_COULDNT_CONNECT
;
304 Curl_nonblock(sock
, TRUE
);
306 return CURLE_OK
; /* Proxy was successful! */
310 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
311 * destination server.
313 CURLcode
Curl_SOCKS5(const char *proxy_name
,
314 const char *proxy_password
,
315 struct connectdata
*conn
)
318 According to the RFC1928, section "6. Replies". This is what a SOCK5
321 +----+-----+-------+------+----------+----------+
322 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
323 +----+-----+-------+------+----------+----------+
324 | 1 | 1 | X'00' | 1 | Variable | 2 |
325 +----+-----+-------+------+----------+----------+
329 o VER protocol version: X'05'
334 unsigned char socksreq
[600]; /* room for large user/pw (255 max each) */
339 curl_socket_t sock
= conn
->sock
[FIRSTSOCKET
];
340 struct SessionHandle
*data
= conn
->data
;
344 if(data
->set
.timeout
&& data
->set
.connecttimeout
) {
345 if (data
->set
.timeout
< data
->set
.connecttimeout
)
346 timeout
= data
->set
.timeout
*1000;
348 timeout
= data
->set
.connecttimeout
*1000;
350 else if(data
->set
.timeout
)
351 timeout
= data
->set
.timeout
*1000;
352 else if(data
->set
.connecttimeout
)
353 timeout
= data
->set
.connecttimeout
*1000;
355 timeout
= DEFAULT_CONNECT_TIMEOUT
;
357 Curl_nonblock(sock
, TRUE
);
359 /* wait until socket gets connected */
360 result
= Curl_select(CURL_SOCKET_BAD
, sock
, (int)timeout
);
363 failf(conn
->data
, "SOCKS5: no connection here");
364 return CURLE_COULDNT_CONNECT
;
366 else if(0 == result
) {
367 failf(conn
->data
, "SOCKS5: connection timeout");
368 return CURLE_OPERATION_TIMEDOUT
;
371 if(result
& CSELECT_ERR
) {
372 failf(conn
->data
, "SOCKS5: error occured during connection");
373 return CURLE_COULDNT_CONNECT
;
376 socksreq
[0] = 5; /* version */
377 socksreq
[1] = (char)(proxy_name
? 2 : 1); /* number of methods (below) */
378 socksreq
[2] = 0; /* no authentication */
379 socksreq
[3] = 2; /* username/password */
381 Curl_nonblock(sock
, FALSE
);
383 code
= Curl_write(conn
, sock
, (char *)socksreq
, (2 + (int)socksreq
[1]),
385 if ((code
!= CURLE_OK
) || (written
!= (2 + (int)socksreq
[1]))) {
386 failf(data
, "Unable to send initial SOCKS5 request.");
387 return CURLE_COULDNT_CONNECT
;
390 Curl_nonblock(sock
, TRUE
);
392 result
= Curl_select(sock
, CURL_SOCKET_BAD
, (int)timeout
);
395 failf(conn
->data
, "SOCKS5 nothing to read");
396 return CURLE_COULDNT_CONNECT
;
398 else if(0 == result
) {
399 failf(conn
->data
, "SOCKS5 read timeout");
400 return CURLE_OPERATION_TIMEDOUT
;
403 if(result
& CSELECT_ERR
) {
404 failf(conn
->data
, "SOCKS5 read error occured");
405 return CURLE_RECV_ERROR
;
408 Curl_nonblock(sock
, FALSE
);
410 result
=blockread_all(conn
, sock
, (char *)socksreq
, 2, &actualread
, timeout
);
411 if ((result
!= CURLE_OK
) || (actualread
!= 2)) {
412 failf(data
, "Unable to receive initial SOCKS5 response.");
413 return CURLE_COULDNT_CONNECT
;
416 if (socksreq
[0] != 5) {
417 failf(data
, "Received invalid version in initial SOCKS5 response.");
418 return CURLE_COULDNT_CONNECT
;
420 if (socksreq
[1] == 0) {
421 /* Nothing to do, no authentication needed */
424 else if (socksreq
[1] == 2) {
425 /* Needs user name and password */
426 size_t userlen
, pwlen
;
428 if(proxy_name
&& proxy_password
) {
429 userlen
= strlen(proxy_name
);
430 pwlen
= proxy_password
?strlen(proxy_password
):0;
437 /* username/password request looks like
438 * +----+------+----------+------+----------+
439 * |VER | ULEN | UNAME | PLEN | PASSWD |
440 * +----+------+----------+------+----------+
441 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
442 * +----+------+----------+------+----------+
445 socksreq
[len
++] = 1; /* username/pw subnegotiation version */
446 socksreq
[len
++] = (char) userlen
;
447 memcpy(socksreq
+ len
, proxy_name
, (int) userlen
);
449 socksreq
[len
++] = (char) pwlen
;
450 memcpy(socksreq
+ len
, proxy_password
, (int) pwlen
);
453 code
= Curl_write(conn
, sock
, (char *)socksreq
, len
, &written
);
454 if ((code
!= CURLE_OK
) || (len
!= written
)) {
455 failf(data
, "Failed to send SOCKS5 sub-negotiation request.");
456 return CURLE_COULDNT_CONNECT
;
459 result
=blockread_all(conn
, sock
, (char *)socksreq
, 2, &actualread
,
461 if ((result
!= CURLE_OK
) || (actualread
!= 2)) {
462 failf(data
, "Unable to receive SOCKS5 sub-negotiation response.");
463 return CURLE_COULDNT_CONNECT
;
466 /* ignore the first (VER) byte */
467 if (socksreq
[1] != 0) { /* status */
468 failf(data
, "User was rejected by the SOCKS5 server (%d %d).",
469 socksreq
[0], socksreq
[1]);
470 return CURLE_COULDNT_CONNECT
;
473 /* Everything is good so far, user was authenticated! */
477 if (socksreq
[1] == 1) {
479 "SOCKS5 GSSAPI per-message authentication is not supported.");
480 return CURLE_COULDNT_CONNECT
;
482 else if (socksreq
[1] == 255) {
483 if (!proxy_name
|| !*proxy_name
) {
485 "No authentication method was acceptable. (It is quite likely"
486 " that the SOCKS5 server wanted a username/password, since none"
487 " was supplied to the server on this connection.)");
490 failf(data
, "No authentication method was acceptable.");
492 return CURLE_COULDNT_CONNECT
;
496 "Undocumented SOCKS5 mode attempted to be used by server.");
497 return CURLE_COULDNT_CONNECT
;
501 /* Authentication is complete, now specify destination to the proxy */
502 socksreq
[0] = 5; /* version (SOCKS5) */
503 socksreq
[1] = 1; /* connect */
504 socksreq
[2] = 0; /* must be zero */
505 socksreq
[3] = 1; /* IPv4 = 1 */
508 struct Curl_dns_entry
*dns
;
509 Curl_addrinfo
*hp
=NULL
;
510 int rc
= Curl_resolv(conn
, conn
->host
.name
, (int)conn
->remote_port
, &dns
);
512 if(rc
== CURLRESOLV_ERROR
)
513 return CURLE_COULDNT_RESOLVE_HOST
;
515 if(rc
== CURLRESOLV_PENDING
)
516 /* this requires that we're in "wait for resolve" state */
517 rc
= Curl_wait_for_resolv(conn
, &dns
);
520 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
521 * returns a Curl_addrinfo pointer that may not always look the same.
527 unsigned short ip
[4];
528 Curl_printable_address(hp
, buf
, sizeof(buf
));
530 if(4 == sscanf( buf
, "%hu.%hu.%hu.%hu",
531 &ip
[0], &ip
[1], &ip
[2], &ip
[3])) {
532 socksreq
[4] = (unsigned char)ip
[0];
533 socksreq
[5] = (unsigned char)ip
[1];
534 socksreq
[6] = (unsigned char)ip
[2];
535 socksreq
[7] = (unsigned char)ip
[3];
538 hp
= NULL
; /* fail! */
540 Curl_resolv_unlock(data
, dns
); /* not used anymore from now on */
543 failf(data
, "Failed to resolve \"%s\" for SOCKS5 connect.",
545 return CURLE_COULDNT_RESOLVE_HOST
;
549 *((unsigned short*)&socksreq
[8]) = htons(conn
->remote_port
);
552 const int packetsize
= 10;
554 code
= Curl_write(conn
, sock
, (char *)socksreq
, packetsize
, &written
);
555 if ((code
!= CURLE_OK
) || (written
!= packetsize
)) {
556 failf(data
, "Failed to send SOCKS5 connect request.");
557 return CURLE_COULDNT_CONNECT
;
560 result
= blockread_all(conn
, sock
, (char *)socksreq
, packetsize
,
561 &actualread
, timeout
);
562 if ((result
!= CURLE_OK
) || (actualread
!= packetsize
)) {
563 failf(data
, "Failed to receive SOCKS5 connect request ack.");
564 return CURLE_COULDNT_CONNECT
;
567 if (socksreq
[0] != 5) { /* version */
569 "SOCKS5 reply has wrong version, version should be 5.");
570 return CURLE_COULDNT_CONNECT
;
572 if (socksreq
[1] != 0) { /* Anything besides 0 is an error */
574 "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
575 (unsigned char)socksreq
[4], (unsigned char)socksreq
[5],
576 (unsigned char)socksreq
[6], (unsigned char)socksreq
[7],
577 (unsigned int)ntohs(*(unsigned short*)(&socksreq
[8])),
579 return CURLE_COULDNT_CONNECT
;
583 Curl_nonblock(sock
, TRUE
);
584 return CURLE_OK
; /* Proxy was successful! */