Annotation of embedaddon/curl/lib/socks.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>