123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
- * Copyright (c) 2007-2019, The Tor Project, Inc.
- */
- /* See LICENSE for licensing information */
- #include "orconfig.h"
- #include "lib/arch/bytes.h"
- #include "lib/log/log.h"
- #include "lib/malloc/malloc.h"
- #include "lib/net/address.h"
- #include "lib/net/resolve.h"
- #include "lib/net/socket.h"
- #include "lib/sandbox/sandbox.h"
- #include "lib/string/util_string.h"
- #include "lib/net/socks5_status.h"
- #include "trunnel/socks5.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #ifdef HAVE_NETINET_IN_H
- #include <netinet/in.h>
- #endif
- #ifdef HAVE_ARPA_INET_H
- #include <arpa/inet.h>
- #endif
- #ifdef HAVE_SYS_SOCKET_H
- #include <sys/socket.h>
- #endif
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h> /* Must be included before sys/stat.h for Ultrix */
- #endif
- #ifdef HAVE_ERRNO_H
- #include <errno.h>
- #endif
- #ifdef _WIN32
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #endif
- #define RESPONSE_LEN_4 8
- #define log_sock_error(act, _s) \
- STMT_BEGIN log_fn(LOG_ERR, LD_NET, "Error while %s: %s", act, \
- tor_socket_strerror(tor_socket_errno(_s))); STMT_END
- static void usage(void) ATTR_NORETURN;
- /**
- * Set <b>out</b> to a pointer to newly allocated buffer containing
- * SOCKS4a RESOLVE request with <b>username</b> and <b>hostname</b>.
- * Return number of bytes in the buffer if succeeded or -1 if failed.
- */
- static ssize_t
- build_socks4a_resolve_request(uint8_t **out,
- const char *username,
- const char *hostname)
- {
- tor_assert(out);
- tor_assert(username);
- tor_assert(hostname);
- const char *errmsg = NULL;
- uint8_t *output = NULL;
- socks4_client_request_t *rq = socks4_client_request_new();
- socks4_client_request_set_version(rq, 4);
- socks4_client_request_set_command(rq, CMD_RESOLVE);
- socks4_client_request_set_port(rq, 0);
- socks4_client_request_set_addr(rq, 0x00000001u);
- socks4_client_request_set_username(rq, username);
- socks4_client_request_set_socks4a_addr_hostname(rq, hostname);
- errmsg = socks4_client_request_check(rq);
- if (errmsg) {
- goto cleanup;
- }
- ssize_t encoded_len = socks4_client_request_encoded_len(rq);
- if (encoded_len <= 0) {
- errmsg = "socks4_client_request_encoded_len failed";
- goto cleanup;
- }
- output = tor_malloc(encoded_len);
- memset(output, 0, encoded_len);
- encoded_len = socks4_client_request_encode(output, encoded_len, rq);
- if (encoded_len <= 0) {
- errmsg = "socks4_client_request_encode failed";
- goto cleanup;
- }
- *out = output;
- cleanup:
- socks4_client_request_free(rq);
- if (errmsg) {
- log_err(LD_NET, "build_socks4a_resolve_request failed: %s", errmsg);
- *out = NULL;
- tor_free(output);
- }
- return errmsg ? -1 : encoded_len;
- }
- #define SOCKS5_ATYPE_HOSTNAME 0x03
- #define SOCKS5_ATYPE_IPV4 0x01
- #define SOCKS5_ATYPE_IPV6 0x04
- /**
- * Set <b>out</b> to pointer to newly allocated buffer containing
- * SOCKS5 RESOLVE/RESOLVE_PTR request with given <b>hostname<b>.
- * Generate a reverse request if <b>reverse</b> is true.
- * Return the number of bytes in the buffer if succeeded or -1 if failed.
- */
- static ssize_t
- build_socks5_resolve_request(uint8_t **out,
- const char *hostname,
- int reverse)
- {
- const char *errmsg = NULL;
- uint8_t *outbuf = NULL;
- int is_ip_address;
- tor_addr_t addr;
- size_t addrlen;
- int ipv6;
- is_ip_address = tor_addr_parse(&addr, hostname) != -1;
- if (!is_ip_address && reverse) {
- log_err(LD_GENERAL, "Tried to do a reverse lookup on a non-IP!");
- return -1;
- }
- ipv6 = reverse && tor_addr_family(&addr) == AF_INET6;
- addrlen = reverse ? (ipv6 ? 16 : 4) : 1 + strlen(hostname);
- if (addrlen > UINT8_MAX) {
- log_err(LD_GENERAL, "Hostname is too long!");
- return -1;
- }
- socks5_client_request_t *rq = socks5_client_request_new();
- socks5_client_request_set_version(rq, 5);
- /* RESOLVE_PTR or RESOLVE */
- socks5_client_request_set_command(rq, reverse ? CMD_RESOLVE_PTR :
- CMD_RESOLVE);
- socks5_client_request_set_reserved(rq, 0);
- uint8_t atype = SOCKS5_ATYPE_HOSTNAME;
- if (reverse)
- atype = ipv6 ? SOCKS5_ATYPE_IPV6 : SOCKS5_ATYPE_IPV4;
- socks5_client_request_set_atype(rq, atype);
- switch (atype) {
- case SOCKS5_ATYPE_IPV4: {
- socks5_client_request_set_dest_addr_ipv4(rq,
- tor_addr_to_ipv4h(&addr));
- } break;
- case SOCKS5_ATYPE_IPV6: {
- uint8_t *ipv6_array =
- socks5_client_request_getarray_dest_addr_ipv6(rq);
- tor_assert(ipv6_array);
- memcpy(ipv6_array, tor_addr_to_in6_addr8(&addr), 16);
- } break;
- case SOCKS5_ATYPE_HOSTNAME: {
- domainname_t *dn = domainname_new();
- domainname_set_len(dn, addrlen - 1);
- domainname_setlen_name(dn, addrlen - 1);
- char *dn_buf = domainname_getarray_name(dn);
- memcpy(dn_buf, hostname, addrlen - 1);
- errmsg = domainname_check(dn);
- if (errmsg) {
- domainname_free(dn);
- goto cleanup;
- } else {
- socks5_client_request_set_dest_addr_domainname(rq, dn);
- }
- } break;
- default:
- tor_assert_unreached();
- break;
- }
- socks5_client_request_set_dest_port(rq, 0);
- errmsg = socks5_client_request_check(rq);
- if (errmsg) {
- goto cleanup;
- }
- ssize_t encoded_len = socks5_client_request_encoded_len(rq);
- if (encoded_len < 0) {
- errmsg = "Cannot predict encoded length";
- goto cleanup;
- }
- outbuf = tor_malloc(encoded_len);
- memset(outbuf, 0, encoded_len);
- encoded_len = socks5_client_request_encode(outbuf, encoded_len, rq);
- if (encoded_len < 0) {
- errmsg = "encoding failed";
- goto cleanup;
- }
- *out = outbuf;
- cleanup:
- socks5_client_request_free(rq);
- if (errmsg) {
- tor_free(outbuf);
- log_err(LD_NET, "build_socks5_resolve_request failed with error: %s",
- errmsg);
- }
- return errmsg ? -1 : encoded_len;
- }
- /** Set *<b>out</b> to a newly allocated SOCKS4a resolve request with
- * <b>username</b> and <b>hostname</b> as provided. Return the number
- * of bytes in the request. */
- static ssize_t
- build_socks_resolve_request(uint8_t **out,
- const char *username,
- const char *hostname,
- int reverse,
- int version)
- {
- tor_assert(out);
- tor_assert(username);
- tor_assert(hostname);
- tor_assert(version == 4 || version == 5);
- if (version == 4) {
- return build_socks4a_resolve_request(out, username, hostname);
- } else if (version == 5) {
- return build_socks5_resolve_request(out, hostname, reverse);
- }
- tor_assert_unreached();
- return -1;
- }
- static void
- onion_warning(const char *hostname)
- {
- log_warn(LD_NET,
- "%s is a hidden service; those don't have IP addresses. "
- "You can use the AutomapHostsOnResolve option to have Tor return a "
- "fake address for hidden services. Or you can have your "
- "application send the address to Tor directly; we recommend an "
- "application that uses SOCKS 5 with hostnames.",
- hostname);
- }
- /** Given a <b>len</b>-byte SOCKS4a response in <b>response</b>, set
- * *<b>addr_out</b> to the address it contains (in host order).
- * Return 0 on success, -1 on error.
- */
- static int
- parse_socks4a_resolve_response(const char *hostname,
- const char *response, size_t len,
- tor_addr_t *addr_out)
- {
- int result = 0;
- uint8_t status;
- tor_assert(response);
- tor_assert(addr_out);
- socks4_server_reply_t *reply;
- ssize_t parsed = socks4_server_reply_parse(&reply,
- (const uint8_t *)response,
- len);
- if (parsed == -1) {
- log_warn(LD_PROTOCOL, "Failed parsing SOCKS4a response");
- result = -1; goto cleanup;
- }
- if (parsed == -2) {
- log_warn(LD_PROTOCOL,"Truncated socks response.");
- result = -1; goto cleanup;
- }
- if (socks4_server_reply_get_version(reply) != 0) { /* version: 0 */
- log_warn(LD_PROTOCOL,"Nonzero version in socks response: bad format.");
- result = -1; goto cleanup;
- }
- if (socks4_server_reply_get_port(reply) != 0) { /* port: 0 */
- log_warn(LD_PROTOCOL,"Nonzero port in socks response: bad format.");
- result = -1; goto cleanup;
- }
- status = socks4_server_reply_get_status(reply);
- if (status != 90) {
- log_warn(LD_NET,"Got status response '%d': socks request failed.", status);
- if (!strcasecmpend(hostname, ".onion")) {
- onion_warning(hostname);
- result = -1; goto cleanup;
- }
- result = -1; goto cleanup;
- }
- tor_addr_from_ipv4h(addr_out, socks4_server_reply_get_addr(reply));
- cleanup:
- socks4_server_reply_free(reply);
- return result;
- }
- /* It would be nice to let someone know what SOCKS5 issue a user may have */
- static const char *
- socks5_reason_to_string(char reason)
- {
- switch (reason) {
- case SOCKS5_SUCCEEDED:
- return "succeeded";
- case SOCKS5_GENERAL_ERROR:
- return "general error";
- case SOCKS5_NOT_ALLOWED:
- return "not allowed";
- case SOCKS5_NET_UNREACHABLE:
- return "network is unreachable";
- case SOCKS5_HOST_UNREACHABLE:
- return "host is unreachable";
- case SOCKS5_CONNECTION_REFUSED:
- return "connection refused";
- case SOCKS5_TTL_EXPIRED:
- return "ttl expired";
- case SOCKS5_COMMAND_NOT_SUPPORTED:
- return "command not supported";
- case SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED:
- return "address type not supported";
- default:
- return "unknown SOCKS5 code";
- }
- }
- /** Send a resolve request for <b>hostname</b> to the Tor listening on
- * <b>sockshost</b>:<b>socksport</b>. Store the resulting IPv4
- * address (in host order) into *<b>result_addr</b>.
- */
- static int
- do_resolve(const char *hostname,
- const tor_addr_t *sockshost, uint16_t socksport,
- int reverse, int version,
- tor_addr_t *result_addr, char **result_hostname)
- {
- int s = -1;
- struct sockaddr_storage ss;
- socklen_t socklen;
- uint8_t *req = NULL;
- ssize_t len = 0;
- tor_assert(hostname);
- tor_assert(result_addr);
- tor_assert(version == 4 || version == 5);
- tor_addr_make_unspec(result_addr);
- *result_hostname = NULL;
- s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
- if (s<0) {
- log_sock_error("creating_socket", -1);
- return -1;
- }
- socklen = tor_addr_to_sockaddr(sockshost, socksport,
- (struct sockaddr *)&ss, sizeof(ss));
- if (connect(s, (struct sockaddr*)&ss, socklen)) {
- log_sock_error("connecting to SOCKS host", s);
- goto err;
- }
- if (version == 5) {
- socks5_client_version_t *v = socks5_client_version_new();
- socks5_client_version_set_version(v, 5);
- socks5_client_version_set_n_methods(v, 1);
- socks5_client_version_setlen_methods(v, 1);
- socks5_client_version_set_methods(v, 0, 0x00);
- tor_assert(!socks5_client_version_check(v));
- ssize_t encoded_len = socks5_client_version_encoded_len(v);
- tor_assert(encoded_len > 0);
- uint8_t *buf = tor_malloc(encoded_len);
- encoded_len = socks5_client_version_encode(buf, encoded_len, v);
- tor_assert(encoded_len > 0);
- socks5_client_version_free(v);
- if (write_all_to_socket(s, (const char *)buf,
- encoded_len) != encoded_len) {
- log_err(LD_NET, "Error sending SOCKS5 method list.");
- tor_free(buf);
- goto err;
- }
- tor_free(buf);
- uint8_t method_buf[2];
- if (read_all_from_socket(s, (char *)method_buf, 2) != 2) {
- log_err(LD_NET, "Error reading SOCKS5 methods.");
- goto err;
- }
- socks5_server_method_t *m;
- ssize_t parsed = socks5_server_method_parse(&m, method_buf,
- sizeof(method_buf));
- if (parsed < 2) {
- log_err(LD_NET, "Failed to parse SOCKS5 method selection "
- "message");
- socks5_server_method_free(m);
- goto err;
- }
- uint8_t method = socks5_server_method_get_method(m);
- socks5_server_method_free(m);
- if (method != 0x00) {
- log_err(LD_NET, "Unrecognized socks authentication method: %u",
- (unsigned)method);
- goto err;
- }
- }
- if ((len = build_socks_resolve_request(&req, "", hostname, reverse,
- version))<0) {
- log_err(LD_BUG,"Error generating SOCKS request");
- tor_assert(!req);
- goto err;
- }
- if (write_all_to_socket(s, (const char *)req, len) != len) {
- log_sock_error("sending SOCKS request", s);
- tor_free(req);
- goto err;
- }
- tor_free(req);
- if (version == 4) {
- char reply_buf[RESPONSE_LEN_4];
- if (read_all_from_socket(s, reply_buf, RESPONSE_LEN_4) != RESPONSE_LEN_4) {
- log_err(LD_NET, "Error reading SOCKS4 response.");
- goto err;
- }
- if (parse_socks4a_resolve_response(hostname,
- reply_buf, RESPONSE_LEN_4,
- result_addr)<0) {
- goto err;
- }
- } else {
- uint8_t reply_buf[512];
- len = read_all_from_socket(s, (char *)reply_buf,
- sizeof(reply_buf));
- socks5_server_reply_t *reply;
- ssize_t parsed = socks5_server_reply_parse(&reply,
- reply_buf,
- len);
- if (parsed == -1) {
- log_err(LD_NET, "Failed parsing SOCKS5 response");
- goto err;
- }
- if (parsed == -2) {
- log_err(LD_NET, "Truncated SOCKS5 response");
- goto err;
- }
- /* Give a user some useful feedback about SOCKS5 errors */
- uint8_t reply_field = socks5_server_reply_get_reply(reply);
- if (reply_field != 0) {
- log_warn(LD_NET,"Got SOCKS5 status response '%u': %s",
- (unsigned)reply_field,
- socks5_reason_to_string(reply_field));
- if (reply_field == 4 && !strcasecmpend(hostname, ".onion")) {
- onion_warning(hostname);
- }
- socks5_server_reply_free(reply);
- goto err;
- }
- uint8_t atype = socks5_server_reply_get_atype(reply);
- if (atype == SOCKS5_ATYPE_IPV4) {
- /* IPv4 address */
- tor_addr_from_ipv4h(result_addr,
- socks5_server_reply_get_bind_addr_ipv4(reply));
- } else if (atype == SOCKS5_ATYPE_IPV6) {
- /* IPv6 address */
- tor_addr_from_ipv6_bytes(result_addr,
- (const char *)socks5_server_reply_getarray_bind_addr_ipv6(reply));
- } else if (atype == SOCKS5_ATYPE_HOSTNAME) {
- /* Domain name */
- domainname_t *dn =
- socks5_server_reply_get_bind_addr_domainname(reply);
- *result_hostname = tor_strdup(domainname_getstr_name(dn));
- }
- socks5_server_reply_free(reply);
- }
- tor_close_socket(s);
- return 0;
- err:
- tor_close_socket(s);
- return -1;
- }
- /** Print a usage message and exit. */
- static void
- usage(void)
- {
- puts("Syntax: tor-resolve [-4] [-5] [-v] [-x] [-p port] "
- "hostname [sockshost[:socksport]]");
- exit(1);
- }
- /** Entry point to tor-resolve */
- int
- main(int argc, char **argv)
- {
- tor_addr_t sockshost;
- uint16_t socksport = 0, port_option = 0;
- int isSocks4 = 0, isVerbose = 0, isReverse = 0;
- char **arg;
- int n_args;
- tor_addr_t result;
- char *result_hostname = NULL;
- init_logging(1);
- sandbox_disable_getaddrinfo_cache();
- arg = &argv[1];
- n_args = argc-1;
- if (!n_args)
- usage();
- if (!strcmp(arg[0],"--version")) {
- printf("Tor version %s.\n",VERSION);
- return 0;
- }
- while (n_args && *arg[0] == '-') {
- if (!strcmp("-v", arg[0]))
- isVerbose = 1;
- else if (!strcmp("-4", arg[0]))
- isSocks4 = 1;
- else if (!strcmp("-5", arg[0]))
- isSocks4 = 0;
- else if (!strcmp("-x", arg[0]))
- isReverse = 1;
- else if (!strcmp("-p", arg[0])) {
- int p;
- if (n_args < 2) {
- fprintf(stderr, "No arguments given to -p\n");
- usage();
- }
- p = atoi(arg[1]);
- if (p<1 || p > 65535) {
- fprintf(stderr, "-p requires a number between 1 and 65535\n");
- usage();
- }
- port_option = (uint16_t) p;
- ++arg; /* skip the port */
- --n_args;
- } else {
- fprintf(stderr, "Unrecognized flag '%s'\n", arg[0]);
- usage();
- }
- ++arg;
- --n_args;
- }
- if (isSocks4 && isReverse) {
- fprintf(stderr, "Reverse lookups not supported with SOCKS4a\n");
- usage();
- }
- log_severity_list_t *severities =
- tor_malloc_zero(sizeof(log_severity_list_t));
- if (isVerbose)
- set_log_severity_config(LOG_DEBUG, LOG_ERR, severities);
- else
- set_log_severity_config(LOG_WARN, LOG_ERR, severities);
- add_stream_log(severities, "<stderr>", fileno(stderr));
- tor_free(severities);
- if (n_args == 1) {
- log_debug(LD_CONFIG, "defaulting to localhost");
- tor_addr_from_ipv4h(&sockshost, 0x7f000001u); /* localhost */
- if (port_option) {
- log_debug(LD_CONFIG, "Using port %d", (int)port_option);
- socksport = port_option;
- } else {
- log_debug(LD_CONFIG, "defaulting to port 9050");
- socksport = 9050; /* 9050 */
- }
- } else if (n_args == 2) {
- if (tor_addr_port_lookup(arg[1], &sockshost, &socksport)<0) {
- fprintf(stderr, "Couldn't parse/resolve address %s", arg[1]);
- return 1;
- }
- if (socksport && port_option && socksport != port_option) {
- log_warn(LD_CONFIG, "Conflicting ports; using %d, not %d",
- (int)socksport, (int)port_option);
- } else if (port_option) {
- socksport = port_option;
- } else if (!socksport) {
- log_debug(LD_CONFIG, "defaulting to port 9050");
- socksport = 9050;
- }
- } else {
- usage();
- }
- if (network_init()<0) {
- log_err(LD_BUG,"Error initializing network; exiting.");
- return 1;
- }
- if (do_resolve(arg[0], &sockshost, socksport, isReverse,
- isSocks4 ? 4 : 5, &result,
- &result_hostname))
- return 1;
- if (result_hostname) {
- printf("%s\n", result_hostname);
- } else {
- printf("%s\n", fmt_addr(&result));
- }
- return 0;
- }
|