Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl / socks.c
blobeaadf0d809f53698f177693d8735a43fd6b05aa2
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
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 ***************************************************************************/
24 #include "setup.h"
26 #include <string.h>
28 #ifdef NEED_MALLOC_H
29 #include <malloc.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
35 #include "urldata.h"
36 #include "sendf.h"
37 #include "strequal.h"
38 #include "select.h"
39 #include "connect.h"
40 #include "timeval.h"
41 #include "socks.h"
43 /* The last #include file should be: */
44 #include "memdebug.h"
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
51 * is what we have...
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
59 relative to
60 conn->created */
62 ssize_t nread;
63 ssize_t allread = 0;
64 int result;
65 struct timeval tvnow;
66 long conntime;
67 *n = 0;
68 do {
69 tvnow = Curl_tvnow();
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 */
74 result = ~CURLE_OK;
75 break;
77 if(Curl_select(sockfd, CURL_SOCKET_BAD,
78 (int)(conn_timeout - conntime)) <= 0) {
79 result = ~CURLE_OK;
80 break;
82 result = Curl_read(conn, sockfd, buf, buffersize, &nread);
83 if(result)
84 break;
86 if(buffersize == nread) {
87 allread += nread;
88 *n = allread;
89 result = CURLE_OK;
90 break;
92 buffersize -= nread;
93 buf += nread;
94 allread += nread;
95 } while(1);
96 return result;
100 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
101 * destination server.
103 * Reference :
104 * http://socks.permeo.com/protocol/socks4.protocol
106 * Note :
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 */
114 int result;
115 CURLcode code;
116 curl_socket_t sock = conn->sock[FIRSTSOCKET];
117 long timeout;
118 struct SessionHandle *data = conn->data;
120 /* get timeout */
121 if(data->set.timeout && data->set.connecttimeout) {
122 if (data->set.timeout < data->set.connecttimeout)
123 timeout = data->set.timeout*1000;
124 else
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;
131 else
132 timeout = DEFAULT_CONNECT_TIMEOUT;
134 Curl_nonblock(sock, FALSE);
137 * Compose socks4 request
139 * Request format
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);
151 /* DNS resolve */
153 struct Curl_dns_entry *dns;
154 Curl_addrinfo *hp=NULL;
155 int rc;
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.
170 if(dns)
171 hp=dns->addr;
172 if (hp) {
173 char buf[64];
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])) {
179 /* Set DSTIP */
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];
185 else
186 hp = NULL; /* fail! */
188 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
191 if(!hp) {
192 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
193 conn->host.name);
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 */
202 if (proxy_name)
203 strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
206 * Make connection
209 ssize_t actualread;
210 ssize_t written;
211 int packetsize = 9 +
212 (int)strlen((char*)socksreq + 8); /* size including NUL */
214 /* Send request */
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;
232 * Response format
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) {
252 failf(data,
253 "SOCKS4 reply has wrong version, version should be 4.");
254 return CURLE_COULDNT_CONNECT;
257 /* Result */
258 switch(socksreq[1])
260 case 90:
261 infof(data, "SOCKS4 request granted.\n");
262 break;
263 case 91:
264 failf(data,
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])),
270 socksreq[1]);
271 return CURLE_COULDNT_CONNECT;
272 case 92:
273 failf(data,
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])),
280 socksreq[1]);
281 return CURLE_COULDNT_CONNECT;
282 case 93:
283 failf(data,
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])),
290 socksreq[1]);
291 return CURLE_COULDNT_CONNECT;
292 default:
293 failf(data,
294 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
295 ", Unknown.",
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])),
299 socksreq[1]);
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
319 replies:
321 +----+-----+-------+------+----------+----------+
322 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
323 +----+-----+-------+------+----------+----------+
324 | 1 | 1 | X'00' | 1 | Variable | 2 |
325 +----+-----+-------+------+----------+----------+
327 Where:
329 o VER protocol version: X'05'
330 o REP Reply field:
331 o X'00' succeeded
334 unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
335 ssize_t actualread;
336 ssize_t written;
337 int result;
338 CURLcode code;
339 curl_socket_t sock = conn->sock[FIRSTSOCKET];
340 struct SessionHandle *data = conn->data;
341 long timeout;
343 /* get timeout */
344 if(data->set.timeout && data->set.connecttimeout) {
345 if (data->set.timeout < data->set.connecttimeout)
346 timeout = data->set.timeout*1000;
347 else
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;
354 else
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);
362 if(-1 == result) {
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]),
384 &written);
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);
394 if(-1 == result) {
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;
427 int len;
428 if(proxy_name && proxy_password) {
429 userlen = strlen(proxy_name);
430 pwlen = proxy_password?strlen(proxy_password):0;
432 else {
433 userlen = 0;
434 pwlen = 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 * +----+------+----------+------+----------+
444 len = 0;
445 socksreq[len++] = 1; /* username/pw subnegotiation version */
446 socksreq[len++] = (char) userlen;
447 memcpy(socksreq + len, proxy_name, (int) userlen);
448 len += userlen;
449 socksreq[len++] = (char) pwlen;
450 memcpy(socksreq + len, proxy_password, (int) pwlen);
451 len += 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,
460 timeout);
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! */
475 else {
476 /* error */
477 if (socksreq[1] == 1) {
478 failf(data,
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) {
484 failf(data,
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.)");
489 else {
490 failf(data, "No authentication method was acceptable.");
492 return CURLE_COULDNT_CONNECT;
494 else {
495 failf(data,
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.
523 if(dns)
524 hp=dns->addr;
525 if (hp) {
526 char buf[64];
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];
537 else
538 hp = NULL; /* fail! */
540 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
542 if(!hp) {
543 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
544 conn->host.name);
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 */
568 failf(data,
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 */
573 failf(data,
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])),
578 socksreq[1]);
579 return CURLE_COULDNT_CONNECT;
583 Curl_nonblock(sock, TRUE);
584 return CURLE_OK; /* Proxy was successful! */