File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / socks.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    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>