LCOV - code coverage report
Current view: top level - core/proto - proto_socks.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 442 487 90.8 %
Date: 2021-11-24 03:28:48 Functions: 17 17 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * @file proto_socks.c
       9             :  * @brief Implementations for SOCKS4 and SOCKS5 protocols.
      10             :  **/
      11             : 
      12             : #include "core/or/or.h"
      13             : #include "feature/client/addressmap.h"
      14             : #include "lib/buf/buffers.h"
      15             : #include "core/mainloop/connection.h"
      16             : #include "feature/control/control_events.h"
      17             : #include "app/config/config.h"
      18             : #include "lib/crypt_ops/crypto_util.h"
      19             : #include "feature/relay/ext_orport.h"
      20             : #include "core/proto/proto_socks.h"
      21             : #include "core/or/reasons.h"
      22             : 
      23             : #include "core/or/socks_request_st.h"
      24             : 
      25             : #include "trunnel/socks5.h"
      26             : 
      27             : #define SOCKS_VER_5 0x05 /* First octet of non-auth SOCKS5 messages */
      28             : #define SOCKS_VER_4 0x04 /*                SOCKS4 messages */
      29             : #define SOCKS_AUTH  0x01 /*                SOCKS5 auth messages */
      30             : 
      31             : typedef enum {
      32             :   SOCKS_RESULT_INVALID       = -1, /* Message invalid. */
      33             :   SOCKS_RESULT_TRUNCATED     =  0, /* Message incomplete/truncated. */
      34             :   SOCKS_RESULT_DONE          =  1, /* OK, we're done. */
      35             :   SOCKS_RESULT_MORE_EXPECTED =  2, /* OK, more messages expected. */
      36             : } socks_result_t;
      37             : 
      38             : static void socks_request_set_socks5_error(socks_request_t *req,
      39             :                               socks5_reply_status_t reason);
      40             : 
      41             : static socks_result_t parse_socks(const char *data,
      42             :                                   size_t datalen,
      43             :                                   socks_request_t *req,
      44             :                                   int log_sockstype,
      45             :                                   int safe_socks,
      46             :                                   size_t *drain_out);
      47             : static int parse_socks_client(const uint8_t *data, size_t datalen,
      48             :                               int state, char **reason,
      49             :                               ssize_t *drain_out);
      50             : /**
      51             :  * Wait this many seconds before warning the user about using SOCKS unsafely
      52             :  * again. */
      53             : #define SOCKS_WARN_INTERVAL 5
      54             : 
      55             : /** Warn that the user application has made an unsafe socks request using
      56             :  * protocol <b>socks_protocol</b> on port <b>port</b>.  Don't warn more than
      57             :  * once per SOCKS_WARN_INTERVAL, unless <b>safe_socks</b> is set. */
      58             : static void
      59           6 : log_unsafe_socks_warning(int socks_protocol, const char *address,
      60             :                          uint16_t port, int safe_socks)
      61             : {
      62           6 :   static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
      63             : 
      64           6 :   if (safe_socks) {
      65           1 :     log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP,
      66             :              "Your application (using socks%d to port %d) is giving "
      67             :              "Tor only an IP address. Applications that do DNS resolves "
      68             :              "themselves may leak information. Consider using Socks4A "
      69             :              "(e.g. via privoxy or socat) instead. For more information, "
      70             :              "please see https://2019.www.torproject.org/docs/faq.html.en"
      71             :              "#WarningsAboutSOCKSandDNSInformationLeaks.%s",
      72             :              socks_protocol,
      73             :              (int)port,
      74             :              safe_socks ? " Rejecting." : "");
      75             :   }
      76           6 :   control_event_client_status(LOG_WARN,
      77             :                               "DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d",
      78             :                               socks_protocol, address, (int)port);
      79           6 : }
      80             : 
      81             : /** Do not attempt to parse socks messages longer than this.  This value is
      82             :  * actually significantly higher than the longest possible socks message. */
      83             : #define MAX_SOCKS_MESSAGE_LEN 512
      84             : 
      85             : /** Return a new socks_request_t. */
      86             : socks_request_t *
      87        1008 : socks_request_new(void)
      88             : {
      89        1008 :   return tor_malloc_zero(sizeof(socks_request_t));
      90             : }
      91             : 
      92             : /** Free all storage held in the socks_request_t <b>req</b>. */
      93             : void
      94        1007 : socks_request_free_(socks_request_t *req)
      95             : {
      96        1007 :   if (!req)
      97             :     return;
      98        1007 :   if (req->username) {
      99           6 :     memwipe(req->username, 0x10, req->usernamelen);
     100           6 :     tor_free(req->username);
     101             :   }
     102        1007 :   if (req->password) {
     103           5 :     memwipe(req->password, 0x04, req->passwordlen);
     104           5 :     tor_free(req->password);
     105             :   }
     106        1007 :   memwipe(req, 0xCC, sizeof(socks_request_t));
     107        1007 :   tor_free(req);
     108             : }
     109             : 
     110             : /**
     111             :  * Parse a single SOCKS4 request from buffer <b>raw_data</b> of length
     112             :  * <b>datalen</b> and update relevant fields of <b>req</b>. If SOCKS4a
     113             :  * request is detected, set <b>*is_socks4a</b> to true. Set <b>*drain_out</b>
     114             :  * to number of bytes we parsed so far.
     115             :  *
     116             :  * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if
     117             :  * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it
     118             :  * failed due to incomplete (truncated) input.
     119             :  */
     120             : static socks_result_t
     121          53 : parse_socks4_request(const uint8_t *raw_data, socks_request_t *req,
     122             :                      size_t datalen, int *is_socks4a, size_t *drain_out)
     123             : {
     124             :   // http://ss5.sourceforge.net/socks4.protocol.txt
     125             :   // http://ss5.sourceforge.net/socks4A.protocol.txt
     126          53 :   socks_result_t res = SOCKS_RESULT_DONE;
     127          53 :   tor_addr_t destaddr;
     128             : 
     129          53 :   tor_assert(is_socks4a);
     130          53 :   tor_assert(drain_out);
     131             : 
     132          53 :   *is_socks4a = 0;
     133          53 :   *drain_out = 0;
     134             : 
     135          53 :   req->socks_version = SOCKS_VER_4;
     136             : 
     137          53 :   socks4_client_request_t *trunnel_req;
     138             : 
     139          53 :   ssize_t parsed =
     140          53 :   socks4_client_request_parse(&trunnel_req, raw_data, datalen);
     141             : 
     142          53 :   if (parsed == -1) {
     143           0 :     log_warn(LD_APP, "socks4: parsing failed - invalid request.");
     144           0 :     res = SOCKS_RESULT_INVALID;
     145           0 :     goto end;
     146          53 :   } else if (parsed == -2) {
     147          45 :     res = SOCKS_RESULT_TRUNCATED;
     148          45 :     if (datalen >= MAX_SOCKS_MESSAGE_LEN) {
     149           2 :       log_warn(LD_APP, "socks4: parsing failed - invalid request.");
     150           2 :       res = SOCKS_RESULT_INVALID;
     151             :     }
     152          45 :     goto end;
     153             :   }
     154             : 
     155           8 :   tor_assert(parsed >= 0);
     156           8 :   *drain_out = (size_t)parsed;
     157             : 
     158           8 :   uint8_t command = socks4_client_request_get_command(trunnel_req);
     159           8 :   req->command = command;
     160             : 
     161           8 :   req->port = socks4_client_request_get_port(trunnel_req);
     162           8 :   uint32_t dest_ip = socks4_client_request_get_addr(trunnel_req);
     163             : 
     164           8 :   if ((!req->port && req->command != SOCKS_COMMAND_RESOLVE) ||
     165             :       dest_ip == 0) {
     166           2 :     log_warn(LD_APP, "socks4: Port or DestIP is zero. Rejecting.");
     167           2 :     res = SOCKS_RESULT_INVALID;
     168           2 :     goto end;
     169             :   }
     170             : 
     171           6 :   *is_socks4a = (dest_ip >> 8) == 0;
     172             : 
     173           6 :   const char *username = socks4_client_request_get_username(trunnel_req);
     174           6 :   const size_t usernamelen = username ? strlen(username) : 0;
     175           6 :   if (username && usernamelen) {
     176           2 :     if (usernamelen > MAX_SOCKS_MESSAGE_LEN) {
     177           0 :       log_warn(LD_APP, "Socks4 user name too long; rejecting.");
     178           0 :       res = SOCKS_RESULT_INVALID;
     179           0 :       goto end;
     180             :     }
     181             : 
     182           2 :     tor_free(req->username);
     183           2 :     req->got_auth = 1;
     184           2 :     req->username = tor_strdup(username);
     185           2 :     req->usernamelen = usernamelen;
     186             :   }
     187             : 
     188           6 :   if (*is_socks4a) {
     189             :     // We cannot rely on trunnel here, as we want to detect if
     190             :     // we have abnormally long hostname field.
     191           3 :     const char *hostname = (char *)raw_data + SOCKS4_NETWORK_LEN +
     192           3 :      usernamelen + 1;
     193           3 :     size_t hostname_len = (char *)raw_data + datalen - hostname;
     194             : 
     195           3 :     if (hostname_len <= sizeof(req->address)) {
     196           2 :       const char *trunnel_hostname =
     197           2 :       socks4_client_request_get_socks4a_addr_hostname(trunnel_req);
     198             : 
     199           2 :       if (trunnel_hostname)
     200           2 :         strlcpy(req->address, trunnel_hostname, sizeof(req->address));
     201             :     } else {
     202           1 :       log_warn(LD_APP, "socks4: Destaddr too long. Rejecting.");
     203           1 :       res = SOCKS_RESULT_INVALID;
     204           1 :       goto end;
     205             :     }
     206             :   } else {
     207           3 :     tor_addr_from_ipv4h(&destaddr, dest_ip);
     208             : 
     209           3 :     if (!tor_addr_to_str(req->address, &destaddr,
     210             :                          MAX_SOCKS_ADDR_LEN, 0)) {
     211           0 :       res = SOCKS_RESULT_INVALID;
     212           0 :       goto end;
     213             :     }
     214             :   }
     215             : 
     216           3 :   end:
     217          53 :   socks4_client_request_free(trunnel_req);
     218             : 
     219          53 :   return res;
     220             : }
     221             : 
     222             : /**
     223             :  * Validate SOCKS4/4a related fields in <b>req</b>. Expect SOCKS4a
     224             :  * if <b>is_socks4a</b> is true. If <b>log_sockstype</b> is true,
     225             :  * log a notice about possible DNS leaks on local system. If
     226             :  * <b>safe_socks</b> is true, reject insecure usage of SOCKS
     227             :  * protocol.
     228             :  *
     229             :  * Return SOCKS_RESULT_DONE if validation passed or
     230             :  * SOCKS_RESULT_INVALID if it failed.
     231             :  */
     232             : static socks_result_t
     233           5 : process_socks4_request(const socks_request_t *req, int is_socks4a,
     234             :                        int log_sockstype, int safe_socks)
     235             : {
     236           5 :   if (is_socks4a && !addressmap_have_mapping(req->address, 0)) {
     237           2 :     log_unsafe_socks_warning(4, req->address, req->port, safe_socks);
     238             : 
     239           2 :     if (safe_socks)
     240             :       return SOCKS_RESULT_INVALID;
     241             :   }
     242             : 
     243           5 :   if (req->command != SOCKS_COMMAND_CONNECT &&
     244             :       req->command != SOCKS_COMMAND_RESOLVE) {
     245             :     /* not a connect or resolve? we don't support it. (No resolve_ptr with
     246             :      * socks4.) */
     247           1 :     log_warn(LD_APP, "socks4: command %d not recognized. Rejecting.",
     248             :              req->command);
     249           1 :     return SOCKS_RESULT_INVALID;
     250             :   }
     251             : 
     252           4 :   if (is_socks4a) {
     253           2 :     if (log_sockstype)
     254           2 :       log_notice(LD_APP,
     255             :                  "Your application (using socks4a to port %d) instructed "
     256             :                  "Tor to take care of the DNS resolution itself if "
     257             :                  "necessary. This is good.", req->port);
     258             :   }
     259             : 
     260           4 :   if (!string_is_valid_dest(req->address)) {
     261           1 :     log_warn(LD_PROTOCOL,
     262             :              "Your application (using socks4 to port %d) gave Tor "
     263             :              "a malformed hostname: %s. Rejecting the connection.",
     264             :              req->port, escaped_safe_str_client(req->address));
     265           1 :      return SOCKS_RESULT_INVALID;
     266             :   }
     267             : 
     268             :   return SOCKS_RESULT_DONE;
     269             : }
     270             : 
     271             : /** Parse a single SOCKS5 version identifier/method selection message
     272             :  * from buffer <b>raw_data</b> (of length <b>datalen</b>). Update
     273             :  * relevant fields of <b>req</b> (if any). Set <b>*have_user_pass</b> to
     274             :  * true if username/password method is found. Set <b>*have_no_auth</b>
     275             :  * if no-auth method is found. Set <b>*drain_out</b> to number of bytes
     276             :  * we parsed so far.
     277             :  *
     278             :  * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if
     279             :  * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it
     280             :  * failed due to incomplete (truncated) input.
     281             :  */
     282             : static socks_result_t
     283         872 : parse_socks5_methods_request(const uint8_t *raw_data, socks_request_t *req,
     284             :                              size_t datalen, int *have_user_pass,
     285             :                              int *have_no_auth, size_t *drain_out)
     286             : {
     287         872 :   socks_result_t res = SOCKS_RESULT_DONE;
     288         872 :   socks5_client_version_t *trunnel_req;
     289             : 
     290         872 :   ssize_t parsed = socks5_client_version_parse(&trunnel_req, raw_data,
     291             :                                                datalen);
     292             : 
     293         872 :   (void)req;
     294             : 
     295         872 :   tor_assert(have_no_auth);
     296         872 :   tor_assert(have_user_pass);
     297         872 :   tor_assert(drain_out);
     298             : 
     299         872 :   *drain_out = 0;
     300             : 
     301         872 :   if (parsed == -1) {
     302           0 :     log_warn(LD_APP, "socks5: parsing failed - invalid version "
     303             :                      "id/method selection message.");
     304           0 :     res = SOCKS_RESULT_INVALID;
     305           0 :     goto end;
     306         872 :   } else if (parsed == -2) {
     307           3 :     res = SOCKS_RESULT_TRUNCATED;
     308           3 :     if (datalen > MAX_SOCKS_MESSAGE_LEN) {
     309           0 :       log_warn(LD_APP, "socks5: parsing failed - invalid version "
     310             :                        "id/method selection message.");
     311           0 :       res = SOCKS_RESULT_INVALID;
     312             :     }
     313           3 :     goto end;
     314             :   }
     315             : 
     316         869 :   tor_assert(parsed >= 0);
     317         869 :   *drain_out = (size_t)parsed;
     318             : 
     319         869 :   size_t n_methods = (size_t)socks5_client_version_get_n_methods(trunnel_req);
     320         869 :   if (n_methods == 0) {
     321           0 :     res = SOCKS_RESULT_INVALID;
     322           0 :     goto end;
     323             :   }
     324             : 
     325         869 :   *have_no_auth = 0;
     326         869 :   *have_user_pass = 0;
     327             : 
     328        1742 :   for (size_t i = 0; i < n_methods; i++) {
     329         873 :     uint8_t method = socks5_client_version_get_methods(trunnel_req,
     330             :                                                        i);
     331             : 
     332         873 :     if (method == SOCKS_USER_PASS) {
     333         525 :       *have_user_pass = 1;
     334         348 :     } else if (method == SOCKS_NO_AUTH) {
     335         343 :       *have_no_auth = 1;
     336             :     }
     337             :   }
     338             : 
     339         869 :   end:
     340         872 :   socks5_client_version_free(trunnel_req);
     341             : 
     342         872 :   return res;
     343             : }
     344             : 
     345             : /**
     346             :  * Validate and respond to version identifier/method selection message
     347             :  * we parsed in parse_socks5_methods_request (corresponding to <b>req</b>
     348             :  * and having user/pass method if <b>have_user_pass</b> is true, no-auth
     349             :  * method if <b>have_no_auth</b> is true). Set <b>req->reply</b> to
     350             :  * an appropriate response (in SOCKS5 wire format).
     351             :  *
     352             :  * On success, return SOCKS_RESULT_DONE. On failure, return
     353             :  * SOCKS_RESULT_INVALID.
     354             :  */
     355             : static socks_result_t
     356         869 : process_socks5_methods_request(socks_request_t *req, int have_user_pass,
     357             :                                int have_no_auth)
     358             : {
     359         869 :   socks_result_t res = SOCKS_RESULT_DONE;
     360         869 :   socks5_server_method_t *trunnel_resp = socks5_server_method_new();
     361         869 :   tor_assert(trunnel_resp);
     362             : 
     363         869 :   socks5_server_method_set_version(trunnel_resp, SOCKS_VER_5);
     364             : 
     365         869 :   if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) {
     366         525 :     req->auth_type = SOCKS_USER_PASS;
     367         525 :     socks5_server_method_set_method(trunnel_resp, SOCKS_USER_PASS);
     368             : 
     369         525 :     req->socks_version = SOCKS_VER_5;
     370             :     // FIXME: come up with better way to remember
     371             :     // that we negotiated auth
     372             : 
     373         525 :     log_debug(LD_APP,"socks5: accepted method 2 (username/password)");
     374         344 :   } else if (have_no_auth) {
     375         343 :     req->auth_type = SOCKS_NO_AUTH;
     376         343 :     socks5_server_method_set_method(trunnel_resp, SOCKS_NO_AUTH);
     377             : 
     378         343 :     req->socks_version = SOCKS_VER_5;
     379             : 
     380         343 :     log_debug(LD_APP,"socks5: accepted method 0 (no authentication)");
     381             :   } else {
     382           1 :     log_warn(LD_APP,
     383             :              "socks5: offered methods don't include 'no auth' or "
     384             :              "username/password. Rejecting.");
     385           1 :     socks5_server_method_set_method(trunnel_resp, 0xFF); // reject all
     386           1 :     res = SOCKS_RESULT_INVALID;
     387             :   }
     388             : 
     389         869 :   const char *errmsg = socks5_server_method_check(trunnel_resp);
     390         869 :   if (errmsg) {
     391           0 :     log_warn(LD_APP, "socks5: method selection validation failed: %s",
     392             :              errmsg);
     393           0 :     res = SOCKS_RESULT_INVALID;
     394             :   } else {
     395         869 :     ssize_t encoded =
     396         869 :     socks5_server_method_encode(req->reply, sizeof(req->reply),
     397             :                                 trunnel_resp);
     398             : 
     399         869 :     if (encoded < 0) {
     400           0 :       log_warn(LD_APP, "socks5: method selection encoding failed");
     401           0 :       res = SOCKS_RESULT_INVALID;
     402             :     } else {
     403         869 :       req->replylen = (size_t)encoded;
     404             :     }
     405             :   }
     406             : 
     407         869 :   socks5_server_method_free(trunnel_resp);
     408         869 :   return res;
     409             : }
     410             : 
     411             : /**
     412             :  * Parse SOCKS5/RFC1929 username/password request from buffer
     413             :  * <b>raw_data</b> of length <b>datalen</b> and update relevant
     414             :  * fields of <b>req</b>. Set <b>*drain_out</b> to number of bytes
     415             :  * we parsed so far.
     416             :  *
     417             :  * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if
     418             :  * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it
     419             :  * failed due to incomplete (truncated) input.
     420             :  */
     421             : static socks_result_t
     422         520 : parse_socks5_userpass_auth(const uint8_t *raw_data, socks_request_t *req,
     423             :                            size_t datalen, size_t *drain_out)
     424             : {
     425         520 :   socks_result_t res = SOCKS_RESULT_DONE;
     426         520 :   socks5_client_userpass_auth_t *trunnel_req = NULL;
     427         520 :   ssize_t parsed = socks5_client_userpass_auth_parse(&trunnel_req, raw_data,
     428             :                                                      datalen);
     429         520 :   tor_assert(drain_out);
     430         520 :   *drain_out = 0;
     431             : 
     432         520 :   if (parsed == -1) {
     433           0 :     log_warn(LD_APP, "socks5: parsing failed - invalid user/pass "
     434             :                      "authentication message.");
     435           0 :     res = SOCKS_RESULT_INVALID;
     436           0 :     goto end;
     437         520 :   } else if (parsed == -2) {
     438         515 :     res = SOCKS_RESULT_TRUNCATED;
     439         515 :     goto end;
     440             :   }
     441             : 
     442           5 :   tor_assert(parsed >= 0);
     443           5 :   *drain_out = (size_t)parsed;
     444             : 
     445           5 :   uint8_t usernamelen =
     446           5 :    socks5_client_userpass_auth_get_username_len(trunnel_req);
     447           5 :   uint8_t passwordlen =
     448           5 :    socks5_client_userpass_auth_get_passwd_len(trunnel_req);
     449           5 :   const char *username =
     450           5 :    socks5_client_userpass_auth_getconstarray_username(trunnel_req);
     451           5 :   const char *password =
     452           5 :    socks5_client_userpass_auth_getconstarray_passwd(trunnel_req);
     453             : 
     454           5 :   if (usernamelen && username) {
     455           4 :     tor_free(req->username);
     456           4 :     req->username = tor_memdup_nulterm(username, usernamelen);
     457           4 :     req->usernamelen = usernamelen;
     458             :   }
     459             : 
     460           5 :   if (passwordlen && password) {
     461           4 :     tor_free(req->password);
     462           4 :     req->password = tor_memdup_nulterm(password, passwordlen);
     463           4 :     req->passwordlen = passwordlen;
     464             :   }
     465             : 
     466             :   /**
     467             :    * Yes, we allow username and/or password to be empty. Yes, that does
     468             :    * violate RFC 1929. However, some client software can send a username/
     469             :    * password message with these fields being empty and we want to allow them
     470             :    * to be used with Tor.
     471             :    */
     472           5 :   req->got_auth = 1;
     473             : 
     474         520 :   end:
     475         520 :   socks5_client_userpass_auth_free(trunnel_req);
     476         520 :   return res;
     477             : }
     478             : 
     479             : /**
     480             :  * Validate and respond to SOCKS5 username/password request we
     481             :  * parsed in parse_socks5_userpass_auth (corresponding to <b>req</b>.
     482             :  * Set <b>req->reply</b> to appropriate response. Return
     483             :  * SOCKS_RESULT_DONE on success or SOCKS_RESULT_INVALID on failure.
     484             :  */
     485             : static socks_result_t
     486           5 : process_socks5_userpass_auth(socks_request_t *req)
     487             : {
     488           5 :   socks_result_t res = SOCKS_RESULT_DONE;
     489           5 :   socks5_server_userpass_auth_t *trunnel_resp =
     490           5 :     socks5_server_userpass_auth_new();
     491           5 :   tor_assert(trunnel_resp);
     492             : 
     493           5 :   if (req->socks_version != SOCKS_VER_5) {
     494           1 :     res = SOCKS_RESULT_INVALID;
     495           1 :     goto end;
     496             :   }
     497             : 
     498           4 :   if (req->auth_type != SOCKS_USER_PASS &&
     499             :       req->auth_type != SOCKS_NO_AUTH) {
     500           0 :     res = SOCKS_RESULT_INVALID;
     501           0 :     goto end;
     502             :   }
     503             : 
     504           4 :   socks5_server_userpass_auth_set_version(trunnel_resp, SOCKS_AUTH);
     505           4 :   socks5_server_userpass_auth_set_status(trunnel_resp, 0); // auth OK
     506             : 
     507           4 :   const char *errmsg = socks5_server_userpass_auth_check(trunnel_resp);
     508           4 :   if (errmsg) {
     509           0 :     log_warn(LD_APP, "socks5: server userpass auth validation failed: %s",
     510             :              errmsg);
     511           0 :     res = SOCKS_RESULT_INVALID;
     512           0 :     goto end;
     513             :   }
     514             : 
     515           4 :   ssize_t encoded = socks5_server_userpass_auth_encode(req->reply,
     516             :                                                        sizeof(req->reply),
     517             :                                                        trunnel_resp);
     518             : 
     519           4 :   if (encoded < 0) {
     520           0 :     log_warn(LD_APP, "socks5: server userpass auth encoding failed");
     521           0 :     res = SOCKS_RESULT_INVALID;
     522           0 :     goto end;
     523             :   }
     524             : 
     525           4 :   req->replylen = (size_t)encoded;
     526             : 
     527           5 :   end:
     528           5 :   socks5_server_userpass_auth_free(trunnel_resp);
     529           5 :   return res;
     530             : }
     531             : 
     532             : /**
     533             :  * Parse a single SOCKS5 client request (RFC 1928 section 4) from buffer
     534             :  * <b>raw_data</b> of length <b>datalen</b> and update relevant field of
     535             :  * <b>req</b>. Set <b>*drain_out</b> to number of bytes we parsed so far.
     536             :  *
     537             :  * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if
     538             :  * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it
     539             :  * failed due to incomplete (truncated) input.
     540             :  */
     541             : static socks_result_t
     542         333 : parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req,
     543             :                             size_t datalen, size_t *drain_out)
     544             : {
     545         333 :   socks_result_t res = SOCKS_RESULT_DONE;
     546         333 :   tor_addr_t destaddr;
     547         333 :   socks5_client_request_t *trunnel_req = NULL;
     548         333 :   ssize_t parsed =
     549         333 :    socks5_client_request_parse(&trunnel_req, raw_data, datalen);
     550         333 :   if (parsed == -1) {
     551           1 :     log_warn(LD_APP, "socks5: parsing failed - invalid client request");
     552           1 :     res = SOCKS_RESULT_INVALID;
     553           1 :     socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
     554           1 :     goto end;
     555         332 :   } else if (parsed == -2) {
     556         315 :     res = SOCKS_RESULT_TRUNCATED;
     557         315 :     goto end;
     558             :   }
     559             : 
     560          17 :   tor_assert(parsed >= 0);
     561          17 :   *drain_out = (size_t)parsed;
     562             : 
     563          17 :   if (socks5_client_request_get_version(trunnel_req) != 5) {
     564           0 :     res = SOCKS_RESULT_INVALID;
     565           0 :     socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
     566           0 :     goto end;
     567             :   }
     568             : 
     569          17 :   req->command = socks5_client_request_get_command(trunnel_req);
     570             : 
     571          17 :   req->port = socks5_client_request_get_dest_port(trunnel_req);
     572             : 
     573          17 :   uint8_t atype = socks5_client_request_get_atype(trunnel_req);
     574          17 :   req->socks5_atyp = atype;
     575             : 
     576          17 :   switch (atype) {
     577           6 :     case 1: {
     578           6 :       uint32_t ipv4 = socks5_client_request_get_dest_addr_ipv4(trunnel_req);
     579           6 :       tor_addr_from_ipv4h(&destaddr, ipv4);
     580             : 
     581           6 :       tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1);
     582           6 :     } break;
     583           9 :     case 3: {
     584           9 :       const struct domainname_st *dns_name =
     585           9 :         socks5_client_request_getconst_dest_addr_domainname(trunnel_req);
     586             : 
     587           9 :       const char *hostname = domainname_getconstarray_name(dns_name);
     588             : 
     589           9 :       strlcpy(req->address, hostname, sizeof(req->address));
     590           9 :     } break;
     591           2 :     case 4: {
     592           2 :       const uint8_t *ipv6 =
     593           2 :           socks5_client_request_getarray_dest_addr_ipv6(trunnel_req);
     594           2 :       tor_addr_from_ipv6_bytes(&destaddr, ipv6);
     595             : 
     596           2 :       tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1);
     597           2 :     } break;
     598           0 :     default: {
     599           0 :       socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
     600           0 :       res = -1;
     601           0 :     } break;
     602             :   }
     603             : 
     604         333 :   end:
     605         333 :   socks5_client_request_free(trunnel_req);
     606         333 :   return res;
     607             : }
     608             : 
     609             : /**
     610             :  * Validate and respond to SOCKS5 request we parsed in
     611             :  * parse_socks5_client_request (corresponding to <b>req</b>.
     612             :  * Write appropriate response to <b>req->reply</b> (in
     613             :  * SOCKS5 wire format). If <b>log_sockstype</b> is true, log a
     614             :  * notice about possible DNS leaks on local system. If
     615             :  * <b>safe_socks</b> is true, disallow insecure usage of SOCKS
     616             :  * protocol. Return SOCKS_RESULT_DONE on success or
     617             :  * SOCKS_RESULT_INVALID on failure.
     618             :  */
     619             : static socks_result_t
     620          17 : process_socks5_client_request(socks_request_t *req,
     621             :                               int log_sockstype,
     622             :                               int safe_socks)
     623             : {
     624          17 :   socks_result_t res = SOCKS_RESULT_DONE;
     625          17 :   tor_addr_t tmpaddr;
     626             : 
     627          17 :   if (req->command != SOCKS_COMMAND_CONNECT &&
     628           6 :       req->command != SOCKS_COMMAND_RESOLVE &&
     629             :       req->command != SOCKS_COMMAND_RESOLVE_PTR) {
     630           2 :     socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED);
     631           2 :     res = SOCKS_RESULT_INVALID;
     632           2 :     goto end;
     633             :   }
     634             : 
     635          19 :   if (req->command == SOCKS_COMMAND_RESOLVE_PTR &&
     636           4 :       tor_addr_parse(&tmpaddr, req->address) < 0) {
     637           1 :     socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
     638           1 :     log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
     639             :                      "a malformed address. Rejecting.");
     640             : 
     641           1 :     res = SOCKS_RESULT_INVALID;
     642           1 :     goto end;
     643             :   }
     644             : 
     645          14 :   if (!string_is_valid_dest(req->address)) {
     646           2 :     socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
     647             : 
     648           2 :     log_warn(LD_PROTOCOL,
     649             :              "Your application (using socks5 to port %d) gave Tor "
     650             :              "a malformed hostname: %s. Rejecting the connection.",
     651             :              req->port, escaped_safe_str_client(req->address));
     652             : 
     653           2 :     res = SOCKS_RESULT_INVALID;
     654           2 :     goto end;
     655             :   }
     656             : 
     657          12 :   if (req->socks5_atyp == 1 || req->socks5_atyp == 4) {
     658          10 :     if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
     659           4 :         !addressmap_have_mapping(req->address,0)) {
     660           4 :       log_unsafe_socks_warning(5, req->address, req->port, safe_socks);
     661           4 :       if (safe_socks) {
     662           1 :         socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED);
     663           1 :         res = SOCKS_RESULT_INVALID;
     664           1 :         goto end;
     665             :       }
     666             :     }
     667             :   }
     668             : 
     669          11 :   if (log_sockstype)
     670           1 :     log_notice(LD_APP,
     671             :               "Your application (using socks5 to port %d) instructed "
     672             :               "Tor to take care of the DNS resolution itself if "
     673             :               "necessary. This is good.", req->port);
     674             : 
     675          10 :   end:
     676          17 :   return res;
     677             : }
     678             : 
     679             : /**
     680             :  * Handle (parse, validate, process, respond) a single SOCKS
     681             :  * message in buffer <b>raw_data</b> of length <b>datalen</b>.
     682             :  * Update relevant fields of <b>req</b>. If <b>log_sockstype</b>
     683             :  * is true, log a warning about possible DNS leaks on local
     684             :  * system. If <b>safe_socks</b> is true, disallow insecure
     685             :  * usage of SOCKS protocol. Set <b>*drain_out</b> to number
     686             :  * of bytes in <b>raw_data</b> that we processed so far and
     687             :  * that can be safely drained from buffer.
     688             :  *
     689             :  * Return:
     690             :  *  - SOCKS_RESULT_DONE if succeeded and not expecting further
     691             :  *    messages from client.
     692             :  *  - SOCKS_RESULT_INVALID if any of the steps failed due to
     693             :  *    request being invalid or unexpected given current state.
     694             :  *  - SOCKS_RESULT_TRUNCATED if we do not found an expected
     695             :  *    SOCKS message in its entirety (more stuff has to arrive
     696             :  *    from client).
     697             :  *  - SOCKS_RESULT_MORE_EXPECTED if we handled current message
     698             :  *    successfully, but we expect more messages from the
     699             :  *    client.
     700             :  */
     701             : static socks_result_t
     702        1802 : handle_socks_message(const uint8_t *raw_data, size_t datalen,
     703             :                      socks_request_t *req, int log_sockstype,
     704             :                      int safe_socks, size_t *drain_out)
     705             : {
     706        1802 :   socks_result_t res = SOCKS_RESULT_DONE;
     707             : 
     708        1802 :   uint8_t socks_version = raw_data[0];
     709             : 
     710        1802 :   if (socks_version == SOCKS_AUTH)
     711             :     socks_version = SOCKS_VER_5; // SOCKS5 username/pass subnegotiation
     712             : 
     713        1282 :   if (socks_version == SOCKS_VER_4) {
     714          77 :     if (datalen < SOCKS4_NETWORK_LEN) {
     715          24 :       res = 0;
     716          24 :       goto end;
     717             :     }
     718             : 
     719          53 :     int is_socks4a = 0;
     720          53 :     res = parse_socks4_request((const uint8_t *)raw_data, req, datalen,
     721             :                                &is_socks4a, drain_out);
     722             : 
     723          53 :     if (res != SOCKS_RESULT_DONE) {
     724          48 :       goto end;
     725             :     }
     726             : 
     727           5 :     res = process_socks4_request(req, is_socks4a,log_sockstype,
     728             :                                  safe_socks);
     729             : 
     730           5 :     if (res != SOCKS_RESULT_DONE) {
     731           2 :       goto end;
     732             :     }
     733             : 
     734           3 :     goto end;
     735        1725 :   } else if (socks_version == SOCKS_VER_5) {
     736        1725 :     if (datalen < 2) { /* version and another byte */
     737           0 :       res = 0;
     738           0 :       goto end;
     739             :     }
     740             :     /* RFC1929 SOCKS5 username/password subnegotiation. */
     741        1725 :     if (!req->got_auth && (raw_data[0] == 1 ||
     742        1204 :         req->auth_type == SOCKS_USER_PASS)) {
     743         520 :       res = parse_socks5_userpass_auth(raw_data, req, datalen,
     744             :                                        drain_out);
     745             : 
     746         520 :       if (res != SOCKS_RESULT_DONE) {
     747         515 :         goto end;
     748             :       }
     749             : 
     750           5 :       res = process_socks5_userpass_auth(req);
     751           5 :       if (res != SOCKS_RESULT_DONE) {
     752           1 :         goto end;
     753             :       }
     754             : 
     755           4 :       res = SOCKS_RESULT_MORE_EXPECTED;
     756           4 :       goto end;
     757        1205 :     } else if (req->socks_version != SOCKS_VER_5) {
     758         872 :       int have_user_pass=0, have_no_auth=0;
     759         872 :       res = parse_socks5_methods_request(raw_data, req, datalen,
     760             :                                          &have_user_pass,
     761             :                                          &have_no_auth,
     762             :                                          drain_out);
     763             : 
     764         872 :       if (res != SOCKS_RESULT_DONE) {
     765           3 :         goto end;
     766             :       }
     767             : 
     768         869 :       res = process_socks5_methods_request(req, have_user_pass,
     769             :                                            have_no_auth);
     770             : 
     771         869 :       if (res != SOCKS_RESULT_DONE) {
     772           1 :         goto end;
     773             :       }
     774             : 
     775         868 :       res = SOCKS_RESULT_MORE_EXPECTED;
     776         868 :       goto end;
     777             :     } else {
     778         333 :       res = parse_socks5_client_request(raw_data, req,
     779             :                                         datalen, drain_out);
     780         333 :       if (BUG(res == SOCKS_RESULT_INVALID && req->replylen == 0)) {
     781           0 :         socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
     782             :       }
     783         333 :       if (res != SOCKS_RESULT_DONE) {
     784         316 :         goto end;
     785             :       }
     786             : 
     787          17 :       res = process_socks5_client_request(req, log_sockstype,
     788             :                                           safe_socks);
     789             : 
     790          17 :       if (res != SOCKS_RESULT_DONE) {
     791           6 :         goto end;
     792             :       }
     793             :     }
     794             :   } else {
     795           0 :     *drain_out = datalen;
     796           0 :     res = SOCKS_RESULT_INVALID;
     797             :   }
     798             : 
     799        1802 :   end:
     800        1802 :   return res;
     801             : }
     802             : 
     803             : /** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
     804             :  * of the forms
     805             :  *  - socks4: "socksheader username\\0"
     806             :  *  - socks4a: "socksheader username\\0 destaddr\\0"
     807             :  *  - socks5 phase one: "version #methods methods"
     808             :  *  - socks5 phase two: "version command 0 addresstype..."
     809             :  * If it's a complete and valid handshake, and destaddr fits in
     810             :  *   MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
     811             :  *   assign to <b>req</b>, and return 1.
     812             :  *
     813             :  * If it's invalid or too big, return -1.
     814             :  *
     815             :  * Else it's not all there yet, leave buf alone and return 0.
     816             :  *
     817             :  * If you want to specify the socks reply, write it into <b>req->reply</b>
     818             :  *   and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
     819             :  *
     820             :  * If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
     821             :  * the connection is possibly leaking DNS requests locally or not.
     822             :  *
     823             :  * If <b>safe_socks</b> is true, then reject unsafe socks protocols.
     824             :  *
     825             :  * If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
     826             :  * undefined.
     827             :  */
     828             : int
     829        1817 : fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
     830             :                      int log_sockstype, int safe_socks)
     831             : {
     832        1817 :   int res = 0;
     833        1817 :   size_t datalen = buf_datalen(buf);
     834        1817 :   size_t n_drain;
     835        1817 :   const char *head = NULL;
     836        1817 :   socks_result_t socks_res;
     837        1817 :   size_t n_pullup;
     838             : 
     839        1817 :   if (buf_datalen(buf) < 2) { /* version and another byte */
     840          28 :     res = 0;
     841          28 :     goto end;
     842             :   }
     843             : 
     844        1804 :   do {
     845        1804 :     n_drain = 0;
     846        1804 :     n_pullup = MIN(MAX_SOCKS_MESSAGE_LEN, buf_datalen(buf));
     847        1804 :     buf_pullup(buf, n_pullup, &head, &datalen);
     848        1804 :     tor_assert(head && datalen >= 2);
     849             : 
     850        1804 :     socks_res = parse_socks(head, datalen, req, log_sockstype,
     851             :                             safe_socks, &n_drain);
     852             : 
     853        1804 :     if (socks_res == SOCKS_RESULT_INVALID)
     854          18 :       buf_clear(buf);
     855        1786 :     else if (socks_res != SOCKS_RESULT_TRUNCATED && n_drain > 0)
     856         886 :       buf_drain(buf, n_drain);
     857             : 
     858        1804 :     switch (socks_res) {
     859             :       case SOCKS_RESULT_INVALID:
     860          18 :         res = -1;
     861          18 :         break;
     862             :       case SOCKS_RESULT_DONE:
     863             :         res = 1;
     864             :         break;
     865         900 :       case SOCKS_RESULT_TRUNCATED:
     866         900 :         if (datalen == n_pullup)
     867             :           return 0;
     868             :         FALLTHROUGH;
     869             :       case SOCKS_RESULT_MORE_EXPECTED:
     870             :         res = 0;
     871             :         break;
     872             :     }
     873         890 :   } while (res == 0 && head && buf_datalen(buf) >= 2);
     874             : 
     875         857 :   end:
     876             :   return res;
     877             : }
     878             : 
     879             : /** Create a SOCKS5 reply message with <b>reason</b> in its REP field and
     880             :  * have Tor send it as error response to <b>req</b>.
     881             :  */
     882             : static void
     883           7 : socks_request_set_socks5_error(socks_request_t *req,
     884             :                   socks5_reply_status_t reason)
     885             : {
     886           7 :   socks5_server_reply_t *trunnel_resp = socks5_server_reply_new();
     887           7 :   tor_assert(trunnel_resp);
     888             : 
     889           7 :   socks5_server_reply_set_version(trunnel_resp, SOCKS_VER_5);
     890           7 :   socks5_server_reply_set_reply(trunnel_resp, reason);
     891           7 :   socks5_server_reply_set_atype(trunnel_resp, 0x01);
     892             : 
     893           7 :   const char *errmsg = socks5_server_reply_check(trunnel_resp);
     894           7 :   if (errmsg) {
     895           0 :     log_warn(LD_APP, "socks5: reply validation failed: %s",
     896             :              errmsg);
     897           0 :     goto end;
     898             :   }
     899             : 
     900           7 :   ssize_t encoded = socks5_server_reply_encode(req->reply,
     901             :                                                sizeof(req->reply),
     902             :                                                trunnel_resp);
     903           7 :   if (encoded < 0) {
     904           0 :     log_warn(LD_APP, "socks5: reply encoding failed: %d",
     905             :              (int)encoded);
     906             :   } else {
     907           7 :     req->replylen = (size_t)encoded;
     908             :   }
     909             : 
     910           7 :   end:
     911           7 :   socks5_server_reply_free(trunnel_resp);
     912           7 : }
     913             : 
     914             : static const char SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG[] =
     915             :   "HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
     916             :   "Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
     917             :   "<html>\n"
     918             :   "<head>\n"
     919             :   "<title>This is a SOCKS Proxy, Not An HTTP Proxy</title>\n"
     920             :   "</head>\n"
     921             :   "<body>\n"
     922             :   "<h1>This is a SOCKs proxy, not an HTTP proxy.</h1>\n"
     923             :   "<p>\n"
     924             :   "It appears you have configured your web browser to use this Tor port as\n"
     925             :   "an HTTP proxy.\n"
     926             :   "</p><p>\n"
     927             :   "This is not correct: This port is configured as a SOCKS proxy, not\n"
     928             :   "an HTTP proxy. If you need an HTTP proxy tunnel, use the HTTPTunnelPort\n"
     929             :   "configuration option in place of, or in addition to, SOCKSPort.\n"
     930             :   "Please configure your client accordingly.\n"
     931             :   "</p>\n"
     932             :   "<p>\n"
     933             :   "See <a href=\"https://www.torproject.org/documentation.html\">"
     934             :   "https://www.torproject.org/documentation.html</a> for more "
     935             :   "information.\n"
     936             :   "</p>\n"
     937             :   "</body>\n"
     938             :   "</html>\n";
     939             : 
     940             : /** Implementation helper to implement fetch_from_*_socks.  Instead of looking
     941             :  * at a buffer's contents, we look at the <b>datalen</b> bytes of data in
     942             :  * <b>data</b>. Instead of removing data from the buffer, we set
     943             :  * <b>drain_out</b> to the amount of data that should be removed (or -1 if the
     944             :  * buffer should be cleared).  Instead of pulling more data into the first
     945             :  * chunk of the buffer, we set *<b>want_length_out</b> to the number of bytes
     946             :  * we'd like to see in the input buffer, if they're available. */
     947             : static int
     948        1804 : parse_socks(const char *data, size_t datalen, socks_request_t *req,
     949             :             int log_sockstype, int safe_socks, size_t *drain_out)
     950             : {
     951        1804 :   uint8_t first_octet;
     952             : 
     953        1804 :   if (datalen < 2) {
     954             :     /* We always need at least 2 bytes. */
     955             :     return 0;
     956             :   }
     957             : 
     958        1804 :   first_octet = get_uint8(data);
     959             : 
     960        1804 :   if (first_octet == SOCKS_VER_5 || first_octet == SOCKS_VER_4 ||
     961        1804 :       first_octet == SOCKS_AUTH) { // XXX: RFC 1929
     962        1802 :     return handle_socks_message((const uint8_t *)data, datalen, req,
     963             :                                 log_sockstype, safe_socks, drain_out);
     964             :   }
     965             : 
     966           2 :   switch (first_octet) { /* which version of socks? */
     967           1 :     case 'G': /* get */
     968             :     case 'H': /* head */
     969             :     case 'P': /* put/post */
     970             :     case 'C': /* connect */
     971           1 :       strlcpy((char*)req->reply, SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG,
     972             :               MAX_SOCKS_REPLY_LEN);
     973           1 :       req->replylen = strlen((char*)req->reply)+1;
     974           2 :       FALLTHROUGH;
     975           2 :     default: /* version is not socks4 or socks5 */
     976           2 :       log_warn(LD_APP,
     977             :                "Socks version %d not recognized. (This port is not an "
     978             :                "HTTP proxy; did you want to use HTTPTunnelPort?)",
     979             :                *(data));
     980             :       {
     981             :         /* Tell the controller the first 8 bytes. */
     982           2 :         char *tmp = tor_strndup(data, datalen < 8 ? datalen : 8);
     983           2 :         control_event_client_status(LOG_WARN,
     984             :                                     "SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"",
     985             :                                     escaped(tmp));
     986           2 :         tor_free(tmp);
     987             :       }
     988           2 :       return -1;
     989             :   }
     990             : 
     991             :   tor_assert_unreached();
     992             :   return -1;
     993             : }
     994             : 
     995             : /** Inspect a reply from SOCKS server stored in <b>buf</b> according
     996             :  * to <b>state</b>, removing the protocol data upon success. Return 0 on
     997             :  * incomplete response, 1 on success and -1 on error, in which case
     998             :  * <b>reason</b> is set to a descriptive message (free() when finished
     999             :  * with it).
    1000             :  *
    1001             :  * As a special case, 2 is returned when user/pass is required
    1002             :  * during SOCKS5 handshake and user/pass is configured.
    1003             :  */
    1004             : int
    1005         122 : fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
    1006             : {
    1007         122 :   ssize_t drain = 0;
    1008         122 :   int r;
    1009         122 :   const char *head = NULL;
    1010         122 :   size_t datalen = 0;
    1011             : 
    1012         122 :   if (buf_datalen(buf) < 2)
    1013             :     return 0;
    1014             : 
    1015         102 :   buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, &head, &datalen);
    1016         102 :   tor_assert(head && datalen >= 2);
    1017             : 
    1018         102 :   r = parse_socks_client((uint8_t*)head, datalen,
    1019             :                          state, reason, &drain);
    1020         102 :   if (drain > 0)
    1021           4 :     buf_drain(buf, drain);
    1022          98 :   else if (drain < 0)
    1023           4 :     buf_clear(buf);
    1024             : 
    1025             :   return r;
    1026             : }
    1027             : 
    1028             : /** Implementation logic for fetch_from_*_socks_client. */
    1029             : static int
    1030         102 : parse_socks_client(const uint8_t *data, size_t datalen,
    1031             :                    int state, char **reason,
    1032             :                    ssize_t *drain_out)
    1033             : {
    1034         102 :   unsigned int addrlen;
    1035         102 :   *drain_out = 0;
    1036         102 :   if (datalen < 2)
    1037             :     return 0;
    1038             : 
    1039         102 :   switch (state) {
    1040          14 :     case PROXY_SOCKS4_WANT_CONNECT_OK:
    1041             :       /* Wait for the complete response */
    1042          14 :       if (datalen < 8)
    1043             :         return 0;
    1044             : 
    1045           2 :       if (data[1] != 0x5a) {
    1046           1 :         *reason = tor_strdup(socks4_response_code_to_string(data[1]));
    1047           1 :         return -1;
    1048             :       }
    1049             : 
    1050             :       /* Success */
    1051           1 :       *drain_out = 8;
    1052           1 :       return 1;
    1053             : 
    1054           2 :     case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
    1055             :       /* we don't have any credentials */
    1056           2 :       if (data[1] != 0x00) {
    1057           1 :         *reason = tor_strdup("server doesn't support any of our "
    1058             :                              "available authentication methods");
    1059           1 :         return -1;
    1060             :       }
    1061             : 
    1062           1 :       log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
    1063           1 :       *drain_out = -1;
    1064           1 :       return 1;
    1065             : 
    1066           3 :     case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
    1067             :       /* we have a username and password. return 1 if we can proceed without
    1068             :        * providing authentication, or 2 otherwise. */
    1069           3 :       switch (data[1]) {
    1070           1 :         case 0x00:
    1071           1 :           log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
    1072             :                             "doesn't require authentication.");
    1073           1 :           *drain_out = -1;
    1074           1 :           return 1;
    1075           1 :         case 0x02:
    1076           1 :           log_info(LD_NET, "SOCKS 5 client: need authentication.");
    1077           1 :           *drain_out = -1;
    1078           1 :           return 2;
    1079           1 :         default:
    1080             :           /* This wasn't supposed to be exhaustive; there are other
    1081             :            * authentication methods too. */
    1082           1 :           ;
    1083             :       }
    1084             : 
    1085           1 :       *reason = tor_strdup("server doesn't support any of our available "
    1086             :                            "authentication methods");
    1087           1 :       return -1;
    1088             : 
    1089           2 :     case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
    1090             :       /* handle server reply to rfc1929 authentication */
    1091           2 :       if (data[1] != 0x00) {
    1092           1 :         *reason = tor_strdup("authentication failed");
    1093           1 :         return -1;
    1094             :       }
    1095             : 
    1096           1 :       log_info(LD_NET, "SOCKS 5 client: authentication successful.");
    1097           1 :       *drain_out = -1;
    1098           1 :       return 1;
    1099             : 
    1100          81 :     case PROXY_SOCKS5_WANT_CONNECT_OK:
    1101             :       /* response is variable length. BND.ADDR, etc, isn't needed
    1102             :        * (don't bother with buf_pullup()), but make sure to eat all
    1103             :        * the data used */
    1104             : 
    1105             :       /* wait for address type field to arrive */
    1106          81 :       if (datalen < 4)
    1107             :         return 0;
    1108             : 
    1109          71 :       switch (data[3]) {
    1110             :         case 0x01: /* ip4 */
    1111             :           addrlen = 4;
    1112             :           break;
    1113          19 :         case 0x04: /* ip6 */
    1114          19 :           addrlen = 16;
    1115          19 :           break;
    1116          44 :         case 0x03: /* fqdn (can this happen here?) */
    1117          44 :           if (datalen < 5)
    1118             :             return 0;
    1119          42 :           addrlen = 1 + data[4];
    1120          42 :           break;
    1121           1 :         default:
    1122           1 :           *reason = tor_strdup("invalid response to connect request");
    1123           1 :           return -1;
    1124             :       }
    1125             : 
    1126             :       /* wait for address and port */
    1127          68 :       if (datalen < 6 + addrlen)
    1128             :         return 0;
    1129             : 
    1130           4 :       if (data[1] != 0x00) {
    1131           1 :         *reason = tor_strdup(socks5_response_code_to_string(data[1]));
    1132           1 :         return -1;
    1133             :       }
    1134             : 
    1135           3 :       *drain_out = 6 + addrlen;
    1136           3 :       return 1;
    1137             :   }
    1138             : 
    1139             :   /* LCOV_EXCL_START */
    1140             :   /* shouldn't get here if the input state is one we know about... */
    1141             :   tor_assert(0);
    1142             : 
    1143             :   return -1;
    1144             :   /* LCOV_EXCL_STOP */
    1145             : }

Generated by: LCOV version 1.14