1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.
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,
114 struct connectdata *conn)
116 unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
119 curl_socket_t sock = conn->sock[sockindex];
121 struct SessionHandle *data = conn->data;
124 if(data->set.timeout && data->set.connecttimeout) {
125 if (data->set.timeout < data->set.connecttimeout)
126 timeout = data->set.timeout;
128 timeout = data->set.connecttimeout;
130 else if(data->set.timeout)
131 timeout = data->set.timeout;
132 else if(data->set.connecttimeout)
133 timeout = data->set.connecttimeout;
135 timeout = DEFAULT_CONNECT_TIMEOUT;
137 Curl_nonblock(sock, FALSE);
140 * Compose socks4 request
144 * +----+----+----+----+----+----+----+----+----+----+....+----+
145 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
146 * +----+----+----+----+----+----+----+----+----+----+....+----+
147 * # of bytes: 1 1 2 4 variable 1
150 socksreq[0] = 4; /* version (SOCKS4) */
151 socksreq[1] = 1; /* connect */
152 *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);
156 struct Curl_dns_entry *dns;
157 Curl_addrinfo *hp=NULL;
160 rc = Curl_resolv(conn, hostname, remote_port, &dns);
162 if(rc == CURLRESOLV_ERROR)
163 return CURLE_COULDNT_RESOLVE_PROXY;
165 if(rc == CURLRESOLV_PENDING)
166 /* this requires that we're in "wait for resolve" state */
167 rc = Curl_wait_for_resolv(conn, &dns);
170 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
171 * returns a Curl_addrinfo pointer that may not always look the same.
177 unsigned short ip[4];
178 Curl_printable_address(hp, buf, sizeof(buf));
180 if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
181 &ip[0], &ip[1], &ip[2], &ip[3])) {
183 socksreq[4] = (unsigned char)ip[0];
184 socksreq[5] = (unsigned char)ip[1];
185 socksreq[6] = (unsigned char)ip[2];
186 socksreq[7] = (unsigned char)ip[3];
189 hp = NULL; /* fail! */
191 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
195 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
197 return CURLE_COULDNT_RESOLVE_HOST;
202 * This is currently not supporting "Identification Protocol (RFC1413)".
204 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
206 strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
215 (int)strlen((char*)socksreq + 8); /* size including NUL */
218 code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
219 if ((code != CURLE_OK) || (written != packetsize)) {
220 failf(data, "Failed to send SOCKS4 connect request.");
221 return CURLE_COULDNT_CONNECT;
224 packetsize = 8; /* receive data size */
226 /* Receive response */
227 result = blockread_all(conn, sock, (char *)socksreq, packetsize,
228 &actualread, timeout);
229 if ((result != CURLE_OK) || (actualread != packetsize)) {
230 failf(data, "Failed to receive SOCKS4 connect request ack.");
231 return CURLE_COULDNT_CONNECT;
237 * +----+----+----+----+----+----+----+----+
238 * | VN | CD | DSTPORT | DSTIP |
239 * +----+----+----+----+----+----+----+----+
240 * # of bytes: 1 1 2 4
242 * VN is the version of the reply code and should be 0. CD is the result
243 * code with one of the following values:
245 * 90: request granted
246 * 91: request rejected or failed
247 * 92: request rejected because SOCKS server cannot connect to
248 * identd on the client
249 * 93: request rejected because the client program and identd
250 * report different user-ids
253 /* wrong version ? */
254 if (socksreq[0] != 0) {
256 "SOCKS4 reply has wrong version, version should be 4.");
257 return CURLE_COULDNT_CONNECT;
264 infof(data, "SOCKS4 request granted.\n");
268 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
269 ", request rejected or failed.",
270 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
271 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
272 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
274 return CURLE_COULDNT_CONNECT;
277 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
278 ", request rejected because SOCKS server cannot connect to "
279 "identd on the client.",
280 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
281 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
282 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
284 return CURLE_COULDNT_CONNECT;
287 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
288 ", request rejected because the client program and identd "
289 "report different user-ids.",
290 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
291 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
292 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
294 return CURLE_COULDNT_CONNECT;
297 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
299 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
300 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
301 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
303 return CURLE_COULDNT_CONNECT;
307 Curl_nonblock(sock, TRUE);
309 return CURLE_OK; /* Proxy was successful! */
313 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
314 * destination server.
316 CURLcode Curl_SOCKS5(const char *proxy_name,
317 const char *proxy_password,
321 struct connectdata *conn)
324 According to the RFC1928, section "6. Replies". This is what a SOCK5
327 +----+-----+-------+------+----------+----------+
328 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
329 +----+-----+-------+------+----------+----------+
330 | 1 | 1 | X'00' | 1 | Variable | 2 |
331 +----+-----+-------+------+----------+----------+
335 o VER protocol version: X'05'
340 unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
345 curl_socket_t sock = conn->sock[sockindex];
346 struct SessionHandle *data = conn->data;
350 if(data->set.timeout && data->set.connecttimeout) {
351 if (data->set.timeout < data->set.connecttimeout)
352 timeout = data->set.timeout;
354 timeout = data->set.connecttimeout;
356 else if(data->set.timeout)
357 timeout = data->set.timeout;
358 else if(data->set.connecttimeout)
359 timeout = data->set.connecttimeout;
361 timeout = DEFAULT_CONNECT_TIMEOUT;
363 Curl_nonblock(sock, TRUE);
365 /* wait until socket gets connected */
366 result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);
369 failf(conn->data, "SOCKS5: no connection here");
370 return CURLE_COULDNT_CONNECT;
372 else if(0 == result) {
373 failf(conn->data, "SOCKS5: connection timeout");
374 return CURLE_OPERATION_TIMEDOUT;
377 if(result & CSELECT_ERR) {
378 failf(conn->data, "SOCKS5: error occured during connection");
379 return CURLE_COULDNT_CONNECT;
382 socksreq[0] = 5; /* version */
383 socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
384 socksreq[2] = 0; /* no authentication */
385 socksreq[3] = 2; /* username/password */
387 Curl_nonblock(sock, FALSE);
389 code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
391 if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
392 failf(data, "Unable to send initial SOCKS5 request.");
393 return CURLE_COULDNT_CONNECT;
396 Curl_nonblock(sock, TRUE);
398 result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);
401 failf(conn->data, "SOCKS5 nothing to read");
402 return CURLE_COULDNT_CONNECT;
404 else if(0 == result) {
405 failf(conn->data, "SOCKS5 read timeout");
406 return CURLE_OPERATION_TIMEDOUT;
409 if(result & CSELECT_ERR) {
410 failf(conn->data, "SOCKS5 read error occured");
411 return CURLE_RECV_ERROR;
414 Curl_nonblock(sock, FALSE);
416 result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
417 if ((result != CURLE_OK) || (actualread != 2)) {
418 failf(data, "Unable to receive initial SOCKS5 response.");
419 return CURLE_COULDNT_CONNECT;
422 if (socksreq[0] != 5) {
423 failf(data, "Received invalid version in initial SOCKS5 response.");
424 return CURLE_COULDNT_CONNECT;
426 if (socksreq[1] == 0) {
427 /* Nothing to do, no authentication needed */
430 else if (socksreq[1] == 2) {
431 /* Needs user name and password */
432 size_t userlen, pwlen;
434 if(proxy_name && proxy_password) {
435 userlen = strlen(proxy_name);
436 pwlen = proxy_password?strlen(proxy_password):0;
443 /* username/password request looks like
444 * +----+------+----------+------+----------+
445 * |VER | ULEN | UNAME | PLEN | PASSWD |
446 * +----+------+----------+------+----------+
447 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
448 * +----+------+----------+------+----------+
451 socksreq[len++] = 1; /* username/pw subnegotiation version */
452 socksreq[len++] = (char) userlen;
453 memcpy(socksreq + len, proxy_name, (int) userlen);
455 socksreq[len++] = (char) pwlen;
456 memcpy(socksreq + len, proxy_password, (int) pwlen);
459 code = Curl_write(conn, sock, (char *)socksreq, len, &written);
460 if ((code != CURLE_OK) || (len != written)) {
461 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
462 return CURLE_COULDNT_CONNECT;
465 result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
467 if ((result != CURLE_OK) || (actualread != 2)) {
468 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
469 return CURLE_COULDNT_CONNECT;
472 /* ignore the first (VER) byte */
473 if (socksreq[1] != 0) { /* status */
474 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
475 socksreq[0], socksreq[1]);
476 return CURLE_COULDNT_CONNECT;
479 /* Everything is good so far, user was authenticated! */
483 if (socksreq[1] == 1) {
485 "SOCKS5 GSSAPI per-message authentication is not supported.");
486 return CURLE_COULDNT_CONNECT;
488 else if (socksreq[1] == 255) {
489 if (!proxy_name || !*proxy_name) {
491 "No authentication method was acceptable. (It is quite likely"
492 " that the SOCKS5 server wanted a username/password, since none"
493 " was supplied to the server on this connection.)");
496 failf(data, "No authentication method was acceptable.");
498 return CURLE_COULDNT_CONNECT;
502 "Undocumented SOCKS5 mode attempted to be used by server.");
503 return CURLE_COULDNT_CONNECT;
507 /* Authentication is complete, now specify destination to the proxy */
508 socksreq[0] = 5; /* version (SOCKS5) */
509 socksreq[1] = 1; /* connect */
510 socksreq[2] = 0; /* must be zero */
511 socksreq[3] = 1; /* IPv4 = 1 */
514 struct Curl_dns_entry *dns;
515 Curl_addrinfo *hp=NULL;
516 int rc = Curl_resolv(conn, hostname, remote_port, &dns);
518 if(rc == CURLRESOLV_ERROR)
519 return CURLE_COULDNT_RESOLVE_HOST;
521 if(rc == CURLRESOLV_PENDING)
522 /* this requires that we're in "wait for resolve" state */
523 rc = Curl_wait_for_resolv(conn, &dns);
526 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
527 * returns a Curl_addrinfo pointer that may not always look the same.
533 unsigned short ip[4];
534 Curl_printable_address(hp, buf, sizeof(buf));
536 if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
537 &ip[0], &ip[1], &ip[2], &ip[3])) {
538 socksreq[4] = (unsigned char)ip[0];
539 socksreq[5] = (unsigned char)ip[1];
540 socksreq[6] = (unsigned char)ip[2];
541 socksreq[7] = (unsigned char)ip[3];
544 hp = NULL; /* fail! */
546 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
549 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
551 return CURLE_COULDNT_RESOLVE_HOST;
555 *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
558 const int packetsize = 10;
560 code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
561 if ((code != CURLE_OK) || (written != packetsize)) {
562 failf(data, "Failed to send SOCKS5 connect request.");
563 return CURLE_COULDNT_CONNECT;
566 result = blockread_all(conn, sock, (char *)socksreq, packetsize,
567 &actualread, timeout);
568 if ((result != CURLE_OK) || (actualread != packetsize)) {
569 failf(data, "Failed to receive SOCKS5 connect request ack.");
570 return CURLE_COULDNT_CONNECT;
573 if (socksreq[0] != 5) { /* version */
575 "SOCKS5 reply has wrong version, version should be 5.");
576 return CURLE_COULDNT_CONNECT;
578 if (socksreq[1] != 0) { /* Anything besides 0 is an error */
580 "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
581 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
582 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
583 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
585 return CURLE_COULDNT_CONNECT;
589 Curl_nonblock(sock, TRUE);
590 return CURLE_OK; /* Proxy was successful! */