Annotation of embedaddon/curl/lib/socks.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
9: *
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 https://curl.haxx.se/docs/copyright.html.
13: *
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.
17: *
18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19: * KIND, either express or implied.
20: *
21: ***************************************************************************/
22:
23: #include "curl_setup.h"
24:
25: #if !defined(CURL_DISABLE_PROXY)
26:
27: #ifdef HAVE_NETINET_IN_H
28: #include <netinet/in.h>
29: #endif
30: #ifdef HAVE_ARPA_INET_H
31: #include <arpa/inet.h>
32: #endif
33:
34: #include "urldata.h"
35: #include "sendf.h"
36: #include "select.h"
37: #include "connect.h"
38: #include "timeval.h"
39: #include "socks.h"
40: #include "multiif.h" /* for getsock macros */
41:
42: /* The last 3 #include files should be in this order */
43: #include "curl_printf.h"
44: #include "curl_memory.h"
45: #include "memdebug.h"
46:
47: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
48: /*
49: * Helper read-from-socket functions. Does the same as Curl_read() but it
50: * blocks until all bytes amount of buffersize will be read. No more, no less.
51: *
52: * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
53: */
54: int Curl_blockread_all(struct connectdata *conn, /* connection data */
55: curl_socket_t sockfd, /* read from this socket */
56: char *buf, /* store read data here */
57: ssize_t buffersize, /* max amount to read */
58: ssize_t *n) /* amount bytes read */
59: {
60: ssize_t nread = 0;
61: ssize_t allread = 0;
62: int result;
63: *n = 0;
64: for(;;) {
65: timediff_t timeout_ms = Curl_timeleft(conn->data, NULL, TRUE);
66: if(timeout_ms < 0) {
67: /* we already got the timeout */
68: result = CURLE_OPERATION_TIMEDOUT;
69: break;
70: }
71: if(!timeout_ms)
72: timeout_ms = TIME_T_MAX;
73: if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
74: result = ~CURLE_OK;
75: break;
76: }
77: result = Curl_read_plain(sockfd, buf, buffersize, &nread);
78: if(CURLE_AGAIN == result)
79: continue;
80: if(result)
81: break;
82:
83: if(buffersize == nread) {
84: allread += nread;
85: *n = allread;
86: result = CURLE_OK;
87: break;
88: }
89: if(!nread) {
90: result = ~CURLE_OK;
91: break;
92: }
93:
94: buffersize -= nread;
95: buf += nread;
96: allread += nread;
97: }
98: return result;
99: }
100: #endif
101:
102: #ifndef DEBUGBUILD
103: #define sxstate(x,y) socksstate(x,y)
104: #else
105: #define sxstate(x,y) socksstate(x,y, __LINE__)
106: #endif
107:
108:
109: /* always use this function to change state, to make debugging easier */
110: static void socksstate(struct connectdata *conn,
111: enum connect_t state
112: #ifdef DEBUGBUILD
113: , int lineno
114: #endif
115: )
116: {
117: enum connect_t oldstate = conn->cnnct.state;
118: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
119: /* synced with the state list in urldata.h */
120: static const char * const statename[] = {
121: "INIT",
122: "SOCKS_INIT",
123: "SOCKS_SEND",
124: "SOCKS_READ_INIT",
125: "SOCKS_READ",
126: "GSSAPI_INIT",
127: "AUTH_INIT",
128: "AUTH_SEND",
129: "AUTH_READ",
130: "REQ_INIT",
131: "RESOLVING",
132: "RESOLVED",
133: "RESOLVE_REMOTE",
134: "REQ_SEND",
135: "REQ_SENDING",
136: "REQ_READ",
137: "REQ_READ_MORE",
138: "DONE"
139: };
140: #endif
141:
142: if(oldstate == state)
143: /* don't bother when the new state is the same as the old state */
144: return;
145:
146: conn->cnnct.state = state;
147:
148: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
149: infof(conn->data,
150: "SXSTATE: %s => %s conn %p; line %d\n",
151: statename[oldstate], statename[conn->cnnct.state], conn,
152: lineno);
153: #endif
154: }
155:
156: int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
157: int sockindex)
158: {
159: int rc = 0;
160: sock[0] = conn->sock[sockindex];
161: switch(conn->cnnct.state) {
162: case CONNECT_RESOLVING:
163: case CONNECT_SOCKS_READ:
164: case CONNECT_AUTH_READ:
165: case CONNECT_REQ_READ:
166: case CONNECT_REQ_READ_MORE:
167: rc = GETSOCK_READSOCK(0);
168: break;
169: default:
170: rc = GETSOCK_WRITESOCK(0);
171: break;
172: }
173: return rc;
174: }
175:
176: /*
177: * This function logs in to a SOCKS4 proxy and sends the specifics to the final
178: * destination server.
179: *
180: * Reference :
181: * https://www.openssh.com/txt/socks4.protocol
182: *
183: * Note :
184: * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
185: * Nonsupport "Identification Protocol (RFC1413)"
186: */
187: CURLcode Curl_SOCKS4(const char *proxy_user,
188: const char *hostname,
189: int remote_port,
190: int sockindex,
191: struct connectdata *conn,
192: bool *done)
193: {
194: const bool protocol4a =
195: (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
196: unsigned char *socksreq = &conn->cnnct.socksreq[0];
197: CURLcode result;
198: curl_socket_t sockfd = conn->sock[sockindex];
199: struct Curl_easy *data = conn->data;
200: struct connstate *sx = &conn->cnnct;
201: struct Curl_dns_entry *dns = NULL;
202: ssize_t actualread;
203: ssize_t written;
204:
205: if(!SOCKS_STATE(sx->state) && !*done)
206: sxstate(conn, CONNECT_SOCKS_INIT);
207:
208: switch(sx->state) {
209: case CONNECT_SOCKS_INIT:
210: /* SOCKS4 can only do IPv4, insist! */
211: conn->ip_version = CURL_IPRESOLVE_V4;
212: if(conn->bits.httpproxy)
213: infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
214: protocol4a ? "a" : "", hostname, remote_port);
215:
216: infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
217:
218: /*
219: * Compose socks4 request
220: *
221: * Request format
222: *
223: * +----+----+----+----+----+----+----+----+----+----+....+----+
224: * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
225: * +----+----+----+----+----+----+----+----+----+----+....+----+
226: * # of bytes: 1 1 2 4 variable 1
227: */
228:
229: socksreq[0] = 4; /* version (SOCKS4) */
230: socksreq[1] = 1; /* connect */
231: socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
232: socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
233:
234: /* DNS resolve only for SOCKS4, not SOCKS4a */
235: if(!protocol4a) {
236: enum resolve_t rc =
237: Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
238:
239: if(rc == CURLRESOLV_ERROR)
240: return CURLE_COULDNT_RESOLVE_PROXY;
241: else if(rc == CURLRESOLV_PENDING) {
242: sxstate(conn, CONNECT_RESOLVING);
243: infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname);
244: return CURLE_OK;
245: }
246: sxstate(conn, CONNECT_RESOLVED);
247: goto CONNECT_RESOLVED;
248: }
249:
250: /* socks4a doesn't resolve anything locally */
251: sxstate(conn, CONNECT_REQ_INIT);
252: goto CONNECT_REQ_INIT;
253:
254: case CONNECT_RESOLVING:
255: /* check if we have the name resolved by now */
256: dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
257:
258: if(dns) {
259: #ifdef CURLRES_ASYNCH
260: conn->async.dns = dns;
261: conn->async.done = TRUE;
262: #endif
263: infof(data, "Hostname '%s' was found\n", hostname);
264: sxstate(conn, CONNECT_RESOLVED);
265: }
266: else {
267: result = Curl_resolv_check(data->conn, &dns);
268: if(!dns)
269: return result;
270: }
271: /* FALLTHROUGH */
272: CONNECT_RESOLVED:
273: case CONNECT_RESOLVED: {
274: Curl_addrinfo *hp = NULL;
275: char buf[64];
276: /*
277: * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
278: * returns a Curl_addrinfo pointer that may not always look the same.
279: */
280: if(dns)
281: hp = dns->addr;
282: if(hp) {
283: Curl_printable_address(hp, buf, sizeof(buf));
284:
285: if(hp->ai_family == AF_INET) {
286: struct sockaddr_in *saddr_in;
287:
288: saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
289: socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
290: socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
291: socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
292: socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
293:
294: infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf);
295: }
296: else {
297: hp = NULL; /* fail! */
298: failf(data, "SOCKS4 connection to %s not supported\n", buf);
299: }
300:
301: Curl_resolv_unlock(data, dns); /* not used anymore from now on */
302: }
303: if(!hp) {
304: failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
305: hostname);
306: return CURLE_COULDNT_RESOLVE_HOST;
307: }
308: }
309: /* FALLTHROUGH */
310: CONNECT_REQ_INIT:
311: case CONNECT_REQ_INIT:
312: /*
313: * This is currently not supporting "Identification Protocol (RFC1413)".
314: */
315: socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
316: if(proxy_user) {
317: size_t plen = strlen(proxy_user);
318: if(plen >= sizeof(sx->socksreq) - 8) {
319: failf(data, "Too long SOCKS proxy name, can't use!\n");
320: return CURLE_COULDNT_CONNECT;
321: }
322: /* copy the proxy name WITH trailing zero */
323: memcpy(socksreq + 8, proxy_user, plen + 1);
324: }
325:
326: /*
327: * Make connection
328: */
329: {
330: ssize_t packetsize = 9 +
331: strlen((char *)socksreq + 8); /* size including NUL */
332:
333: /* If SOCKS4a, set special invalid IP address 0.0.0.x */
334: if(protocol4a) {
335: ssize_t hostnamelen = 0;
336: socksreq[4] = 0;
337: socksreq[5] = 0;
338: socksreq[6] = 0;
339: socksreq[7] = 1;
340: /* append hostname */
341: hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
342: if(hostnamelen <= 255)
343: strcpy((char *)socksreq + packetsize, hostname);
344: else {
345: failf(data, "SOCKS4: too long host name");
346: return CURLE_COULDNT_CONNECT;
347: }
348: packetsize += hostnamelen;
349: }
350: sx->outp = socksreq;
351: sx->outstanding = packetsize;
352: sxstate(conn, CONNECT_REQ_SENDING);
353: }
354: /* FALLTHROUGH */
355: case CONNECT_REQ_SENDING:
356: /* Send request */
357: result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
358: sx->outstanding, &written);
359: if(result && (CURLE_AGAIN != result)) {
360: failf(data, "Failed to send SOCKS4 connect request.");
361: return CURLE_COULDNT_CONNECT;
362: }
363: if(written != sx->outstanding) {
364: /* not done, remain in state */
365: sx->outstanding -= written;
366: sx->outp += written;
367: return CURLE_OK;
368: }
369:
370: /* done sending! */
371: sx->outstanding = 8; /* receive data size */
372: sx->outp = socksreq;
373: sxstate(conn, CONNECT_SOCKS_READ);
374:
375: /* FALLTHROUGH */
376: case CONNECT_SOCKS_READ:
377: /* Receive response */
378: result = Curl_read_plain(sockfd, (char *)sx->outp,
379: sx->outstanding, &actualread);
380: if(result && (CURLE_AGAIN != result)) {
381: failf(data, "SOCKS4: Failed receiving connect request ack: %s",
382: curl_easy_strerror(result));
383: return CURLE_COULDNT_CONNECT;
384: }
385: else if(actualread != sx->outstanding) {
386: /* remain in reading state */
387: sx->outstanding -= actualread;
388: sx->outp += actualread;
389: return CURLE_OK;
390: }
391: sxstate(conn, CONNECT_DONE);
392: break;
393: default: /* lots of unused states in SOCKS4 */
394: break;
395: }
396:
397: /*
398: * Response format
399: *
400: * +----+----+----+----+----+----+----+----+
401: * | VN | CD | DSTPORT | DSTIP |
402: * +----+----+----+----+----+----+----+----+
403: * # of bytes: 1 1 2 4
404: *
405: * VN is the version of the reply code and should be 0. CD is the result
406: * code with one of the following values:
407: *
408: * 90: request granted
409: * 91: request rejected or failed
410: * 92: request rejected because SOCKS server cannot connect to
411: * identd on the client
412: * 93: request rejected because the client program and identd
413: * report different user-ids
414: */
415:
416: /* wrong version ? */
417: if(socksreq[0] != 0) {
418: failf(data,
419: "SOCKS4 reply has wrong version, version should be 0.");
420: return CURLE_COULDNT_CONNECT;
421: }
422:
423: /* Result */
424: switch(socksreq[1]) {
425: case 90:
426: infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
427: break;
428: case 91:
429: failf(data,
430: "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
431: ", request rejected or failed.",
432: (unsigned char)socksreq[4], (unsigned char)socksreq[5],
433: (unsigned char)socksreq[6], (unsigned char)socksreq[7],
434: (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
435: (unsigned char)socksreq[1]);
436: return CURLE_COULDNT_CONNECT;
437: case 92:
438: failf(data,
439: "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
440: ", request rejected because SOCKS server cannot connect to "
441: "identd on the client.",
442: (unsigned char)socksreq[4], (unsigned char)socksreq[5],
443: (unsigned char)socksreq[6], (unsigned char)socksreq[7],
444: (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
445: (unsigned char)socksreq[1]);
446: return CURLE_COULDNT_CONNECT;
447: case 93:
448: failf(data,
449: "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
450: ", request rejected because the client program and identd "
451: "report different user-ids.",
452: (unsigned char)socksreq[4], (unsigned char)socksreq[5],
453: (unsigned char)socksreq[6], (unsigned char)socksreq[7],
454: (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
455: (unsigned char)socksreq[1]);
456: return CURLE_COULDNT_CONNECT;
457: default:
458: failf(data,
459: "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
460: ", Unknown.",
461: (unsigned char)socksreq[4], (unsigned char)socksreq[5],
462: (unsigned char)socksreq[6], (unsigned char)socksreq[7],
463: (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
464: (unsigned char)socksreq[1]);
465: return CURLE_COULDNT_CONNECT;
466: }
467:
468: *done = TRUE;
469: return CURLE_OK; /* Proxy was successful! */
470: }
471:
472: /*
473: * This function logs in to a SOCKS5 proxy and sends the specifics to the final
474: * destination server.
475: */
476: CURLcode Curl_SOCKS5(const char *proxy_user,
477: const char *proxy_password,
478: const char *hostname,
479: int remote_port,
480: int sockindex,
481: struct connectdata *conn,
482: bool *done)
483: {
484: /*
485: According to the RFC1928, section "6. Replies". This is what a SOCK5
486: replies:
487:
488: +----+-----+-------+------+----------+----------+
489: |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
490: +----+-----+-------+------+----------+----------+
491: | 1 | 1 | X'00' | 1 | Variable | 2 |
492: +----+-----+-------+------+----------+----------+
493:
494: Where:
495:
496: o VER protocol version: X'05'
497: o REP Reply field:
498: o X'00' succeeded
499: */
500: unsigned char *socksreq = &conn->cnnct.socksreq[0];
501: char dest[256] = "unknown"; /* printable hostname:port */
502: int idx;
503: ssize_t actualread;
504: ssize_t written;
505: CURLcode result;
506: curl_socket_t sockfd = conn->sock[sockindex];
507: struct Curl_easy *data = conn->data;
508: bool socks5_resolve_local =
509: (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
510: const size_t hostname_len = strlen(hostname);
511: ssize_t len = 0;
512: const unsigned long auth = data->set.socks5auth;
513: bool allow_gssapi = FALSE;
514: struct connstate *sx = &conn->cnnct;
515: struct Curl_dns_entry *dns = NULL;
516:
517: if(!SOCKS_STATE(sx->state) && !*done)
518: sxstate(conn, CONNECT_SOCKS_INIT);
519:
520: switch(sx->state) {
521: case CONNECT_SOCKS_INIT:
522: if(conn->bits.httpproxy)
523: infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
524: hostname, remote_port);
525:
526: /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
527: if(!socks5_resolve_local && hostname_len > 255) {
528: infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
529: "length > 255 [actual len=%zu]\n", hostname_len);
530: socks5_resolve_local = TRUE;
531: }
532:
533: if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
534: infof(conn->data,
535: "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
536: auth);
537: if(!(auth & CURLAUTH_BASIC))
538: /* disable username/password auth */
539: proxy_user = NULL;
540: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
541: if(auth & CURLAUTH_GSSAPI)
542: allow_gssapi = TRUE;
543: #endif
544:
545: idx = 0;
546: socksreq[idx++] = 5; /* version */
547: idx++; /* number of authentication methods */
548: socksreq[idx++] = 0; /* no authentication */
549: if(allow_gssapi)
550: socksreq[idx++] = 1; /* GSS-API */
551: if(proxy_user)
552: socksreq[idx++] = 2; /* username/password */
553: /* write the number of authentication methods */
554: socksreq[1] = (unsigned char) (idx - 2);
555:
556: result = Curl_write_plain(conn, sockfd, (char *)socksreq, idx, &written);
557: if(result && (CURLE_AGAIN != result)) {
558: failf(data, "Unable to send initial SOCKS5 request.");
559: return CURLE_COULDNT_CONNECT;
560: }
561: if(written != idx) {
562: sxstate(conn, CONNECT_SOCKS_SEND);
563: sx->outstanding = idx - written;
564: sx->outp = &socksreq[written];
565: return CURLE_OK;
566: }
567: sxstate(conn, CONNECT_SOCKS_READ);
568: goto CONNECT_SOCKS_READ_INIT;
569: case CONNECT_SOCKS_SEND:
570: result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
571: sx->outstanding, &written);
572: if(result && (CURLE_AGAIN != result)) {
573: failf(data, "Unable to send initial SOCKS5 request.");
574: return CURLE_COULDNT_CONNECT;
575: }
576: if(written != sx->outstanding) {
577: /* not done, remain in state */
578: sx->outstanding -= written;
579: sx->outp += written;
580: return CURLE_OK;
581: }
582: /* FALLTHROUGH */
583: CONNECT_SOCKS_READ_INIT:
584: case CONNECT_SOCKS_READ_INIT:
585: sx->outstanding = 2; /* expect two bytes */
586: sx->outp = socksreq; /* store it here */
587: /* FALLTHROUGH */
588: case CONNECT_SOCKS_READ:
589: result = Curl_read_plain(sockfd, (char *)sx->outp,
590: sx->outstanding, &actualread);
591: if(result && (CURLE_AGAIN != result)) {
592: failf(data, "Unable to receive initial SOCKS5 response.");
593: return CURLE_COULDNT_CONNECT;
594: }
595: else if(actualread != sx->outstanding) {
596: /* remain in reading state */
597: sx->outstanding -= actualread;
598: sx->outp += actualread;
599: return CURLE_OK;
600: }
601: else if(socksreq[0] != 5) {
602: failf(data, "Received invalid version in initial SOCKS5 response.");
603: return CURLE_COULDNT_CONNECT;
604: }
605: else if(socksreq[1] == 0) {
606: /* DONE! No authentication needed. Send request. */
607: sxstate(conn, CONNECT_REQ_INIT);
608: goto CONNECT_REQ_INIT;
609: }
610: else if(socksreq[1] == 2) {
611: /* regular name + password authentication */
612: sxstate(conn, CONNECT_AUTH_INIT);
613: goto CONNECT_AUTH_INIT;
614: }
615: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
616: else if(allow_gssapi && (socksreq[1] == 1)) {
617: sxstate(conn, CONNECT_GSSAPI_INIT);
618: result = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
619: if(result) {
620: failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
621: return CURLE_COULDNT_CONNECT;
622: }
623: }
624: #endif
625: else {
626: /* error */
627: if(!allow_gssapi && (socksreq[1] == 1)) {
628: failf(data,
629: "SOCKS5 GSSAPI per-message authentication is not supported.");
630: return CURLE_COULDNT_CONNECT;
631: }
632: else if(socksreq[1] == 255) {
633: failf(data, "No authentication method was acceptable.");
634: return CURLE_COULDNT_CONNECT;
635: }
636: failf(data,
637: "Undocumented SOCKS5 mode attempted to be used by server.");
638: return CURLE_COULDNT_CONNECT;
639: }
640: break;
641: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
642: case CONNECT_GSSAPI_INIT:
643: /* GSSAPI stuff done non-blocking */
644: break;
645: #endif
646:
647: default: /* do nothing! */
648: break;
649:
650: CONNECT_AUTH_INIT:
651: case CONNECT_AUTH_INIT: {
652: /* Needs user name and password */
653: size_t proxy_user_len, proxy_password_len;
654: if(proxy_user && proxy_password) {
655: proxy_user_len = strlen(proxy_user);
656: proxy_password_len = strlen(proxy_password);
657: }
658: else {
659: proxy_user_len = 0;
660: proxy_password_len = 0;
661: }
662:
663: /* username/password request looks like
664: * +----+------+----------+------+----------+
665: * |VER | ULEN | UNAME | PLEN | PASSWD |
666: * +----+------+----------+------+----------+
667: * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
668: * +----+------+----------+------+----------+
669: */
670: len = 0;
671: socksreq[len++] = 1; /* username/pw subnegotiation version */
672: socksreq[len++] = (unsigned char) proxy_user_len;
673: if(proxy_user && proxy_user_len) {
674: /* the length must fit in a single byte */
675: if(proxy_user_len >= 255) {
676: failf(data, "Excessive user name length for proxy auth");
677: return CURLE_BAD_FUNCTION_ARGUMENT;
678: }
679: memcpy(socksreq + len, proxy_user, proxy_user_len);
680: }
681: len += proxy_user_len;
682: socksreq[len++] = (unsigned char) proxy_password_len;
683: if(proxy_password && proxy_password_len) {
684: /* the length must fit in a single byte */
685: if(proxy_password_len > 255) {
686: failf(data, "Excessive password length for proxy auth");
687: return CURLE_BAD_FUNCTION_ARGUMENT;
688: }
689: memcpy(socksreq + len, proxy_password, proxy_password_len);
690: }
691: len += proxy_password_len;
692: sxstate(conn, CONNECT_AUTH_SEND);
693: sx->outstanding = len;
694: sx->outp = socksreq;
695: }
696: /* FALLTHROUGH */
697: case CONNECT_AUTH_SEND:
698: result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
699: sx->outstanding, &written);
700: if(result && (CURLE_AGAIN != result)) {
701: failf(data, "Failed to send SOCKS5 sub-negotiation request.");
702: return CURLE_COULDNT_CONNECT;
703: }
704: if(sx->outstanding != written) {
705: /* remain in state */
706: sx->outstanding -= written;
707: sx->outp += written;
708: return CURLE_OK;
709: }
710: sx->outp = socksreq;
711: sx->outstanding = 2;
712: sxstate(conn, CONNECT_AUTH_READ);
713: /* FALLTHROUGH */
714: case CONNECT_AUTH_READ:
715: result = Curl_read_plain(sockfd, (char *)sx->outp,
716: sx->outstanding, &actualread);
717: if(result && (CURLE_AGAIN != result)) {
718: failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
719: return CURLE_COULDNT_CONNECT;
720: }
721: if(actualread != sx->outstanding) {
722: /* remain in state */
723: sx->outstanding -= actualread;
724: sx->outp += actualread;
725: return CURLE_OK;
726: }
727:
728: /* ignore the first (VER) byte */
729: if(socksreq[1] != 0) { /* status */
730: failf(data, "User was rejected by the SOCKS5 server (%d %d).",
731: socksreq[0], socksreq[1]);
732: return CURLE_COULDNT_CONNECT;
733: }
734:
735: /* Everything is good so far, user was authenticated! */
736: sxstate(conn, CONNECT_REQ_INIT);
737: /* FALLTHROUGH */
738: CONNECT_REQ_INIT:
739: case CONNECT_REQ_INIT:
740: if(socks5_resolve_local) {
741: enum resolve_t rc = Curl_resolv(conn, hostname, remote_port,
742: FALSE, &dns);
743:
744: if(rc == CURLRESOLV_ERROR)
745: return CURLE_COULDNT_RESOLVE_HOST;
746:
747: if(rc == CURLRESOLV_PENDING) {
748: sxstate(conn, CONNECT_RESOLVING);
749: return CURLE_OK;
750: }
751: sxstate(conn, CONNECT_RESOLVED);
752: goto CONNECT_RESOLVED;
753: }
754: goto CONNECT_RESOLVE_REMOTE;
755:
756: case CONNECT_RESOLVING:
757: /* check if we have the name resolved by now */
758: dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
759:
760: if(dns) {
761: #ifdef CURLRES_ASYNCH
762: conn->async.dns = dns;
763: conn->async.done = TRUE;
764: #endif
765: infof(data, "SOCKS5: hostname '%s' found\n", hostname);
766: }
767:
768: if(!dns) {
769: result = Curl_resolv_check(data->conn, &dns);
770: if(!dns)
771: return result;
772: }
773: /* FALLTHROUGH */
774: CONNECT_RESOLVED:
775: case CONNECT_RESOLVED: {
776: Curl_addrinfo *hp = NULL;
777: if(dns)
778: hp = dns->addr;
779: if(!hp) {
780: failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
781: hostname);
782: return CURLE_COULDNT_RESOLVE_HOST;
783: }
784:
785: if(Curl_printable_address(hp, dest, sizeof(dest))) {
786: size_t destlen = strlen(dest);
787: msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
788: }
789: else {
790: strcpy(dest, "unknown");
791: }
792:
793: len = 0;
794: socksreq[len++] = 5; /* version (SOCKS5) */
795: socksreq[len++] = 1; /* connect */
796: socksreq[len++] = 0; /* must be zero */
797: if(hp->ai_family == AF_INET) {
798: int i;
799: struct sockaddr_in *saddr_in;
800: socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
801:
802: saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
803: for(i = 0; i < 4; i++) {
804: socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
805: }
806:
807: infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
808: }
809: #ifdef ENABLE_IPV6
810: else if(hp->ai_family == AF_INET6) {
811: int i;
812: struct sockaddr_in6 *saddr_in6;
813: socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
814:
815: saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
816: for(i = 0; i < 16; i++) {
817: socksreq[len++] =
818: ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
819: }
820:
821: infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
822: }
823: #endif
824: else {
825: hp = NULL; /* fail! */
826: failf(data, "SOCKS5 connection to %s not supported\n", dest);
827: }
828:
829: Curl_resolv_unlock(data, dns); /* not used anymore from now on */
830: goto CONNECT_REQ_SEND;
831: }
832: CONNECT_RESOLVE_REMOTE:
833: case CONNECT_RESOLVE_REMOTE:
834: /* Authentication is complete, now specify destination to the proxy */
835: len = 0;
836: socksreq[len++] = 5; /* version (SOCKS5) */
837: socksreq[len++] = 1; /* connect */
838: socksreq[len++] = 0; /* must be zero */
839:
840: if(!socks5_resolve_local) {
841: socksreq[len++] = 3; /* ATYP: domain name = 3 */
842: socksreq[len++] = (char) hostname_len; /* one byte address length */
843: memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
844: len += hostname_len;
845: infof(data, "SOCKS5 connect to %s:%d (remotely resolved)\n",
846: hostname, remote_port);
847: }
848: /* FALLTHROUGH */
849:
850: CONNECT_REQ_SEND:
851: case CONNECT_REQ_SEND:
852: /* PORT MSB */
853: socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
854: /* PORT LSB */
855: socksreq[len++] = (unsigned char)(remote_port & 0xff);
856:
857: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
858: if(conn->socks5_gssapi_enctype) {
859: failf(data, "SOCKS5 GSS-API protection not yet implemented.");
860: return CURLE_COULDNT_CONNECT;
861: }
862: #endif
863: sx->outp = socksreq;
864: sx->outstanding = len;
865: sxstate(conn, CONNECT_REQ_SENDING);
866: /* FALLTHROUGH */
867: case CONNECT_REQ_SENDING:
868: result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
869: sx->outstanding, &written);
870: if(result && (CURLE_AGAIN != result)) {
871: failf(data, "Failed to send SOCKS5 connect request.");
872: return CURLE_COULDNT_CONNECT;
873: }
874: if(sx->outstanding != written) {
875: /* remain in state */
876: sx->outstanding -= written;
877: sx->outp += written;
878: return CURLE_OK;
879: }
880: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
881: if(conn->socks5_gssapi_enctype) {
882: failf(data, "SOCKS5 GSS-API protection not yet implemented.");
883: return CURLE_COULDNT_CONNECT;
884: }
885: #endif
886: sx->outstanding = 10; /* minimum packet size is 10 */
887: sx->outp = socksreq;
888: sxstate(conn, CONNECT_REQ_READ);
889: /* FALLTHROUGH */
890: case CONNECT_REQ_READ:
891: result = Curl_read_plain(sockfd, (char *)sx->outp,
892: sx->outstanding, &actualread);
893: if(result && (CURLE_AGAIN != result)) {
894: failf(data, "Failed to receive SOCKS5 connect request ack.");
895: return CURLE_COULDNT_CONNECT;
896: }
897: else if(actualread != sx->outstanding) {
898: /* remain in state */
899: sx->outstanding -= actualread;
900: sx->outp += actualread;
901: return CURLE_OK;
902: }
903:
904: if(socksreq[0] != 5) { /* version */
905: failf(data,
906: "SOCKS5 reply has wrong version, version should be 5.");
907: return CURLE_COULDNT_CONNECT;
908: }
909: else if(socksreq[1] != 0) { /* Anything besides 0 is an error */
910: failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
911: hostname, (unsigned char)socksreq[1]);
912: return CURLE_COULDNT_CONNECT;
913: }
914:
915: /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
916: 1928, so the reply packet should be read until the end to avoid errors
917: at subsequent protocol level.
918:
919: +----+-----+-------+------+----------+----------+
920: |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
921: +----+-----+-------+------+----------+----------+
922: | 1 | 1 | X'00' | 1 | Variable | 2 |
923: +----+-----+-------+------+----------+----------+
924:
925: ATYP:
926: o IP v4 address: X'01', BND.ADDR = 4 byte
927: o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
928: o IP v6 address: X'04', BND.ADDR = 16 byte
929: */
930:
931: /* Calculate real packet size */
932: if(socksreq[3] == 3) {
933: /* domain name */
934: int addrlen = (int) socksreq[4];
935: len = 5 + addrlen + 2;
936: }
937: else if(socksreq[3] == 4) {
938: /* IPv6 */
939: len = 4 + 16 + 2;
940: }
941:
942: /* At this point we already read first 10 bytes */
943: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
944: if(!conn->socks5_gssapi_enctype) {
945: /* decrypt_gssapi_blockread already read the whole packet */
946: #endif
947: if(len > 10) {
948: sx->outstanding = len - 10; /* get the rest */
949: sx->outp = &socksreq[10];
950: sxstate(conn, CONNECT_REQ_READ_MORE);
951: }
952: else {
953: sxstate(conn, CONNECT_DONE);
954: break;
955: }
956: #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
957: }
958: #endif
959: /* FALLTHROUGH */
960: case CONNECT_REQ_READ_MORE:
961: result = Curl_read_plain(sockfd, (char *)sx->outp,
962: sx->outstanding, &actualread);
963: if(result && (CURLE_AGAIN != result)) {
964: failf(data, "Failed to receive SOCKS5 connect request ack.");
965: return CURLE_COULDNT_CONNECT;
966: }
967: if(actualread != sx->outstanding) {
968: /* remain in state */
969: sx->outstanding -= actualread;
970: sx->outp += actualread;
971: return CURLE_OK;
972: }
973: sxstate(conn, CONNECT_DONE);
974: }
975: infof(data, "SOCKS5 request granted.\n");
976:
977: *done = TRUE;
978: return CURLE_OK; /* Proxy was successful! */
979: }
980:
981: #endif /* CURL_DISABLE_PROXY */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>