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>