Annotation of embedaddon/curl/lib/socks.c, revision 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>