11 /* The last #include file should be: */
15 * Helper read-from-socket functions. Does the same as Curl_read() but it
16 * blocks until all bytes amount of buffersize will be read. No more, no less.
18 * This is STUPID BLOCKING behaviour which we frown upon, but right now this
21 static int blockread_all(struct connectdata *conn, /* connection data */
22 curl_socket_t sockfd, /* read from this socket */
23 char *buf, /* store read data here */
24 ssize_t buffersize, /* max amount to read */
25 ssize_t *n, /* amount bytes read */
26 long conn_timeout) /* timeout for data wait
38 /* calculating how long connection is establishing */
39 conntime = Curl_tvdiff(tvnow, conn->created);
40 if(conntime > conn_timeout) {
41 /* we already got the timeout */
44 if(Curl_select(sockfd, CURL_SOCKET_BAD,
45 (int)(conn_timeout - conntime)) <= 0) {
48 result = Curl_read(conn, sockfd, buf, buffersize, &nread);
52 if(buffersize == nread) {
64 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
68 * http://socks.permeo.com/protocol/socks4.protocol
71 * Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
72 * Nonsupport "Identification Protocol (RFC1413)"
74 CURLcode Curl_SOCKS4(const char *proxy_name,
75 struct connectdata *conn)
77 unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
80 curl_socket_t sock = conn->sock[FIRSTSOCKET];
82 struct SessionHandle *data = conn->data;
85 if(data->set.timeout && data->set.connecttimeout) {
86 if (data->set.timeout < data->set.connecttimeout)
87 timeout = data->set.timeout*1000;
89 timeout = data->set.connecttimeout*1000;
91 else if(data->set.timeout)
92 timeout = data->set.timeout*1000;
93 else if(data->set.connecttimeout)
94 timeout = data->set.connecttimeout*1000;
96 timeout = DEFAULT_CONNECT_TIMEOUT;
98 Curl_nonblock(sock, FALSE);
101 * Compose socks4 request
105 * +----+----+----+----+----+----+----+----+----+----+....+----+
106 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
107 * +----+----+----+----+----+----+----+----+----+----+....+----+
108 * # of bytes: 1 1 2 4 variable 1
111 socksreq[0] = 4; /* version (SOCKS4) */
112 socksreq[1] = 1; /* connect */
113 *((unsigned short*)&socksreq[2]) = htons(conn->remote_port);
117 struct Curl_dns_entry *dns;
118 Curl_addrinfo *hp=NULL;
121 rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
123 if(rc == CURLRESOLV_ERROR)
124 return CURLE_COULDNT_RESOLVE_PROXY;
126 if(rc == CURLRESOLV_PENDING)
127 /* this requires that we're in "wait for resolve" state */
128 rc = Curl_wait_for_resolv(conn, &dns);
131 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
132 * returns a Curl_addrinfo pointer that may not always look the same.
138 unsigned short ip[4];
139 Curl_printable_address(hp, buf, sizeof(buf));
141 if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
142 &ip[0], &ip[1], &ip[2], &ip[3])) {
144 socksreq[4] = (unsigned char)ip[0];
145 socksreq[5] = (unsigned char)ip[1];
146 socksreq[6] = (unsigned char)ip[2];
147 socksreq[7] = (unsigned char)ip[3];
150 hp = NULL; /* fail! */
152 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
156 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
158 return CURLE_COULDNT_RESOLVE_HOST;
163 * This is currently not supporting "Identification Protocol (RFC1413)".
165 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
167 strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
176 (int)strlen((char*)socksreq + 8); /* size including NUL */
179 code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
180 if ((code != CURLE_OK) || (written != packetsize)) {
181 failf(data, "Failed to send SOCKS4 connect request.");
182 return CURLE_COULDNT_CONNECT;
185 packetsize = 8; /* receive data size */
187 /* Receive response */
188 result = blockread_all(conn, sock, (char *)socksreq, packetsize,
189 &actualread, timeout);
190 if ((result != CURLE_OK) || (actualread != packetsize)) {
191 failf(data, "Failed to receive SOCKS4 connect request ack.");
192 return CURLE_COULDNT_CONNECT;
198 * +----+----+----+----+----+----+----+----+
199 * | VN | CD | DSTPORT | DSTIP |
200 * +----+----+----+----+----+----+----+----+
201 * # of bytes: 1 1 2 4
203 * VN is the version of the reply code and should be 0. CD is the result
204 * code with one of the following values:
206 * 90: request granted
207 * 91: request rejected or failed
208 * 92: request rejected because SOCKS server cannot connect to
209 * identd on the client
210 * 93: request rejected because the client program and identd
211 * report different user-ids
214 /* wrong version ? */
215 if (socksreq[0] != 0) {
217 "SOCKS4 reply has wrong version, version should be 4.");
218 return CURLE_COULDNT_CONNECT;
225 infof(data, "SOCKS4 request granted.\n");
229 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
230 ", request rejected or failed.",
231 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
232 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
233 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
235 return CURLE_COULDNT_CONNECT;
238 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
239 ", request rejected because SOCKS server cannot connect to "
240 "identd on the client.",
241 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
242 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
243 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
245 return CURLE_COULDNT_CONNECT;
248 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
249 ", request rejected because the client program and identd "
250 "report different user-ids.",
251 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
252 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
253 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
255 return CURLE_COULDNT_CONNECT;
258 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
260 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
261 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
262 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
264 return CURLE_COULDNT_CONNECT;
268 Curl_nonblock(sock, TRUE);
270 return CURLE_OK; /* Proxy was successful! */
274 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
275 * destination server.
277 CURLcode Curl_SOCKS5(const char *proxy_name,
278 const char *proxy_password,
279 struct connectdata *conn)
282 According to the RFC1928, section "6. Replies". This is what a SOCK5
285 +----+-----+-------+------+----------+----------+
286 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
287 +----+-----+-------+------+----------+----------+
288 | 1 | 1 | X'00' | 1 | Variable | 2 |
289 +----+-----+-------+------+----------+----------+
293 o VER protocol version: X'05'
298 unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
303 curl_socket_t sock = conn->sock[FIRSTSOCKET];
304 struct SessionHandle *data = conn->data;
308 if(data->set.timeout && data->set.connecttimeout) {
309 if (data->set.timeout < data->set.connecttimeout)
310 timeout = data->set.timeout*1000;
312 timeout = data->set.connecttimeout*1000;
314 else if(data->set.timeout)
315 timeout = data->set.timeout*1000;
316 else if(data->set.connecttimeout)
317 timeout = data->set.connecttimeout*1000;
319 timeout = DEFAULT_CONNECT_TIMEOUT;
321 Curl_nonblock(sock, TRUE);
323 /* wait until socket gets connected */
324 result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);
327 failf(conn->data, "SOCKS5: no connection here");
328 return CURLE_COULDNT_CONNECT;
330 else if(0 == result) {
331 failf(conn->data, "SOCKS5: connection timeout");
332 return CURLE_OPERATION_TIMEDOUT;
335 if(result & CSELECT_ERR) {
336 failf(conn->data, "SOCKS5: error occured during connection");
337 return CURLE_COULDNT_CONNECT;
340 socksreq[0] = 5; /* version */
341 socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
342 socksreq[2] = 0; /* no authentication */
343 socksreq[3] = 2; /* username/password */
345 Curl_nonblock(sock, FALSE);
347 code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
349 if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
350 failf(data, "Unable to send initial SOCKS5 request.");
351 return CURLE_COULDNT_CONNECT;
354 Curl_nonblock(sock, TRUE);
356 result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);
359 failf(conn->data, "SOCKS5 nothing to read");
360 return CURLE_COULDNT_CONNECT;
362 else if(0 == result) {
363 failf(conn->data, "SOCKS5 read timeout");
364 return CURLE_OPERATION_TIMEDOUT;
367 if(result & CSELECT_ERR) {
368 failf(conn->data, "SOCKS5 read error occured");
369 return CURLE_RECV_ERROR;
372 Curl_nonblock(sock, FALSE);
374 result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
375 if ((result != CURLE_OK) || (actualread != 2)) {
376 failf(data, "Unable to receive initial SOCKS5 response.");
377 return CURLE_COULDNT_CONNECT;
380 if (socksreq[0] != 5) {
381 failf(data, "Received invalid version in initial SOCKS5 response.");
382 return CURLE_COULDNT_CONNECT;
384 if (socksreq[1] == 0) {
385 /* Nothing to do, no authentication needed */
388 else if (socksreq[1] == 2) {
389 /* Needs user name and password */
390 size_t userlen, pwlen;
392 if(proxy_name && proxy_password) {
393 userlen = strlen(proxy_name);
394 pwlen = proxy_password?strlen(proxy_password):0;
401 /* username/password request looks like
402 * +----+------+----------+------+----------+
403 * |VER | ULEN | UNAME | PLEN | PASSWD |
404 * +----+------+----------+------+----------+
405 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
406 * +----+------+----------+------+----------+
409 socksreq[len++] = 1; /* username/pw subnegotiation version */
410 socksreq[len++] = (char) userlen;
411 memcpy(socksreq + len, proxy_name, (int) userlen);
413 socksreq[len++] = (char) pwlen;
414 memcpy(socksreq + len, proxy_password, (int) pwlen);
417 code = Curl_write(conn, sock, (char *)socksreq, len, &written);
418 if ((code != CURLE_OK) || (len != written)) {
419 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
420 return CURLE_COULDNT_CONNECT;
423 result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
425 if ((result != CURLE_OK) || (actualread != 2)) {
426 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
427 return CURLE_COULDNT_CONNECT;
430 /* ignore the first (VER) byte */
431 if (socksreq[1] != 0) { /* status */
432 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
433 socksreq[0], socksreq[1]);
434 return CURLE_COULDNT_CONNECT;
437 /* Everything is good so far, user was authenticated! */
441 if (socksreq[1] == 1) {
443 "SOCKS5 GSSAPI per-message authentication is not supported.");
444 return CURLE_COULDNT_CONNECT;
446 else if (socksreq[1] == 255) {
447 if (!proxy_name || !*proxy_name) {
449 "No authentication method was acceptable. (It is quite likely"
450 " that the SOCKS5 server wanted a username/password, since none"
451 " was supplied to the server on this connection.)");
454 failf(data, "No authentication method was acceptable.");
456 return CURLE_COULDNT_CONNECT;
460 "Undocumented SOCKS5 mode attempted to be used by server.");
461 return CURLE_COULDNT_CONNECT;
465 /* Authentication is complete, now specify destination to the proxy */
466 socksreq[0] = 5; /* version (SOCKS5) */
467 socksreq[1] = 1; /* connect */
468 socksreq[2] = 0; /* must be zero */
469 socksreq[3] = 1; /* IPv4 = 1 */
472 struct Curl_dns_entry *dns;
473 Curl_addrinfo *hp=NULL;
474 int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
476 if(rc == CURLRESOLV_ERROR)
477 return CURLE_COULDNT_RESOLVE_HOST;
479 if(rc == CURLRESOLV_PENDING)
480 /* this requires that we're in "wait for resolve" state */
481 rc = Curl_wait_for_resolv(conn, &dns);
484 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
485 * returns a Curl_addrinfo pointer that may not always look the same.
491 unsigned short ip[4];
492 Curl_printable_address(hp, buf, sizeof(buf));
494 if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
495 &ip[0], &ip[1], &ip[2], &ip[3])) {
496 socksreq[4] = (unsigned char)ip[0];
497 socksreq[5] = (unsigned char)ip[1];
498 socksreq[6] = (unsigned char)ip[2];
499 socksreq[7] = (unsigned char)ip[3];
502 hp = NULL; /* fail! */
504 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
507 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
509 return CURLE_COULDNT_RESOLVE_HOST;
513 *((unsigned short*)&socksreq[8]) = htons(conn->remote_port);
516 const int packetsize = 10;
518 code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
519 if ((code != CURLE_OK) || (written != packetsize)) {
520 failf(data, "Failed to send SOCKS5 connect request.");
524 result = blockread_all(conn, sock, (char *)socksreq, packetsize,
525 &actualread, timeout);
526 if ((result != CURLE_OK) || (actualread != packetsize)) {
527 failf(data, "Failed to receive SOCKS5 connect request ack.");
528 return CURLE_COULDNT_CONNECT;
531 if (socksreq[0] != 5) { /* version */
533 "SOCKS5 reply has wrong version, version should be 5.");
534 return CURLE_COULDNT_CONNECT;
536 if (socksreq[1] != 0) { /* Anything besides 0 is an error */
538 "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
539 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
540 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
541 (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
543 return CURLE_COULDNT_CONNECT;
547 Curl_nonblock(sock, TRUE);
548 return CURLE_OK; /* Proxy was successful! */