diff options
Diffstat (limited to 'src')
68 files changed, 7623 insertions, 958 deletions
diff --git a/src/app/config/auth_dirs.inc b/src/app/config/auth_dirs.inc index e0937541ea..08a919b053 100644 --- a/src/app/config/auth_dirs.inc +++ b/src/app/config/auth_dirs.inc @@ -8,8 +8,8 @@ "dizum orport=443 " "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", -"Bifroest orport=443 bridge " - "37.218.247.217:80 1D8F 3A91 C37C 5D1C 4C19 B1AD 1D0C FBE8 BF72 D8E1", +"Serge orport=9001 bridge " + "66.111.2.131:9030 BA44 A889 E64B 93FA A2B1 14E0 2C2A 279A 8555 C533", "gabelmoo orport=443 " "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " "ipv6=[2001:638:a000:4140::ffff:189]:443 " diff --git a/src/app/config/config.c b/src/app/config/config.c index 74aa64dede..1b1889779d 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -3544,6 +3544,16 @@ options_validate(or_options_t *old_options, or_options_t *options, !options->RecommendedServerVersions)) REJECT("Versioning authoritative dir servers must set " "Recommended*Versions."); + +#ifdef HAVE_MODULE_DIRAUTH + char *t; + /* Call these functions to produce warnings only. */ + t = format_recommended_version_list(options->RecommendedClientVersions, 1); + tor_free(t); + t = format_recommended_version_list(options->RecommendedServerVersions, 1); + tor_free(t); +#endif + if (options->UseEntryGuards) { log_info(LD_CONFIG, "Authoritative directory servers can't set " "UseEntryGuards. Disabling."); @@ -3560,7 +3570,7 @@ options_validate(or_options_t *old_options, or_options_t *options, "(Bridge/V3)AuthoritativeDir is set."); /* If we have a v3bandwidthsfile and it's broken, complain on startup */ if (options->V3BandwidthsFile && !old_options) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL); } /* same for guardfraction file */ if (options->GuardfractionFile && !old_options) { @@ -7001,7 +7011,7 @@ parse_port_config(smartlist_t *out, port = 0; else port = 1; - } else if (!strcmp(addrport, "auto")) { + } else if (!strcasecmp(addrport, "auto")) { port = CFG_AUTO_PORT; int af = tor_addr_parse(&addr, defaultaddr); tor_assert(af >= 0); diff --git a/src/app/config/confparse.c b/src/app/config/confparse.c index 5b7f54bc65..6fa4fd1ea8 100644 --- a/src/app/config/confparse.c +++ b/src/app/config/confparse.c @@ -267,7 +267,7 @@ config_assign_value(const config_format_t *fmt, void *options, break; case CONFIG_TYPE_AUTOBOOL: - if (!strcmp(c->value, "auto")) + if (!strcasecmp(c->value, "auto")) *(int *)lvalue = -1; else if (!strcmp(c->value, "0")) *(int *)lvalue = 0; diff --git a/src/core/or/addr_policy_st.h b/src/core/or/addr_policy_st.h index be3e9d8f01..222a067252 100644 --- a/src/core/or/addr_policy_st.h +++ b/src/core/or/addr_policy_st.h @@ -18,7 +18,7 @@ typedef enum { #define addr_policy_action_bitfield_t ENUM_BF(addr_policy_action_t) /** A reference-counted address policy rule. */ -typedef struct addr_policy_t { +struct addr_policy_t { int refcnt; /**< Reference count */ /** What to do when the policy matches.*/ addr_policy_action_bitfield_t policy_type:2; @@ -41,6 +41,6 @@ typedef struct addr_policy_t { tor_addr_t addr; uint16_t prt_min; /**< Lowest port number to accept/reject. */ uint16_t prt_max; /**< Highest port number to accept/reject. */ -} addr_policy_t; +}; #endif diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 159ee96266..c5ff10f6a3 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -52,6 +52,7 @@ #include "core/proto/proto_cell.h" #include "core/or/reasons.h" #include "core/or/relay.h" +#include "feature/rend/rendcommon.h" #include "feature/stats/rephist.h" #include "feature/relay/router.h" #include "feature/relay/routerkeys.h" @@ -1938,10 +1939,13 @@ connection_or_client_learned_peer_id(or_connection_t *conn, conn->identity_digest); const int is_authority_fingerprint = router_digest_is_trusted_dir( conn->identity_digest); + const int non_anonymous_mode = rend_non_anonymous_mode_enabled(options); int severity; const char *extra_log = ""; - if (server_mode(options)) { + /* Relays and Single Onion Services make direct connections using + * untrusted authentication keys. */ + if (server_mode(options) || non_anonymous_mode) { severity = LOG_PROTOCOL_WARN; } else { if (using_hardcoded_fingerprints) { @@ -1965,8 +1969,8 @@ connection_or_client_learned_peer_id(or_connection_t *conn, } log_fn(severity, LD_HANDSHAKE, - "Tried connecting to router at %s:%d, but RSA identity key was not " - "as expected: wanted %s + %s but got %s + %s.%s", + "Tried connecting to router at %s:%d, but RSA + ed25519 identity " + "keys were not as expected: wanted %s + %s but got %s + %s.%s", conn->base_.address, conn->base_.port, expected_rsa, expected_ed, seen_rsa, seen_ed, extra_log); @@ -1983,8 +1987,8 @@ connection_or_client_learned_peer_id(or_connection_t *conn, } if (!expected_ed_key && ed_peer_id) { - log_info(LD_HANDSHAKE, "(we had no Ed25519 ID in mind when we made this " - "connection."); + log_info(LD_HANDSHAKE, "(We had no Ed25519 ID in mind when we made this " + "connection.)"); connection_or_set_identity_digest(conn, (const char*)rsa_peer_id, ed_peer_id); changed_identity = 1; diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 32bb69d25f..51084e2a17 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -2005,9 +2005,10 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial, } if (connection_edge_send_command(conn, RELAY_COMMAND_DATA, - payload, length) < 0 ) + payload, length) < 0 ) { /* circuit got marked for close, don't continue, don't need to mark conn */ return 0; + } if (!cpath_layer) { /* non-rendezvous exit */ tor_assert(circ->package_window > 0); diff --git a/src/core/or/scheduler.c b/src/core/or/scheduler.c index e19059f0c5..dd028fc785 100644 --- a/src/core/or/scheduler.c +++ b/src/core/or/scheduler.c @@ -351,8 +351,8 @@ set_scheduler(void) /* Finally we notice log if we switched schedulers. We use the type in case * two schedulers share a scheduler object. */ if (old_scheduler_type != the_scheduler->type) { - log_notice(LD_CONFIG, "Scheduler type %s has been enabled.", - get_scheduler_type_string(the_scheduler->type)); + log_info(LD_CONFIG, "Scheduler type %s has been enabled.", + get_scheduler_type_string(the_scheduler->type)); } } diff --git a/src/core/or/socks_request_st.h b/src/core/or/socks_request_st.h index d7b979c3eb..17b668e179 100644 --- a/src/core/or/socks_request_st.h +++ b/src/core/or/socks_request_st.h @@ -70,6 +70,8 @@ struct socks_request_t { /** The negotiated password value if any (for socks5). This value is NOT * nul-terminated; see passwordlen for its length. */ char *password; + + uint8_t socks5_atyp; /* SOCKS5 address type */ }; #endif diff --git a/src/core/proto/proto_ext_or.h b/src/core/proto/proto_ext_or.h index 708a45974b..2ff6ad45ca 100644 --- a/src/core/proto/proto_ext_or.h +++ b/src/core/proto/proto_ext_or.h @@ -10,11 +10,11 @@ struct buf_t; /** A parsed Extended ORPort message. */ -typedef struct ext_or_cmd_t { +struct ext_or_cmd_t { uint16_t cmd; /** Command type */ uint16_t len; /** Body length */ char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */ -} ext_or_cmd_t; +}; int fetch_ext_or_command_from_buf(struct buf_t *buf, struct ext_or_cmd_t **out); diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c index 6912441472..ccf96f7814 100644 --- a/src/core/proto/proto_socks.c +++ b/src/core/proto/proto_socks.c @@ -17,12 +17,28 @@ #include "core/or/socks_request_st.h" +#include "trunnel/socks5.h" + +#define SOCKS_VER_5 0x05 /* First octet of non-auth SOCKS5 messages */ +#define SOCKS_VER_4 0x04 /* SOCKS4 messages */ +#define SOCKS_AUTH 0x01 /* SOCKS5 auth messages */ + +typedef enum { + SOCKS_RESULT_INVALID = -1, /* Message invalid. */ + SOCKS_RESULT_TRUNCATED = 0, /* Message incomplete/truncated. */ + SOCKS_RESULT_DONE = 1, /* OK, we're done. */ + SOCKS_RESULT_MORE_EXPECTED = 2, /* OK, more messages expected. */ +} socks_result_t; + static void socks_request_set_socks5_error(socks_request_t *req, socks5_reply_status_t reason); -static int parse_socks(const char *data, size_t datalen, socks_request_t *req, - int log_sockstype, int safe_socks, ssize_t *drain_out, - size_t *want_length_out); +static socks_result_t parse_socks(const char *data, + size_t datalen, + socks_request_t *req, + int log_sockstype, + int safe_socks, + size_t *drain_out); static int parse_socks_client(const uint8_t *data, size_t datalen, int state, char **reason, ssize_t *drain_out); @@ -86,6 +102,689 @@ socks_request_free_(socks_request_t *req) tor_free(req); } +/** + * Parse a single SOCKS4 request from buffer <b>raw_data</b> of length + * <b>datalen</b> and update relevant fields of <b>req</b>. If SOCKS4a + * request is detected, set <b>*is_socks4a<b> to true. Set <b>*drain_out</b> + * to number of bytes we parsed so far. + * + * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if + * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it + * failed due to incomplete (truncated) input. + */ +static socks_result_t +parse_socks4_request(const uint8_t *raw_data, socks_request_t *req, + size_t datalen, int *is_socks4a, size_t *drain_out) +{ + // http://ss5.sourceforge.net/socks4.protocol.txt + // http://ss5.sourceforge.net/socks4A.protocol.txt + socks_result_t res = SOCKS_RESULT_DONE; + tor_addr_t destaddr; + + tor_assert(is_socks4a); + tor_assert(drain_out); + + *is_socks4a = 0; + *drain_out = 0; + + req->socks_version = SOCKS_VER_4; + + socks4_client_request_t *trunnel_req; + + ssize_t parsed = + socks4_client_request_parse(&trunnel_req, raw_data, datalen); + + if (parsed == -1) { + log_warn(LD_APP, "socks4: parsing failed - invalid request."); + res = SOCKS_RESULT_INVALID; + goto end; + } else if (parsed == -2) { + res = SOCKS_RESULT_TRUNCATED; + if (datalen >= MAX_SOCKS_MESSAGE_LEN) { + log_warn(LD_APP, "socks4: parsing failed - invalid request."); + res = SOCKS_RESULT_INVALID; + } + goto end; + } + + tor_assert(parsed >= 0); + *drain_out = (size_t)parsed; + + uint8_t command = socks4_client_request_get_command(trunnel_req); + req->command = command; + + req->port = socks4_client_request_get_port(trunnel_req); + uint32_t dest_ip = socks4_client_request_get_addr(trunnel_req); + + if ((!req->port && req->command != SOCKS_COMMAND_RESOLVE) || + dest_ip == 0) { + log_warn(LD_APP, "socks4: Port or DestIP is zero. Rejecting."); + res = SOCKS_RESULT_INVALID; + goto end; + } + + *is_socks4a = (dest_ip >> 8) == 0; + + const char *username = socks4_client_request_get_username(trunnel_req); + const size_t usernamelen = username ? strlen(username) : 0; + if (username && usernamelen) { + if (usernamelen > MAX_SOCKS_MESSAGE_LEN) { + log_warn(LD_APP, "Socks4 user name too long; rejecting."); + res = SOCKS_RESULT_INVALID; + goto end; + } + + tor_free(req->username); + req->got_auth = 1; + req->username = tor_strdup(username); + req->usernamelen = usernamelen; + } + + if (*is_socks4a) { + // We cannot rely on trunnel here, as we want to detect if + // we have abnormally long hostname field. + const char *hostname = (char *)raw_data + SOCKS4_NETWORK_LEN + + usernamelen + 1; + size_t hostname_len = (char *)raw_data + datalen - hostname; + + if (hostname_len <= sizeof(req->address)) { + const char *trunnel_hostname = + socks4_client_request_get_socks4a_addr_hostname(trunnel_req); + + if (trunnel_hostname) + strlcpy(req->address, trunnel_hostname, sizeof(req->address)); + } else { + log_warn(LD_APP, "socks4: Destaddr too long. Rejecting."); + res = SOCKS_RESULT_INVALID; + goto end; + } + } else { + tor_addr_from_ipv4h(&destaddr, dest_ip); + + if (!tor_addr_to_str(req->address, &destaddr, + MAX_SOCKS_ADDR_LEN, 0)) { + res = SOCKS_RESULT_INVALID; + goto end; + } + } + + end: + socks4_client_request_free(trunnel_req); + + return res; +} + +/** + * Validate SOCKS4/4a related fields in <b>req</b>. Expect SOCKS4a + * if <b>is_socks4a</b> is true. If <b>log_sockstype</b> is true, + * log a notice about possible DNS leaks on local system. If + * <b>safe_socks</b> is true, reject insecure usage of SOCKS + * protocol. + * + * Return SOCKS_RESULT_DONE if validation passed or + * SOCKS_RESULT_INVALID if it failed. + */ +static socks_result_t +process_socks4_request(const socks_request_t *req, int is_socks4a, + int log_sockstype, int safe_socks) +{ + if (is_socks4a && !addressmap_have_mapping(req->address, 0)) { + log_unsafe_socks_warning(4, req->address, req->port, safe_socks); + + if (safe_socks) + return SOCKS_RESULT_INVALID; + } + + if (req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE) { + /* not a connect or resolve? we don't support it. (No resolve_ptr with + * socks4.) */ + log_warn(LD_APP, "socks4: command %d not recognized. Rejecting.", + req->command); + return SOCKS_RESULT_INVALID; + } + + if (is_socks4a) { + if (log_sockstype) + log_notice(LD_APP, + "Your application (using socks4a to port %d) instructed " + "Tor to take care of the DNS resolution itself if " + "necessary. This is good.", req->port); + } + + if (!string_is_valid_dest(req->address)) { + log_warn(LD_PROTOCOL, + "Your application (using socks4 to port %d) gave Tor " + "a malformed hostname: %s. Rejecting the connection.", + req->port, escaped_safe_str_client(req->address)); + return SOCKS_RESULT_INVALID; + } + + return SOCKS_RESULT_DONE; +} + +/** Parse a single SOCKS5 version identifier/method selection message + * from buffer <b>raw_data</b> (of length <b>datalen</b>). Update + * relevant fields of <b>req</b> (if any). Set <b>*have_user_pass</b> to + * true if username/password method is found. Set <b>*have_no_auth</b> + * if no-auth method is found. Set <b>*drain_out</b> to number of bytes + * we parsed so far. + * + * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if + * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it + * failed due to incomplete (truncated) input. + */ +static socks_result_t +parse_socks5_methods_request(const uint8_t *raw_data, socks_request_t *req, + size_t datalen, int *have_user_pass, + int *have_no_auth, size_t *drain_out) +{ + socks_result_t res = SOCKS_RESULT_DONE; + socks5_client_version_t *trunnel_req; + + ssize_t parsed = socks5_client_version_parse(&trunnel_req, raw_data, + datalen); + + (void)req; + + tor_assert(have_no_auth); + tor_assert(have_user_pass); + tor_assert(drain_out); + + *drain_out = 0; + + if (parsed == -1) { + log_warn(LD_APP, "socks5: parsing failed - invalid version " + "id/method selection message."); + res = SOCKS_RESULT_INVALID; + goto end; + } else if (parsed == -2) { + res = SOCKS_RESULT_TRUNCATED; + if (datalen > MAX_SOCKS_MESSAGE_LEN) { + log_warn(LD_APP, "socks5: parsing failed - invalid version " + "id/method selection message."); + res = SOCKS_RESULT_INVALID; + } + goto end; + } + + tor_assert(parsed >= 0); + *drain_out = (size_t)parsed; + + size_t n_methods = (size_t)socks5_client_version_get_n_methods(trunnel_req); + if (n_methods == 0) { + res = SOCKS_RESULT_INVALID; + goto end; + } + + *have_no_auth = 0; + *have_user_pass = 0; + + for (size_t i = 0; i < n_methods; i++) { + uint8_t method = socks5_client_version_get_methods(trunnel_req, + i); + + if (method == SOCKS_USER_PASS) { + *have_user_pass = 1; + } else if (method == SOCKS_NO_AUTH) { + *have_no_auth = 1; + } + } + + end: + socks5_client_version_free(trunnel_req); + + return res; +} + +/** + * Validate and respond to version identifier/method selection message + * we parsed in parse_socks5_methods_request (corresponding to <b>req</b> + * and having user/pass method if <b>have_user_pass</b> is true, no-auth + * method if <b>have_no_auth</b> is true). Set <b>req->reply</b> to + * an appropriate response (in SOCKS5 wire format). + * + * On success, return SOCKS_RESULT_DONE. On failure, return + * SOCKS_RESULT_INVALID. + */ +static socks_result_t +process_socks5_methods_request(socks_request_t *req, int have_user_pass, + int have_no_auth) +{ + socks_result_t res = SOCKS_RESULT_DONE; + socks5_server_method_t *trunnel_resp = socks5_server_method_new(); + + socks5_server_method_set_version(trunnel_resp, SOCKS_VER_5); + + if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) { + req->auth_type = SOCKS_USER_PASS; + socks5_server_method_set_method(trunnel_resp, SOCKS_USER_PASS); + + req->socks_version = SOCKS_VER_5; + // FIXME: come up with better way to remember + // that we negotiated auth + + log_debug(LD_APP,"socks5: accepted method 2 (username/password)"); + } else if (have_no_auth) { + req->auth_type = SOCKS_NO_AUTH; + socks5_server_method_set_method(trunnel_resp, SOCKS_NO_AUTH); + + req->socks_version = SOCKS_VER_5; + + log_debug(LD_APP,"socks5: accepted method 0 (no authentication)"); + } else { + log_warn(LD_APP, + "socks5: offered methods don't include 'no auth' or " + "username/password. Rejecting."); + socks5_server_method_set_method(trunnel_resp, 0xFF); // reject all + res = SOCKS_RESULT_INVALID; + } + + const char *errmsg = socks5_server_method_check(trunnel_resp); + if (errmsg) { + log_warn(LD_APP, "socks5: method selection validation failed: %s", + errmsg); + res = SOCKS_RESULT_INVALID; + } else { + ssize_t encoded = + socks5_server_method_encode(req->reply, sizeof(req->reply), + trunnel_resp); + + if (encoded < 0) { + log_warn(LD_APP, "socks5: method selection encoding failed"); + res = SOCKS_RESULT_INVALID; + } else { + req->replylen = (size_t)encoded; + } + } + + socks5_server_method_free(trunnel_resp); + return res; +} + +/** + * Parse SOCKS5/RFC1929 username/password request from buffer + * <b>raw_data</b> of length <b>datalen</b> and update relevant + * fields of <b>req</b>. Set <b>*drain_out</b> to number of bytes + * we parsed so far. + * + * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if + * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it + * failed due to incomplete (truncated) input. + */ +static socks_result_t +parse_socks5_userpass_auth(const uint8_t *raw_data, socks_request_t *req, + size_t datalen, size_t *drain_out) +{ + socks_result_t res = SOCKS_RESULT_DONE; + socks5_client_userpass_auth_t *trunnel_req = NULL; + ssize_t parsed = socks5_client_userpass_auth_parse(&trunnel_req, raw_data, + datalen); + tor_assert(drain_out); + *drain_out = 0; + + if (parsed == -1) { + log_warn(LD_APP, "socks5: parsing failed - invalid user/pass " + "authentication message."); + res = SOCKS_RESULT_INVALID; + goto end; + } else if (parsed == -2) { + res = SOCKS_RESULT_TRUNCATED; + goto end; + } + + tor_assert(parsed >= 0); + *drain_out = (size_t)parsed; + + uint8_t usernamelen = + socks5_client_userpass_auth_get_username_len(trunnel_req); + uint8_t passwordlen = + socks5_client_userpass_auth_get_passwd_len(trunnel_req); + const char *username = + socks5_client_userpass_auth_getconstarray_username(trunnel_req); + const char *password = + socks5_client_userpass_auth_getconstarray_passwd(trunnel_req); + + if (usernamelen && username) { + tor_free(req->username); + req->username = tor_memdup_nulterm(username, usernamelen); + req->usernamelen = usernamelen; + + req->got_auth = 1; + } + + if (passwordlen && password) { + tor_free(req->password); + req->password = tor_memdup_nulterm(password, passwordlen); + req->passwordlen = passwordlen; + + req->got_auth = 1; + } + + end: + socks5_client_userpass_auth_free(trunnel_req); + return res; +} + +/** + * Validate and respond to SOCKS5 username/password request we + * parsed in parse_socks5_userpass_auth (corresponding to <b>req</b>. + * Set <b>req->reply</b> to appropriate responsed. Return + * SOCKS_RESULT_DONE on success or SOCKS_RESULT_INVALID on failure. + */ +static socks_result_t +process_socks5_userpass_auth(socks_request_t *req) +{ + socks_result_t res = SOCKS_RESULT_DONE; + socks5_server_userpass_auth_t *trunnel_resp = + socks5_server_userpass_auth_new(); + + if (req->socks_version != SOCKS_VER_5) { + res = SOCKS_RESULT_INVALID; + goto end; + } + + if (req->auth_type != SOCKS_USER_PASS && + req->auth_type != SOCKS_NO_AUTH) { + res = SOCKS_RESULT_INVALID; + goto end; + } + + socks5_server_userpass_auth_set_version(trunnel_resp, SOCKS_AUTH); + socks5_server_userpass_auth_set_status(trunnel_resp, 0); // auth OK + + const char *errmsg = socks5_server_userpass_auth_check(trunnel_resp); + if (errmsg) { + log_warn(LD_APP, "socks5: server userpass auth validation failed: %s", + errmsg); + res = SOCKS_RESULT_INVALID; + goto end; + } + + ssize_t encoded = socks5_server_userpass_auth_encode(req->reply, + sizeof(req->reply), + trunnel_resp); + + if (encoded < 0) { + log_warn(LD_APP, "socks5: server userpass auth encoding failed"); + res = SOCKS_RESULT_INVALID; + goto end; + } + + req->replylen = (size_t)encoded; + + end: + socks5_server_userpass_auth_free(trunnel_resp); + return res; +} + +/** + * Parse a single SOCKS5 client request (RFC 1928 section 4) from buffer + * <b>raw_data</b> of length <b>datalen</b> and update relevant field of + * <b>req</b>. Set <b>*drain_out</b> to number of bytes we parsed so far. + * + * Return SOCKS_RESULT_DONE if parsing succeeded, SOCKS_RESULT_INVALID if + * parsing failed because of invalid input or SOCKS_RESULT_TRUNCATED if it + * failed due to incomplete (truncated) input. + */ +static socks_result_t +parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, + size_t datalen, size_t *drain_out) +{ + socks_result_t res = SOCKS_RESULT_DONE; + tor_addr_t destaddr; + socks5_client_request_t *trunnel_req = NULL; + ssize_t parsed = + socks5_client_request_parse(&trunnel_req, raw_data, datalen); + if (parsed == -1) { + log_warn(LD_APP, "socks5: parsing failed - invalid client request"); + res = SOCKS_RESULT_INVALID; + goto end; + } else if (parsed == -2) { + res = SOCKS_RESULT_TRUNCATED; + goto end; + } + + tor_assert(parsed >= 0); + *drain_out = (size_t)parsed; + + if (socks5_client_request_get_version(trunnel_req) != 5) { + res = SOCKS_RESULT_INVALID; + goto end; + } + + req->command = socks5_client_request_get_command(trunnel_req); + + req->port = socks5_client_request_get_dest_port(trunnel_req); + + uint8_t atype = socks5_client_request_get_atype(trunnel_req); + req->socks5_atyp = atype; + + switch (atype) { + case 1: { + uint32_t ipv4 = socks5_client_request_get_dest_addr_ipv4(trunnel_req); + tor_addr_from_ipv4h(&destaddr, ipv4); + + tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); + } break; + case 3: { + const struct domainname_st *dns_name = + socks5_client_request_getconst_dest_addr_domainname(trunnel_req); + + const char *hostname = domainname_getconstarray_name(dns_name); + + strlcpy(req->address, hostname, sizeof(req->address)); + } break; + case 4: { + const char *ipv6 = + (const char *)socks5_client_request_getarray_dest_addr_ipv6( + trunnel_req); + tor_addr_from_ipv6_bytes(&destaddr, ipv6); + + tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); + } break; + default: { + res = -1; + } break; + } + + end: + socks5_client_request_free(trunnel_req); + return res; +} + +/** + * Validate and respond to SOCKS5 request we parsed in + * parse_socks5_client_request (corresponding to <b>req</b>. + * Write appropriate response to <b>req->reply</b> (in + * SOCKS5 wire format). If <b>log_sockstype</b> is true, log a + * notice about possible DNS leaks on local system. If + * <b>safe_socks</b> is true, disallow insecure usage of SOCKS + * protocol. Return SOCKS_RESULT_DONE on success or + * SOCKS_RESULT_INVALID on failure. + */ +static socks_result_t +process_socks5_client_request(socks_request_t *req, + int log_sockstype, + int safe_socks) +{ + socks_result_t res = SOCKS_RESULT_DONE; + + if (req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE && + req->command != SOCKS_COMMAND_RESOLVE_PTR) { + socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED); + res = SOCKS_RESULT_INVALID; + goto end; + } + + if (req->command == SOCKS_COMMAND_RESOLVE_PTR && + !string_is_valid_ipv4_address(req->address) && + !string_is_valid_ipv6_address(req->address)) { + socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED); + log_warn(LD_APP, "socks5 received RESOLVE_PTR command with " + "hostname type. Rejecting."); + + res = SOCKS_RESULT_INVALID; + goto end; + } + + if (!string_is_valid_dest(req->address)) { + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); + + log_warn(LD_PROTOCOL, + "Your application (using socks5 to port %d) gave Tor " + "a malformed hostname: %s. Rejecting the connection.", + req->port, escaped_safe_str_client(req->address)); + + res = SOCKS_RESULT_INVALID; + goto end; + } + + if (req->socks5_atyp == 1 || req->socks5_atyp == 4) { + if (req->command != SOCKS_COMMAND_RESOLVE_PTR && + !addressmap_have_mapping(req->address,0)) { + log_unsafe_socks_warning(5, req->address, req->port, safe_socks); + if (safe_socks) { + socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED); + res = SOCKS_RESULT_INVALID; + goto end; + } + } + } + + if (log_sockstype) + log_notice(LD_APP, + "Your application (using socks5 to port %d) instructed " + "Tor to take care of the DNS resolution itself if " + "necessary. This is good.", req->port); + + end: + return res; +} + +/** + * Handle (parse, validate, process, respond) a single SOCKS + * message in buffer <b>raw_data</b> of length <b>datalen</b>. + * Update relevant fields of <b>req</b>. If <b>log_sockstype</b> + * is true, log a warning about possible DNS leaks on local + * system. If <b>safe_socks</b> is true, disallow insecure + * usage of SOCKS protocol. Set <b>*drain_out</b> to number + * of bytes in <b>raw_data</b> that we processed so far and + * that can be safely drained from buffer. + * + * Return: + * - SOCKS_RESULT_DONE if succeeded and not expecting further + * messages from client. + * - SOCKS_RESULT_INVALID if any of the steps failed due to + * request being invalid or unexpected given current state. + * - SOCKS_RESULT_TRUNCATED if we do not found an expected + * SOCKS message in its entirety (more stuff has to arrive + * from client). + * - SOCKS_RESULT_MORE_EXPECTED if we handled current message + * successfully, but we expect more messages from the + * client. + */ +static socks_result_t +handle_socks_message(const uint8_t *raw_data, size_t datalen, + socks_request_t *req, int log_sockstype, + int safe_socks, size_t *drain_out) +{ + socks_result_t res = SOCKS_RESULT_DONE; + + uint8_t socks_version = raw_data[0]; + + if (socks_version == SOCKS_AUTH) + socks_version = SOCKS_VER_5; // SOCKS5 username/pass subnegotiation + + if (socks_version == SOCKS_VER_4) { + if (datalen < SOCKS4_NETWORK_LEN) { + res = 0; + goto end; + } + + int is_socks4a = 0; + res = parse_socks4_request((const uint8_t *)raw_data, req, datalen, + &is_socks4a, drain_out); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + res = process_socks4_request(req, is_socks4a,log_sockstype, + safe_socks); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + goto end; + } else if (socks_version == SOCKS_VER_5) { + if (datalen < 2) { /* version and another byte */ + res = 0; + goto end; + } + /* RFC1929 SOCKS5 username/password subnegotiation. */ + if (!req->got_auth && (raw_data[0] == 1 || + req->auth_type == SOCKS_USER_PASS)) { + res = parse_socks5_userpass_auth(raw_data, req, datalen, + drain_out); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + res = process_socks5_userpass_auth(req); + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + res = SOCKS_RESULT_MORE_EXPECTED; + goto end; + } else if (req->socks_version != SOCKS_VER_5) { + int have_user_pass, have_no_auth; + res = parse_socks5_methods_request(raw_data, req, datalen, + &have_user_pass, + &have_no_auth, + drain_out); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + res = process_socks5_methods_request(req, have_user_pass, + have_no_auth); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + + res = SOCKS_RESULT_MORE_EXPECTED; + goto end; + } else { + res = parse_socks5_client_request(raw_data, req, + datalen, drain_out); + if (res != SOCKS_RESULT_DONE) { + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); + goto end; + } + + res = process_socks5_client_request(req, log_sockstype, + safe_socks); + + if (res != SOCKS_RESULT_DONE) { + goto end; + } + } + } else { + *drain_out = datalen; + res = SOCKS_RESULT_INVALID; + } + + end: + return res; +} + /** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one * of the forms * - socks4: "socksheader username\\0" @@ -115,32 +814,50 @@ int fetch_from_buf_socks(buf_t *buf, socks_request_t *req, int log_sockstype, int safe_socks) { - int res; - ssize_t n_drain; - size_t want_length = 128; + int res = 0; + size_t datalen = buf_datalen(buf); + size_t n_drain; const char *head = NULL; - size_t datalen = 0; + socks_result_t socks_res; + size_t n_pullup; - if (buf_datalen(buf) < 2) /* version and another byte */ - return 0; + if (buf_datalen(buf) < 2) { /* version and another byte */ + res = 0; + goto end; + } do { n_drain = 0; - buf_pullup(buf, want_length, &head, &datalen); + n_pullup = MIN(MAX_SOCKS_MESSAGE_LEN, buf_datalen(buf)); + buf_pullup(buf, n_pullup, &head, &datalen); tor_assert(head && datalen >= 2); - want_length = 0; - res = parse_socks(head, datalen, req, log_sockstype, - safe_socks, &n_drain, &want_length); + socks_res = parse_socks(head, datalen, req, log_sockstype, + safe_socks, &n_drain); - if (n_drain < 0) + if (socks_res == SOCKS_RESULT_INVALID) buf_clear(buf); - else if (n_drain > 0) + else if (socks_res != SOCKS_RESULT_TRUNCATED && n_drain > 0) buf_drain(buf, n_drain); - } while (res == 0 && head && want_length < buf_datalen(buf) && - buf_datalen(buf) >= 2); + switch (socks_res) { + case SOCKS_RESULT_INVALID: + res = -1; + break; + case SOCKS_RESULT_DONE: + res = 1; + break; + case SOCKS_RESULT_TRUNCATED: + if (datalen == n_pullup) + return 0; + /* FALLTHRU */ + case SOCKS_RESULT_MORE_EXPECTED: + res = 0; + break; + } + } while (res == 0 && head && buf_datalen(buf) >= 2); + end: return res; } @@ -151,12 +868,31 @@ static void socks_request_set_socks5_error(socks_request_t *req, socks5_reply_status_t reason) { - req->replylen = 10; - memset(req->reply,0,10); + socks5_server_reply_t *trunnel_resp = socks5_server_reply_new(); + + socks5_server_reply_set_version(trunnel_resp, SOCKS_VER_5); + socks5_server_reply_set_reply(trunnel_resp, reason); + socks5_server_reply_set_atype(trunnel_resp, 0x01); + + const char *errmsg = socks5_server_reply_check(trunnel_resp); + if (errmsg) { + log_warn(LD_APP, "socks5: reply validation failed: %s", + errmsg); + goto end; + } + + ssize_t encoded = socks5_server_reply_encode(req->reply, + sizeof(req->reply), + trunnel_resp); + if (encoded < 0) { + log_warn(LD_APP, "socks5: reply encoding failed: %d", + (int)encoded); + } else { + req->replylen = (size_t)encoded; + } - req->reply[0] = 0x05; // VER field. - req->reply[1] = reason; // REP field. - req->reply[3] = 0x01; // ATYP field. + end: + socks5_server_reply_free(trunnel_resp); } static const char SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG[] = @@ -194,350 +930,24 @@ static const char SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG[] = * we'd like to see in the input buffer, if they're available. */ static int parse_socks(const char *data, size_t datalen, socks_request_t *req, - int log_sockstype, int safe_socks, ssize_t *drain_out, - size_t *want_length_out) + int log_sockstype, int safe_socks, size_t *drain_out) { - unsigned int len; - char tmpbuf[TOR_ADDR_BUF_LEN+1]; - tor_addr_t destaddr; - uint32_t destip; - uint8_t socksver; - char *next, *startaddr; - unsigned char usernamelen, passlen; - struct in_addr in; + uint8_t first_octet; if (datalen < 2) { /* We always need at least 2 bytes. */ - *want_length_out = 2; return 0; } - if (req->socks_version == 5 && !req->got_auth) { - /* See if we have received authentication. Strictly speaking, we should - also check whether we actually negotiated username/password - authentication. But some broken clients will send us authentication - even if we negotiated SOCKS_NO_AUTH. */ - if (*data == 1) { /* username/pass version 1 */ - /* Format is: authversion [1 byte] == 1 - usernamelen [1 byte] - username [usernamelen bytes] - passlen [1 byte] - password [passlen bytes] */ - usernamelen = (unsigned char)*(data + 1); - if (datalen < 2u + usernamelen + 1u) { - *want_length_out = 2u + usernamelen + 1u; - return 0; - } - passlen = (unsigned char)*(data + 2u + usernamelen); - if (datalen < 2u + usernamelen + 1u + passlen) { - *want_length_out = 2u + usernamelen + 1u + passlen; - return 0; - } - req->replylen = 2; /* 2 bytes of response */ - req->reply[0] = 1; /* authversion == 1 */ - req->reply[1] = 0; /* authentication successful */ - log_debug(LD_APP, - "socks5: Accepted username/password without checking."); - if (usernamelen) { - req->username = tor_memdup(data+2u, usernamelen); - req->usernamelen = usernamelen; - } - if (passlen) { - req->password = tor_memdup(data+3u+usernamelen, passlen); - req->passwordlen = passlen; - } - *drain_out = 2u + usernamelen + 1u + passlen; - req->got_auth = 1; - *want_length_out = 7; /* Minimal socks5 command. */ - return 0; - } else if (req->auth_type == SOCKS_USER_PASS) { - /* unknown version byte */ - log_warn(LD_APP, "Socks5 username/password version %d not recognized; " - "rejecting.", (int)*data); - return -1; - } - } - - socksver = *data; - - switch (socksver) { /* which version of socks? */ - case 5: /* socks5 */ - - if (req->socks_version != 5) { /* we need to negotiate a method */ - unsigned char nummethods = (unsigned char)*(data+1); - int have_user_pass, have_no_auth; - int r=0; - tor_assert(!req->socks_version); - if (datalen < 2u+nummethods) { - *want_length_out = 2u+nummethods; - return 0; - } - if (!nummethods) - return -1; - req->replylen = 2; /* 2 bytes of response */ - req->reply[0] = 5; /* socks5 reply */ - have_user_pass = (memchr(data+2, SOCKS_USER_PASS, nummethods) !=NULL); - have_no_auth = (memchr(data+2, SOCKS_NO_AUTH, nummethods) !=NULL); - if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) { - req->auth_type = SOCKS_USER_PASS; - req->reply[1] = SOCKS_USER_PASS; /* tell client to use "user/pass" - auth method */ - req->socks_version = 5; /* remember we've already negotiated auth */ - log_debug(LD_APP,"socks5: accepted method 2 (username/password)"); - r=0; - } else if (have_no_auth) { - req->reply[1] = SOCKS_NO_AUTH; /* tell client to use "none" auth - method */ - req->socks_version = 5; /* remember we've already negotiated auth */ - log_debug(LD_APP,"socks5: accepted method 0 (no authentication)"); - r=0; - } else { - log_warn(LD_APP, - "socks5: offered methods don't include 'no auth' or " - "username/password. Rejecting."); - req->reply[1] = '\xFF'; /* reject all methods */ - r=-1; - } - /* Remove packet from buf. Some SOCKS clients will have sent extra - * junk at this point; let's hope it's an authentication message. */ - *drain_out = 2u + nummethods; - - return r; - } - if (req->auth_type != SOCKS_NO_AUTH && !req->got_auth) { - log_warn(LD_APP, - "socks5: negotiated authentication, but none provided"); - return -1; - } - /* we know the method; read in the request */ - log_debug(LD_APP,"socks5: checking request"); - if (datalen < 7) {/* basic info plus >=1 for addr plus 2 for port */ - *want_length_out = 7; - return 0; /* not yet */ - } - req->command = (unsigned char) *(data+1); - if (req->command != SOCKS_COMMAND_CONNECT && - req->command != SOCKS_COMMAND_RESOLVE && - req->command != SOCKS_COMMAND_RESOLVE_PTR) { - /* not a connect or resolve or a resolve_ptr? we don't support it. */ - socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED); - - log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.", - req->command); - return -1; - } - switch (*(data+3)) { /* address type */ - case 1: /* IPv4 address */ - case 4: /* IPv6 address */ { - const int is_v6 = *(data+3) == 4; - const unsigned addrlen = is_v6 ? 16 : 4; - log_debug(LD_APP,"socks5: ipv4 address type"); - if (datalen < 6+addrlen) {/* ip/port there? */ - *want_length_out = 6+addrlen; - return 0; /* not yet */ - } - - if (is_v6) - tor_addr_from_ipv6_bytes(&destaddr, data+4); - else - tor_addr_from_ipv4n(&destaddr, get_uint32(data+4)); - - tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1); - - if (BUG(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN)) { - /* LCOV_EXCL_START -- This branch is unreachable, given the - * size of tmpbuf and the actual value of MAX_SOCKS_ADDR_LEN */ - socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); - log_warn(LD_APP, - "socks5 IP takes %d bytes, which doesn't fit in %d. " - "Rejecting.", - (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN); - return -1; - /* LCOV_EXCL_STOP */ - } - strlcpy(req->address,tmpbuf,sizeof(req->address)); - req->port = ntohs(get_uint16(data+4+addrlen)); - *drain_out = 6+addrlen; - if (req->command != SOCKS_COMMAND_RESOLVE_PTR && - !addressmap_have_mapping(req->address,0)) { - log_unsafe_socks_warning(5, req->address, req->port, safe_socks); - if (safe_socks) { - socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED); - return -1; - } - } - return 1; - } - case 3: /* fqdn */ - log_debug(LD_APP,"socks5: fqdn address type"); - if (req->command == SOCKS_COMMAND_RESOLVE_PTR) { - socks_request_set_socks5_error(req, - SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED); - log_warn(LD_APP, "socks5 received RESOLVE_PTR command with " - "hostname type. Rejecting."); - return -1; - } - len = (unsigned char)*(data+4); - if (datalen < 7+len) { /* addr/port there? */ - *want_length_out = 7+len; - return 0; /* not yet */ - } - if (BUG(len+1 > MAX_SOCKS_ADDR_LEN)) { - /* LCOV_EXCL_START -- unreachable, since len is at most 255, - * and MAX_SOCKS_ADDR_LEN is 256. */ - socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); - log_warn(LD_APP, - "socks5 hostname is %d bytes, which doesn't fit in " - "%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN); - return -1; - /* LCOV_EXCL_STOP */ - } - memcpy(req->address,data+5,len); - req->address[len] = 0; - req->port = ntohs(get_uint16(data+5+len)); - *drain_out = 5+len+2; - - if (!string_is_valid_dest(req->address)) { - socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); - - log_warn(LD_PROTOCOL, - "Your application (using socks5 to port %d) gave Tor " - "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped_safe_str_client(req->address)); - return -1; - } - if (log_sockstype) - log_notice(LD_APP, - "Your application (using socks5 to port %d) instructed " - "Tor to take care of the DNS resolution itself if " - "necessary. This is good.", req->port); - return 1; - default: /* unsupported */ - socks_request_set_socks5_error(req, - SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED); - log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.", - (int) *(data+3)); - return -1; - } - tor_assert(0); - break; - case 4: { /* socks4 */ - enum {socks4, socks4a} socks4_prot = socks4a; - const char *authstart, *authend; - /* http://ss5.sourceforge.net/socks4.protocol.txt */ - /* http://ss5.sourceforge.net/socks4A.protocol.txt */ - - req->socks_version = 4; - if (datalen < SOCKS4_NETWORK_LEN) {/* basic info available? */ - *want_length_out = SOCKS4_NETWORK_LEN; - return 0; /* not yet */ - } - // buf_pullup(buf, 1280); - req->command = (unsigned char) *(data+1); - if (req->command != SOCKS_COMMAND_CONNECT && - req->command != SOCKS_COMMAND_RESOLVE) { - /* not a connect or resolve? we don't support it. (No resolve_ptr with - * socks4.) */ - log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.", - req->command); - return -1; - } - - req->port = ntohs(get_uint16(data+2)); - destip = ntohl(get_uint32(data+4)); - if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) { - log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting."); - return -1; - } - if (destip >> 8) { - log_debug(LD_APP,"socks4: destip not in form 0.0.0.x."); - in.s_addr = htonl(destip); - tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf)); - if (BUG(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN)) { - /* LCOV_EXCL_START -- This branch is unreachable, given the - * size of tmpbuf and the actual value of MAX_SOCKS_ADDR_LEN */ - log_debug(LD_APP,"socks4 addr (%d bytes) too long. Rejecting.", - (int)strlen(tmpbuf)); - return -1; - /* LCOV_EXCL_STOP */ - } - log_debug(LD_APP, - "socks4: successfully read destip (%s)", - safe_str_client(tmpbuf)); - socks4_prot = socks4; - } + first_octet = get_uint8(data); - authstart = data + SOCKS4_NETWORK_LEN; - next = memchr(authstart, 0, - datalen-SOCKS4_NETWORK_LEN); - if (!next) { - if (datalen >= 1024) { - log_debug(LD_APP, "Socks4 user name too long; rejecting."); - return -1; - } - log_debug(LD_APP,"socks4: Username not here yet."); - *want_length_out = datalen+1024; /* More than we need, but safe */ - return 0; - } - authend = next; - tor_assert(next < data+datalen); - - startaddr = NULL; - if (socks4_prot != socks4a && - !addressmap_have_mapping(tmpbuf,0)) { - log_unsafe_socks_warning(4, tmpbuf, req->port, safe_socks); + if (first_octet == SOCKS_VER_5 || first_octet == SOCKS_VER_4 || + first_octet == SOCKS_AUTH) { // XXX: RFC 1929 + return handle_socks_message((const uint8_t *)data, datalen, req, + log_sockstype, safe_socks, drain_out); + } - if (safe_socks) - return -1; - } - if (socks4_prot == socks4a) { - if (next+1 == data+datalen) { - log_debug(LD_APP,"socks4: No part of destaddr here yet."); - *want_length_out = datalen + 1024; /* More than we need, but safe */ - return 0; - } - startaddr = next+1; - next = memchr(startaddr, 0, data + datalen - startaddr); - if (!next) { - if (datalen >= 1024) { - log_debug(LD_APP,"socks4: Destaddr too long."); - return -1; - } - log_debug(LD_APP,"socks4: Destaddr not all here yet."); - *want_length_out = datalen + 1024; /* More than we need, but safe */ - return 0; - } - if (MAX_SOCKS_ADDR_LEN <= next-startaddr) { - log_warn(LD_APP,"socks4: Destaddr too long. Rejecting."); - return -1; - } - // tor_assert(next < buf->cur+buf_datalen(buf)); - - if (log_sockstype) - log_notice(LD_APP, - "Your application (using socks4a to port %d) instructed " - "Tor to take care of the DNS resolution itself if " - "necessary. This is good.", req->port); - } - log_debug(LD_APP,"socks4: Everything is here. Success."); - strlcpy(req->address, startaddr ? startaddr : tmpbuf, - sizeof(req->address)); - if (!string_is_valid_dest(req->address)) { - log_warn(LD_PROTOCOL, - "Your application (using socks4 to port %d) gave Tor " - "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped_safe_str_client(req->address)); - return -1; - } - if (authend != authstart) { - req->got_auth = 1; - req->usernamelen = authend - authstart; - req->username = tor_memdup(authstart, authend - authstart); - } - /* next points to the final \0 on inbuf */ - *drain_out = next - data + 1; - return 1; - } + switch (first_octet) { /* which version of socks? */ case 'G': /* get */ case 'H': /* head */ case 'P': /* put/post */ @@ -561,6 +971,9 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, } return -1; } + + tor_assert_unreached(); + return -1; } /** Inspect a reply from SOCKS server stored in <b>buf</b> according diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c index 8b2c71bebf..2a475bd917 100644 --- a/src/ext/tinytest.c +++ b/src/ext/tinytest.c @@ -152,7 +152,7 @@ testcase_run_forked_(const struct testgroup_t *group, if (opt_verbosity>0) printf("[forking] "); - snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", + snprintf(buffer, sizeof(buffer), "\"%s\" --RUNNING-FORKED %s %s%s", commandname, verbosity_flag, group->prefix, testcase->name); memset(&si, 0, sizeof(si)); diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index 664be8ce11..494ad33528 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -406,6 +406,17 @@ get_remove_unlisted_guards_after_days(void) DFLT_REMOVE_UNLISTED_GUARDS_AFTER_DAYS, 1, 365*10); } + +/** + * Return number of seconds that will make a guard no longer eligible + * for selection if unlisted for this long. + */ +static time_t +get_remove_unlisted_guards_after_seconds(void) +{ + return get_remove_unlisted_guards_after_days() * 24 * 60 * 60; +} + /** * We remove unconfirmed guards from the sample after this many days, * regardless of whether they are listed or unlisted. @@ -1237,30 +1248,28 @@ entry_guard_is_listed,(guard_selection_t *gs, const entry_guard_t *guard)) } /** - * Update the status of all sampled guards based on the arrival of a - * new consensus networkstatus document. This will include marking - * some guards as listed or unlisted, and removing expired guards. */ -STATIC void -sampled_guards_update_from_consensus(guard_selection_t *gs) + * Enumerate <b>sampled_entry_guards</b> smartlist in <b>gs</b>. + * For each <b>entry_guard_t</b> object in smartlist, do the following: + * * Update <b>currently_listed</b> field to reflect if guard is listed + * in guard selection <b>gs</b>. + * * Set <b>unlisted_since_date</b> to approximate UNIX time of + * unlisting if guard is unlisted (randomize within 20% of + * get_remove_unlisted_guards_after_seconds()). Otherwise, + * set it to 0. + * + * Require <b>gs</b> to be non-null pointer. + * Return a number of entries updated. + */ +static size_t +sampled_guards_update_consensus_presence(guard_selection_t *gs) { - tor_assert(gs); - const int REMOVE_UNLISTED_GUARDS_AFTER = - (get_remove_unlisted_guards_after_days() * 86400); - const int unlisted_since_slop = REMOVE_UNLISTED_GUARDS_AFTER / 5; + size_t n_changes = 0; - // It's important to use only a live consensus here; we don't want to - // make changes based on anything expired or old. - if (live_consensus_is_missing(gs)) { - log_info(LD_GUARD, "Not updating the sample guard set; we have " - "no live consensus."); - return; - } - log_info(LD_GUARD, "Updating sampled guard status based on received " - "consensus."); + tor_assert(gs); - int n_changes = 0; + const time_t unlisted_since_slop = + get_remove_unlisted_guards_after_seconds() / 5; - /* First: Update listed/unlisted. */ SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { /* XXXX #20827 check ed ID too */ const int is_listed = entry_guard_is_listed(gs, guard); @@ -1304,14 +1313,33 @@ sampled_guards_update_from_consensus(guard_selection_t *gs) } } SMARTLIST_FOREACH_END(guard); - const time_t remove_if_unlisted_since = - approx_time() - REMOVE_UNLISTED_GUARDS_AFTER; - const time_t maybe_remove_if_sampled_before = - approx_time() - get_guard_lifetime(); - const time_t remove_if_confirmed_before = - approx_time() - get_guard_confirmed_min_lifetime(); + return n_changes; +} + +/** + * Enumerate <b>sampled_entry_guards</b> smartlist in <b>gs</b>. + * For each <b>entry_guard_t</b> object in smartlist, do the following: + * * If <b>currently_listed</b> is false and <b>unlisted_since_date</b> + * is earlier than <b>remove_if_unlisted_since</b> - remove it. + * * Otherwise, check if <b>sampled_on_date</b> is earlier than + * <b>maybe_remove_if_sampled_before</b>. + * * When above condition is correct, remove the guard if: + * * It was never confirmed. + * * It was confirmed before <b>remove_if_confirmed_before</b>. + * + * Require <b>gs</b> to be non-null pointer. + * Return number of entries deleted. + */ +static size_t +sampled_guards_prune_obsolete_entries(guard_selection_t *gs, + const time_t remove_if_unlisted_since, + const time_t maybe_remove_if_sampled_before, + const time_t remove_if_confirmed_before) +{ + size_t n_changes = 0; + + tor_assert(gs); - /* Then: remove the ones that have been junk for too long */ SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { int rmv = 0; @@ -1319,7 +1347,7 @@ sampled_guards_update_from_consensus(guard_selection_t *gs) guard->unlisted_since_date < remove_if_unlisted_since) { /* "We have a live consensus, and {IS_LISTED} is false, and - {FIRST_UNLISTED_AT} is over {REMOVE_UNLISTED_GUARDS_AFTER} + {FIRST_UNLISTED_AT} is over get_remove_unlisted_guards_after_days() days in the past." */ log_info(LD_GUARD, "Removing sampled guard %s: it has been unlisted " @@ -1355,6 +1383,45 @@ sampled_guards_update_from_consensus(guard_selection_t *gs) } } SMARTLIST_FOREACH_END(guard); + return n_changes; +} + +/** + * Update the status of all sampled guards based on the arrival of a + * new consensus networkstatus document. This will include marking + * some guards as listed or unlisted, and removing expired guards. */ +STATIC void +sampled_guards_update_from_consensus(guard_selection_t *gs) +{ + tor_assert(gs); + + // It's important to use only a live consensus here; we don't want to + // make changes based on anything expired or old. + if (live_consensus_is_missing(gs)) { + log_info(LD_GUARD, "Not updating the sample guard set; we have " + "no live consensus."); + return; + } + log_info(LD_GUARD, "Updating sampled guard status based on received " + "consensus."); + + /* First: Update listed/unlisted. */ + size_t n_changes = sampled_guards_update_consensus_presence(gs); + + const time_t remove_if_unlisted_since = + approx_time() - get_remove_unlisted_guards_after_seconds(); + const time_t maybe_remove_if_sampled_before = + approx_time() - get_guard_lifetime(); + const time_t remove_if_confirmed_before = + approx_time() - get_guard_confirmed_min_lifetime(); + + /* Then: remove the ones that have been junk for too long */ + n_changes += + sampled_guards_prune_obsolete_entries(gs, + remove_if_unlisted_since, + maybe_remove_if_sampled_before, + remove_if_confirmed_before); + if (n_changes) { gs->primary_guards_up_to_date = 0; entry_guards_update_filtered_sets(gs); @@ -1816,28 +1883,24 @@ entry_guards_update_primary(guard_selection_t *gs) smartlist_add(new_primary_guards, guard); } SMARTLIST_FOREACH_END(guard); - /* Can we keep any older primary guards? First remove all the ones - * that we already kept. */ SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { + /* Can we keep any older primary guards? First remove all the ones + * that we already kept. */ if (smartlist_contains(new_primary_guards, guard)) { SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard); - } - } SMARTLIST_FOREACH_END(guard); - - /* Now add any that are still good. */ - SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { - if (smartlist_len(new_primary_guards) >= N_PRIMARY_GUARDS) - break; - if (! guard->is_filtered_guard) continue; - guard->is_primary = 1; - smartlist_add(new_primary_guards, guard); - SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard); - } SMARTLIST_FOREACH_END(guard); + } - /* Mark the remaining previous primary guards as non-primary */ - SMARTLIST_FOREACH_BEGIN(old_primary_guards, entry_guard_t *, guard) { - guard->is_primary = 0; + /* Now add any that are still good. */ + if (smartlist_len(new_primary_guards) < N_PRIMARY_GUARDS && + guard->is_filtered_guard) { + guard->is_primary = 1; + smartlist_add(new_primary_guards, guard); + SMARTLIST_DEL_CURRENT_KEEPORDER(old_primary_guards, guard); + } else { + /* Mark the remaining previous primary guards as non-primary */ + guard->is_primary = 0; + } } SMARTLIST_FOREACH_END(guard); /* Finally, fill out the list with sampled guards. */ @@ -1861,18 +1924,8 @@ entry_guards_update_primary(guard_selection_t *gs) }); #endif /* 1 */ - int any_change = 0; - if (smartlist_len(gs->primary_entry_guards) != - smartlist_len(new_primary_guards)) { - any_change = 1; - } else { - SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, g) { - if (g != smartlist_get(new_primary_guards, g_sl_idx)) { - any_change = 1; - } - } SMARTLIST_FOREACH_END(g); - } - + const int any_change = !smartlist_ptrs_eq(gs->primary_entry_guards, + new_primary_guards); if (any_change) { log_info(LD_GUARD, "Primary entry guards have changed. " "New primary guard list is: "); @@ -1974,31 +2027,23 @@ entry_guards_note_internet_connectivity(guard_selection_t *gs) } /** - * Get a guard for use with a circuit. Prefer to pick a running primary - * guard; then a non-pending running filtered confirmed guard; then a - * non-pending runnable filtered guard. Update the + * Pick a primary guard for use with a circuit, if available. Update the * <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the * guard as appropriate. Set <b>state_out</b> to the new guard-state * of the circuit. */ -STATIC entry_guard_t * -select_entry_guard_for_circuit(guard_selection_t *gs, - guard_usage_t usage, - const entry_guard_restriction_t *rst, - unsigned *state_out) +static entry_guard_t * +select_primary_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out) { const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC); - tor_assert(gs); - tor_assert(state_out); - - if (!gs->primary_guards_up_to_date) - entry_guards_update_primary(gs); + entry_guard_t *chosen_guard = NULL; int num_entry_guards = get_n_primary_guards_to_use(usage); smartlist_t *usable_primary_guards = smartlist_new(); - /* "If any entry in PRIMARY_GUARDS has {is_reachable} status of - <maybe> or <yes>, return the first such guard." */ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard); if (! entry_guard_obeys_restriction(guard, rst)) @@ -2016,18 +2061,30 @@ select_entry_guard_for_circuit(guard_selection_t *gs, } SMARTLIST_FOREACH_END(guard); if (smartlist_len(usable_primary_guards)) { - entry_guard_t *guard = smartlist_choose(usable_primary_guards); + chosen_guard = smartlist_choose(usable_primary_guards); smartlist_free(usable_primary_guards); log_info(LD_GUARD, "Selected primary guard %s for circuit.", - entry_guard_describe(guard)); - return guard; + entry_guard_describe(chosen_guard)); } + smartlist_free(usable_primary_guards); + return chosen_guard; +} + +/** + * For use with a circuit, pick a non-pending running filtered confirmed guard, + * if one is available. Update the <b>last_tried_to_connect</b> time and the + * <b>is_pending</b> fields of the guard as appropriate. Set <b>state_out</b> + * to the new guard-state of the circuit. + */ +static entry_guard_t * +select_confirmed_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out) +{ + const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC); - /* "Otherwise, if the ordered intersection of {CONFIRMED_GUARDS} - and {USABLE_FILTERED_GUARDS} is nonempty, return the first - entry in that intersection that has {is_pending} set to - false." */ SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { if (guard->is_primary) continue; /* we already considered this one. */ @@ -2048,34 +2105,93 @@ select_entry_guard_for_circuit(guard_selection_t *gs, } } SMARTLIST_FOREACH_END(guard); + return NULL; +} + +/** + * For use with a circuit, pick a confirmed usable filtered guard + * at random. Update the <b>last_tried_to_connect</b> time and the + * <b>is_pending</b> fields of the guard as appropriate. Set <b>state_out</b> + * to the new guard-state of the circuit. + */ +static entry_guard_t * +select_filtered_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out) +{ + const int need_descriptor = (usage == GUARD_USAGE_TRAFFIC); + entry_guard_t *chosen_guard = NULL; + unsigned flags = 0; + if (need_descriptor) + flags |= SAMPLE_EXCLUDE_NO_DESCRIPTOR; + chosen_guard = sample_reachable_filtered_entry_guards(gs, + rst, + SAMPLE_EXCLUDE_CONFIRMED | + SAMPLE_EXCLUDE_PRIMARY | + SAMPLE_EXCLUDE_PENDING | + flags); + if (!chosen_guard) { + return NULL; + } + + chosen_guard->is_pending = 1; + chosen_guard->last_tried_to_connect = approx_time(); + *state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; + log_info(LD_GUARD, "No primary or confirmed guards available. Selected " + "random guard %s for circuit. Will try other guards before " + "using this circuit.", + entry_guard_describe(chosen_guard)); + return chosen_guard; +} + +/** + * Get a guard for use with a circuit. Prefer to pick a running primary + * guard; then a non-pending running filtered confirmed guard; then a + * non-pending runnable filtered guard. Update the + * <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the + * guard as appropriate. Set <b>state_out</b> to the new guard-state + * of the circuit. + */ +STATIC entry_guard_t * +select_entry_guard_for_circuit(guard_selection_t *gs, + guard_usage_t usage, + const entry_guard_restriction_t *rst, + unsigned *state_out) +{ + entry_guard_t *chosen_guard = NULL; + tor_assert(gs); + tor_assert(state_out); + + if (!gs->primary_guards_up_to_date) + entry_guards_update_primary(gs); + + /* "If any entry in PRIMARY_GUARDS has {is_reachable} status of + <maybe> or <yes>, return the first such guard." */ + chosen_guard = select_primary_guard_for_circuit(gs, usage, rst, state_out); + if (chosen_guard) + return chosen_guard; + + /* "Otherwise, if the ordered intersection of {CONFIRMED_GUARDS} + and {USABLE_FILTERED_GUARDS} is nonempty, return the first + entry in that intersection that has {is_pending} set to + false." */ + chosen_guard = select_confirmed_guard_for_circuit(gs, usage, rst, state_out); + if (chosen_guard) + return chosen_guard; + /* "Otherwise, if there is no such entry, select a member at random from {USABLE_FILTERED_GUARDS}." */ - { - entry_guard_t *guard; - unsigned flags = 0; - if (need_descriptor) - flags |= SAMPLE_EXCLUDE_NO_DESCRIPTOR; - guard = sample_reachable_filtered_entry_guards(gs, - rst, - SAMPLE_EXCLUDE_CONFIRMED | - SAMPLE_EXCLUDE_PRIMARY | - SAMPLE_EXCLUDE_PENDING | - flags); - if (guard == NULL) { - log_info(LD_GUARD, "Absolutely no sampled guards were available. " - "Marking all guards for retry and starting from top again."); - mark_all_guards_maybe_reachable(gs); - return NULL; - } - guard->is_pending = 1; - guard->last_tried_to_connect = approx_time(); - *state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; - log_info(LD_GUARD, "No primary or confirmed guards available. Selected " - "random guard %s for circuit. Will try other guards before " - "using this circuit.", - entry_guard_describe(guard)); - return guard; + chosen_guard = select_filtered_guard_for_circuit(gs, usage, rst, state_out); + + if (chosen_guard == NULL) { + log_info(LD_GUARD, "Absolutely no sampled guards were available. " + "Marking all guards for retry and starting from top again."); + mark_all_guards_maybe_reachable(gs); + return NULL; } + + return chosen_guard; } /** diff --git a/src/feature/control/control.c b/src/feature/control/control.c index 12f10926b7..f22df30e11 100644 --- a/src/feature/control/control.c +++ b/src/feature/control/control.c @@ -2011,6 +2011,8 @@ getinfo_helper_listeners(control_connection_t *control_conn, if (!strcmp(question, "net/listeners/or")) type = CONN_TYPE_OR_LISTENER; + else if (!strcmp(question, "net/listeners/extor")) + type = CONN_TYPE_EXT_OR_LISTENER; else if (!strcmp(question, "net/listeners/dir")) type = CONN_TYPE_DIR_LISTENER; else if (!strcmp(question, "net/listeners/socks")) @@ -2019,6 +2021,8 @@ getinfo_helper_listeners(control_connection_t *control_conn, type = CONN_TYPE_AP_TRANS_LISTENER; else if (!strcmp(question, "net/listeners/natd")) type = CONN_TYPE_AP_NATD_LISTENER; + else if (!strcmp(question, "net/listeners/httptunnel")) + type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER; else if (!strcmp(question, "net/listeners/dns")) type = CONN_TYPE_AP_DNS_LISTENER; else if (!strcmp(question, "net/listeners/control")) diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index ce67c1bb9a..6477f34baf 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -15,6 +15,7 @@ #include "feature/nodelist/parsecommon.h" #include "core/or/policies.h" #include "core/or/protover.h" +#include "core/or/tor_version_st.h" #include "feature/stats/rephist.h" #include "feature/relay/router.h" #include "feature/relay/routerkeys.h" @@ -254,6 +255,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, /* XXXX Abstraction violation: should be pulling a field out of v3_ns.*/ char *flag_thresholds = dirserv_get_flag_thresholds_line(); char *params; + char *bw_headers_line = NULL; authority_cert_t *cert = v3_ns->cert; char *methods = make_consensus_method_list(MIN_SUPPORTED_CONSENSUS_METHOD, @@ -267,8 +269,32 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, params = smartlist_join_strings(v3_ns->net_params, " ", 0, NULL); else params = tor_strdup(""); - tor_assert(cert); + + /* v3_ns->bw_file_headers is only set when V3BandwidthsFile is + * configured */ + if (v3_ns->bw_file_headers) { + char *bw_file_headers = NULL; + /* If there are too many headers, leave the header string NULL */ + if (! BUG(smartlist_len(v3_ns->bw_file_headers) + > MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) { + bw_file_headers = smartlist_join_strings(v3_ns->bw_file_headers, " ", + 0, NULL); + if (BUG(strlen(bw_file_headers) > MAX_BW_FILE_HEADERS_LINE_LEN)) { + /* Free and set to NULL, because the line was too long */ + tor_free(bw_file_headers); + } + } + if (!bw_file_headers) { + /* If parsing failed, add a bandwidth header line with no entries */ + bw_file_headers = tor_strdup(""); + } + /* At this point, the line will always be present */ + bw_headers_line = format_line_if_present("bandwidth-file-headers", + bw_file_headers); + tor_free(bw_file_headers); + } + smartlist_add_asprintf(chunks, "network-status-version 3\n" "vote-status %s\n" @@ -286,7 +312,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, "params %s\n" "dir-source %s %s %s %s %d %d\n" "contact %s\n" - "%s", /* shared randomness information */ + "%s" /* shared randomness information */ + "%s" /* bandwidth file headers */ + , v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion", methods, published, va, fu, vu, @@ -302,13 +330,16 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, fmt_addr32(addr), voter->dir_port, voter->or_port, voter->contact, shared_random_vote_str ? - shared_random_vote_str : ""); + shared_random_vote_str : "", + bw_headers_line ? + bw_headers_line : ""); tor_free(params); tor_free(flags); tor_free(flag_thresholds); tor_free(methods); tor_free(shared_random_vote_str); + tor_free(bw_headers_line); if (!tor_digest_is_zero(voter->legacy_id_digest)) { char fpbuf[HEX_DIGEST_LEN+1]; @@ -797,6 +828,14 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) int min = n_versioning / 2; smartlist_t *good = smartlist_new(); char *result; + SMARTLIST_FOREACH_BEGIN(lst, const char *, v) { + if (strchr(v, ' ')) { + log_warn(LD_DIR, "At least one authority has voted for a version %s " + "that contains a space. This probably wasn't intentional, and " + "is likely to cause trouble. Please tell them to stop it.", + escaped(v)); + } + } SMARTLIST_FOREACH_END(v); sort_version_list(lst, 0); get_frequent_members(good, lst, min); result = smartlist_join_strings(good, ",", 0, NULL); @@ -4200,8 +4239,8 @@ version_from_platform(const char *platform) * allocate and return a new string containing the version numbers, in order, * separated by commas. Used to generate Recommended(Client|Server)?Versions */ -static char * -format_versions_list(config_line_t *ln) +char * +format_recommended_version_list(const config_line_t *ln, int warn) { smartlist_t *versions; char *result; @@ -4210,6 +4249,37 @@ format_versions_list(config_line_t *ln) smartlist_split_string(versions, ln->value, ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); } + + /* Handle the case where a dirauth operator has accidentally made some + * versions space-separated instead of comma-separated. */ + smartlist_t *more_versions = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(versions, char *, v) { + if (strchr(v, ' ')) { + if (warn) + log_warn(LD_DIRSERV, "Unexpected space in versions list member %s. " + "(These are supposed to be comma-separated; I'll pretend you " + "used commas instead.)", escaped(v)); + SMARTLIST_DEL_CURRENT(versions, v); + smartlist_split_string(more_versions, v, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tor_free(v); + } + } SMARTLIST_FOREACH_END(v); + smartlist_add_all(versions, more_versions); + smartlist_free(more_versions); + + /* Check to make sure everything looks like a version. */ + if (warn) { + SMARTLIST_FOREACH_BEGIN(versions, const char *, v) { + tor_version_t ver; + if (tor_version_parse(v, &ver) < 0) { + log_warn(LD_DIRSERV, "Recommended version %s does not look valid. " + " (I'll include it anyway, since you told me to.)", + escaped(v)); + } + } SMARTLIST_FOREACH_END(v); + } + sort_version_list(versions, 1); result = smartlist_join_strings(versions,",",0,NULL); SMARTLIST_FOREACH(versions,char *,s,tor_free(s)); @@ -4303,6 +4373,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, digestmap_t *omit_as_sybil = NULL; const int vote_on_reachability = running_long_enough_to_decide_unreachable(); smartlist_t *microdescriptors = NULL; + smartlist_t *bw_file_headers = NULL; tor_assert(private_key); tor_assert(cert); @@ -4325,8 +4396,10 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, } if (options->VersioningAuthoritativeDir) { - client_versions = format_versions_list(options->RecommendedClientVersions); - server_versions = format_versions_list(options->RecommendedServerVersions); + client_versions = + format_recommended_version_list(options->RecommendedClientVersions, 0); + server_versions = + format_recommended_version_list(options->RecommendedServerVersions, 0); } contact = get_options()->ContactInfo; @@ -4338,7 +4411,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, * set_routerstatus_from_routerinfo() see up-to-date bandwidth info. */ if (options->V3BandwidthsFile) { - dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL); + dirserv_read_measured_bandwidths(options->V3BandwidthsFile, NULL, NULL); } else { /* * No bandwidths file; clear the measured bandwidth cache in case we had @@ -4440,8 +4513,10 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* This pass through applies the measured bw lines to the routerstatuses */ if (options->V3BandwidthsFile) { + /* Only set bw_file_headers when V3BandwidthsFile is configured */ + bw_file_headers = smartlist_new(); dirserv_read_measured_bandwidths(options->V3BandwidthsFile, - routerstatuses); + routerstatuses, bw_file_headers); } else { /* * No bandwidths file; clear the measured bandwidth cache in case we had @@ -4537,6 +4612,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, options->ConsensusParams, NULL, 0, 0); smartlist_sort_strings(v3_out->net_params); } + v3_out->bw_file_headers = bw_file_headers; voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup(options->Nickname); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index 7ce8e4a699..979a2be8a6 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -89,6 +89,9 @@ #define DGV_INCLUDE_PENDING 2 #define DGV_INCLUDE_PREVIOUS 4 +/** Maximum size of a line in a vote. */ +#define MAX_BW_FILE_HEADERS_LINE_LEN 1024 + /* * Public API. Used outside of the dirauth subsystem. * diff --git a/src/feature/dircache/dirserv.c b/src/feature/dircache/dirserv.c index 1500467ec0..b85db8324f 100644 --- a/src/feature/dircache/dirserv.c +++ b/src/feature/dircache/dirserv.c @@ -51,6 +51,7 @@ #include "lib/crypt_ops/crypto_format.h" #include "lib/encoding/confline.h" +#include "lib/encoding/keyval.h" /** * \file dirserv.c * \brief Directory server core implementation. Manages directory @@ -2599,12 +2600,14 @@ measured_bw_line_apply(measured_bw_line_t *parsed_line, } /** - * Read the measured bandwidth file and apply it to the list of - * vote_routerstatus_t. Returns -1 on error, 0 otherwise. + * Read the measured bandwidth list file, apply it to the list of + * vote_routerstatus_t and store all the headers in <b>bw_file_headers</b>. + * Returns -1 on error, 0 otherwise. */ int dirserv_read_measured_bandwidths(const char *from_file, - smartlist_t *routerstatuses) + smartlist_t *routerstatuses, + smartlist_t *bw_file_headers) { FILE *fp = tor_fopen_cloexec(from_file, "r"); int applied_lines = 0; @@ -2654,6 +2657,12 @@ dirserv_read_measured_bandwidths(const char *from_file, goto err; } + /* If timestamp was correct and bw_file_headers is not NULL, + * add timestamp to bw_file_headers */ + if (bw_file_headers) + smartlist_add_asprintf(bw_file_headers, "timestamp=%lu", + (unsigned long)file_time); + if (routerstatuses) smartlist_sort(routerstatuses, compare_vote_routerstatus_entries); @@ -2669,7 +2678,24 @@ dirserv_read_measured_bandwidths(const char *from_file, dirserv_cache_measured_bw(&parsed_line, file_time); if (measured_bw_line_apply(&parsed_line, routerstatuses) > 0) applied_lines++; - } + /* if the terminator is found, it is the end of header lines, set the + * flag but do not store anything */ + } else if (strcmp(line, BW_FILE_HEADERS_TERMINATOR) == 0) { + line_is_after_headers = 1; + /* if the line was not a correct relay line nor the terminator and + * the end of the header lines has not been detected yet + * and it is key_value and bw_file_headers did not reach the maximum + * number of headers, + * then assume this line is a header and add it to bw_file_headers */ + } else if (bw_file_headers && + (line_is_after_headers == 0) && + string_is_key_value(LOG_DEBUG, line) && + !strchr(line, ' ') && + (smartlist_len(bw_file_headers) + < MAX_BW_FILE_HEADER_COUNT_IN_VOTE)) { + line[strlen(line)-1] = '\0'; + smartlist_add_strdup(bw_file_headers, line); + }; } } diff --git a/src/feature/dircache/dirserv.h b/src/feature/dircache/dirserv.h index 3b4a646094..9be4bf9db2 100644 --- a/src/feature/dircache/dirserv.h +++ b/src/feature/dircache/dirserv.h @@ -49,6 +49,13 @@ typedef enum { /** Maximum allowable length of a version line in a networkstatus. */ #define MAX_V_LINE_LEN 128 +/** Maximum allowable length of bandwidth headers in a bandwidth file */ +#define MAX_BW_FILE_HEADER_COUNT_IN_VOTE 50 + +/** Terminatore that separates bandwidth file headers from bandwidth file + * relay lines */ +#define BW_FILE_HEADERS_TERMINATOR "=====\n" + /** Ways to convert a spoolable_resource_t to a bunch of bytes. */ typedef enum dir_spool_source_t { DIR_SPOOL_SERVER_BY_DIGEST=1, DIR_SPOOL_SERVER_BY_FP, @@ -180,7 +187,9 @@ char *routerstatus_format_entry( void dirserv_free_all(void); void cached_dir_decref(cached_dir_t *d); cached_dir_t *new_cached_dir(char *s, time_t published); - +struct config_line_t; +char *format_recommended_version_list(const struct config_line_t *line, + int warn); int validate_recommended_package_line(const char *line); int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_out, @@ -215,7 +224,8 @@ dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str, #endif /* defined(DIRSERV_PRIVATE) */ int dirserv_read_measured_bandwidths(const char *from_file, - smartlist_t *routerstatuses); + smartlist_t *routerstatuses, + smartlist_t *bw_file_headers); int dirserv_read_guardfraction_file(const char *fname, smartlist_t *vote_routerstatuses); diff --git a/src/feature/dircommon/voting_schedule.c b/src/feature/dircommon/voting_schedule.c index 84c016c2b9..07e65ef06d 100644 --- a/src/feature/dircommon/voting_schedule.c +++ b/src/feature/dircommon/voting_schedule.c @@ -168,7 +168,7 @@ voting_schedule_get_next_valid_after_time(void) done: if (need_to_recalculate_voting_schedule) { - voting_schedule_recalculate_timing(get_options(), now); + voting_schedule_recalculate_timing(get_options(), approx_time()); voting_schedule.created_on_demand = 1; } diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index cd312e98be..541b165dd5 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -566,10 +566,14 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) return; } -/* Add all possible link specifiers in node to lspecs. - * legacy ID is mandatory thus MUST be present in node. If the primary address - * is not IPv4, log a BUG() warning, and return an empty smartlist. - * Includes ed25519 id and IPv6 link specifiers if present in the node. */ +/* Add all possible link specifiers in node to lspecs: + * - legacy ID is mandatory thus MUST be present in node; + * - include ed25519 link specifier if present in the node, and the node + * supports ed25519 link authentication, even if its link versions are not + * compatible with us; + * - include IPv4 link specifier, if the primary address is not IPv4, log a + * BUG() warning, and return an empty smartlist; + * - include IPv6 link specifier if present in the node. */ static void get_lspecs_from_node(const node_t *node, smartlist_t *lspecs) { @@ -607,8 +611,12 @@ get_lspecs_from_node(const node_t *node, smartlist_t *lspecs) link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is only included if the node has it. */ - if (!ed25519_public_key_is_zero(&node->ed25519_id)) { + /* ed25519 ID is only included if the node has it, and the node declares a + protocol version that supports ed25519 link authentication, even if that + link version is not compatible with us. (We are sending the ed25519 key + to another tor, which may support different link versions.) */ + if (!ed25519_public_key_is_zero(&node->ed25519_id) && + node_supports_ed25519_link_authentication(node, 0)) { ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_ED25519_ID); memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id, diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index 723cfa6ea8..12405a79cb 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -254,8 +254,8 @@ get_time_period_length(void) HS_TIME_PERIOD_LENGTH_MIN, HS_TIME_PERIOD_LENGTH_MAX); /* Make sure it's a positive value. */ - tor_assert(time_period_length >= 0); - /* uint64_t will always be able to contain a int32_t */ + tor_assert(time_period_length > 0); + /* uint64_t will always be able to contain a positive int32_t */ return (uint64_t) time_period_length; } @@ -844,7 +844,7 @@ hs_get_subcredential(const ed25519_public_key_t *identity_pk, memwipe(credential, 0, sizeof(credential)); } -/* From the given list of hidden service ports, find the ones that much the +/* From the given list of hidden service ports, find the ones that match the * given edge connection conn, pick one at random and use it to set the * connection address. Return 0 on success or -1 if none. */ int @@ -1102,8 +1102,7 @@ hs_in_period_between_tp_and_srv,(const networkstatus_t *consensus, time_t now)) /* Get start time of next TP and of current SRV protocol run, and check if we * are between them. */ valid_after = consensus->valid_after; - srv_start_time = - sr_state_get_start_time_of_current_protocol_run(valid_after); + srv_start_time = sr_state_get_start_time_of_current_protocol_run(); tp_start_time = hs_get_start_time_of_next_time_period(srv_start_time); if (valid_after >= srv_start_time && valid_after < tp_start_time) { diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index 8b4de21387..7775ac6de8 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -17,6 +17,7 @@ #include "core/mainloop/connection.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/crypt_ops/crypto_ope.h" #include "feature/dircache/directory.h" #include "core/mainloop/main.h" #include "feature/nodelist/networkstatus.h" @@ -102,7 +103,8 @@ static smartlist_t *hs_service_staging_list; static int consider_republishing_hs_descriptors = 0; /* Static declaration. */ -static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc); +static void set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc, + time_t now, bool is_current); static void move_descriptors(hs_service_t *src, hs_service_t *dst); /* Helper: Function to compare two objects in the service map. Return 1 if the @@ -408,17 +410,21 @@ service_intro_point_free_void(void *obj) } /* Return a newly allocated service intro point and fully initialized from the - * given extend_info_t ei if non NULL. If is_legacy is true, we also generate - * the legacy key. On error, NULL is returned. + * given extend_info_t ei if non NULL. + * If is_legacy is true, we also generate the legacy key. + * If supports_ed25519_link_handshake_any is true, we add the relay's ed25519 + * key to the link specifiers. * * If ei is NULL, returns a hs_service_intro_point_t with an empty link * specifier list and no onion key. (This is used for testing.) + * On any other error, NULL is returned. * * ei must be an extend_info_t containing an IPv4 address. (We will add supoort * for IPv6 in a later release.) When calling extend_info_from_node(), pass * 0 in for_direct_connection to make sure ei always has an IPv4 address. */ STATIC hs_service_intro_point_t * -service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) +service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy, + unsigned int supports_ed25519_link_handshake_any) { hs_desc_link_specifier_t *ls; hs_service_intro_point_t *ip; @@ -443,7 +449,7 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) if (BUG(intro_point_max_lifetime < intro_point_min_lifetime)) { goto err; } - ip->time_to_expire = time(NULL) + + ip->time_to_expire = approx_time() + crypto_rand_int_range(intro_point_min_lifetime,intro_point_max_lifetime); } @@ -489,10 +495,13 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) } smartlist_add(ip->base.link_specifiers, ls); - /* ed25519 identity key is optional for intro points */ - ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID); - if (ls) { - smartlist_add(ip->base.link_specifiers, ls); + /* ed25519 identity key is optional for intro points. If the node supports + * ed25519 link authentication, we include it. */ + if (supports_ed25519_link_handshake_any) { + ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID); + if (ls) { + smartlist_add(ip->base.link_specifiers, ls); + } } /* IPv6 is not supported in this release. */ @@ -1084,6 +1093,7 @@ service_descriptor_free_(hs_service_descriptor_t *desc) SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s)); smartlist_free(desc->previous_hsdirs); } + crypto_ope_free(desc->ope_cipher); tor_free(desc); } @@ -1388,13 +1398,30 @@ build_service_desc_plaintext(const hs_service_t *service, return ret; } +/** Compute the descriptor's OPE cipher for encrypting revision counters. */ +static crypto_ope_t * +generate_ope_cipher_for_desc(const hs_service_descriptor_t *hs_desc) +{ + /* Compute OPE key as H("rev-counter-generation" | blinded privkey) */ + uint8_t key[DIGEST256_LEN]; + crypto_digest_t *digest = crypto_digest256_new(DIGEST_SHA3_256); + const char ope_key_prefix[] = "rev-counter-generation"; + const ed25519_secret_key_t *eph_privkey = &hs_desc->blinded_kp.seckey; + crypto_digest_add_bytes(digest, ope_key_prefix, sizeof(ope_key_prefix)); + crypto_digest_add_bytes(digest, (char*)eph_privkey->seckey, + sizeof(eph_privkey->seckey)); + crypto_digest_get_digest(digest, (char *)key, sizeof(key)); + crypto_digest_free(digest); + + return crypto_ope_new(key); +} + /* For the given service and descriptor object, create the key material which * is the blinded keypair and the descriptor signing keypair. Return 0 on * success else -1 on error where the generated keys MUST be ignored. */ static int build_service_desc_keys(const hs_service_t *service, - hs_service_descriptor_t *desc, - uint64_t time_period_num) + hs_service_descriptor_t *desc) { int ret = 0; ed25519_keypair_t kp; @@ -1410,10 +1437,17 @@ build_service_desc_keys(const hs_service_t *service, memcpy(&kp.pubkey, &service->keys.identity_pk, sizeof(kp.pubkey)); memcpy(&kp.seckey, &service->keys.identity_sk, sizeof(kp.seckey)); /* Build blinded keypair for this time period. */ - hs_build_blinded_keypair(&kp, NULL, 0, time_period_num, &desc->blinded_kp); + hs_build_blinded_keypair(&kp, NULL, 0, desc->time_period_num, + &desc->blinded_kp); /* Let's not keep too much traces of our keys in memory. */ memwipe(&kp, 0, sizeof(kp)); + /* Compute the OPE cipher struct (it's tied to the current blinded key) */ + log_info(LD_GENERAL, + "Getting OPE for TP#%u", (unsigned) desc->time_period_num); + tor_assert_nonfatal(!desc->ope_cipher); + desc->ope_cipher = generate_ope_cipher_for_desc(desc); + /* No need for extra strong, this is a temporary key only for this * descriptor. Nothing long term. */ if (ed25519_keypair_generate(&desc->signing_kp, 0) < 0) { @@ -1444,10 +1478,12 @@ build_service_descriptor(hs_service_t *service, time_t now, tor_assert(desc_out); desc = service_descriptor_new(); + + /* Set current time period */ desc->time_period_num = time_period_num; /* Create the needed keys so we can setup the descriptor content. */ - if (build_service_desc_keys(service, desc, time_period_num) < 0) { + if (build_service_desc_keys(service, desc) < 0) { goto err; } /* Setup plaintext descriptor content. */ @@ -1459,9 +1495,6 @@ build_service_descriptor(hs_service_t *service, time_t now, goto err; } - /* Set the revision counter for this descriptor */ - set_descriptor_revision_counter(desc->desc); - /* Let's make sure that we've created a descriptor that can actually be * encoded properly. This function also checks if the encoded output is * decodable after. */ @@ -1627,8 +1660,12 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) tor_assert_nonfatal(!ed25519_public_key_is_zero(&info->ed_identity)); } - /* Create our objects and populate them with the node information. */ - ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node)); + /* Create our objects and populate them with the node information. + * We don't care if the intro's link auth is compatible with us, because + * we are sending the ed25519 key to a remote client via the descriptor. */ + ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node), + node_supports_ed25519_link_authentication(node, + 0)); if (ip == NULL) { goto err; } @@ -1769,7 +1806,6 @@ service_desc_schedule_upload(hs_service_descriptor_t *desc, /* Update the given descriptor from the given service. The possible update * actions includes: * - Picking missing intro points if needed. - * - Incrementing the revision counter if needed. */ static void update_service_descriptor(hs_service_t *service, @@ -1933,19 +1969,12 @@ cleanup_intro_points(hs_service_t *service, time_t now) /* Set the next rotation time of the descriptors for the given service for the * time now. */ static void -set_rotation_time(hs_service_t *service, time_t now) +set_rotation_time(hs_service_t *service) { - time_t valid_after; - const networkstatus_t *ns = networkstatus_get_live_consensus(now); - if (ns) { - valid_after = ns->valid_after; - } else { - valid_after = now; - } - tor_assert(service); + service->state.next_rotation_time = - sr_state_get_start_time_of_current_protocol_run(valid_after) + + sr_state_get_start_time_of_current_protocol_run() + sr_state_get_protocol_run_duration(); { @@ -2012,7 +2041,7 @@ should_rotate_descriptors(hs_service_t *service, time_t now) * will be freed, the next one put in as the current and finally the next * descriptor pointer is NULLified. */ static void -rotate_service_descriptors(hs_service_t *service, time_t now) +rotate_service_descriptors(hs_service_t *service) { if (service->desc_current) { /* Close all IP circuits for the descriptor. */ @@ -2027,7 +2056,7 @@ rotate_service_descriptors(hs_service_t *service, time_t now) service->desc_next = NULL; /* We've just rotated, set the next time for the rotation. */ - set_rotation_time(service, now); + set_rotation_time(service); } /* Rotate descriptors for each service if needed. A non existing current @@ -2055,7 +2084,7 @@ rotate_all_descriptors(time_t now) service->desc_current, service->desc_next, safe_str_client(service->onion_address)); - rotate_service_descriptors(service, now); + rotate_service_descriptors(service); } FOR_EACH_SERVICE_END; } @@ -2077,7 +2106,7 @@ run_housekeeping_event(time_t now) /* Set the next rotation time of the descriptors. If it's Oct 25th * 23:47:00, the next rotation time is when the next SRV is computed * which is at Oct 26th 00:00:00 that is in 13 minutes. */ - set_rotation_time(service, now); + set_rotation_time(service); } /* Cleanup invalid intro points from the service descriptor. */ @@ -2326,13 +2355,17 @@ upload_descriptor_to_hsdir(const hs_service_t *service, int is_next_desc = (service->desc_next == desc); const uint8_t *idx = (is_next_desc) ? hsdir->hsdir_index.store_second: hsdir->hsdir_index.store_first; + char *blinded_pubkey_log_str = + tor_strdup(hex_str((char*)&desc->blinded_kp.pubkey.pubkey, 32)); log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64 - " initiated upload request to %s with index %s", + " initiated upload request to %s with index %s (%s)", safe_str_client(service->onion_address), (is_next_desc) ? "next" : "current", desc->desc->plaintext_data.revision_counter, safe_str_client(node_describe(hsdir)), - safe_str_client(hex_str((const char *) idx, 32))); + safe_str_client(hex_str((const char *) idx, 32)), + safe_str_client(blinded_pubkey_log_str)); + tor_free(blinded_pubkey_log_str); /* Fire a UPLOAD control port event. */ hs_control_desc_event_upload(service->onion_address, hsdir->identity, @@ -2344,197 +2377,77 @@ upload_descriptor_to_hsdir(const hs_service_t *service, return; } -/** Return a newly-allocated string for our state file which contains revision - * counter information for <b>desc</b>. The format is: +/** Set the revision counter in <b>hs_desc</b>. We do this by encrypting a + * timestamp using an OPE scheme and using the ciphertext as our revision + * counter. * - * HidServRevCounter <blinded_pubkey> <rev_counter> - */ -STATIC char * -encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc) -{ - char *state_str = NULL; - char blinded_pubkey_b64[ED25519_BASE64_LEN+1]; - uint64_t rev_counter = desc->desc->plaintext_data.revision_counter; - const ed25519_public_key_t *blinded_pubkey = &desc->blinded_kp.pubkey; - - /* Turn the blinded key into b64 so that we save it on state */ - tor_assert(blinded_pubkey); - if (ed25519_public_to_base64(blinded_pubkey_b64, blinded_pubkey) < 0) { - goto done; - } - - /* Format is: <blinded key> <rev counter> */ - tor_asprintf(&state_str, "%s %" PRIu64, blinded_pubkey_b64, rev_counter); - - log_info(LD_GENERAL, "[!] Adding rev counter %" PRIu64 " for %s!", - rev_counter, blinded_pubkey_b64); - - done: - return state_str; -} - -/** Update HS descriptor revision counters in our state by removing the old - * ones and writing down the ones that are currently active. */ + * If <b>is_current</b> is true, then this is the current HS descriptor, + * otherwise it's the next one. */ static void -update_revision_counters_in_state(void) -{ - config_line_t *lines = NULL; - config_line_t **nextline = &lines; - or_state_t *state = get_or_state(); - - /* Prepare our state structure with the rev counters */ - FOR_EACH_SERVICE_BEGIN(service) { - FOR_EACH_DESCRIPTOR_BEGIN(service, desc) { - /* We don't want to save zero counters */ - if (desc->desc->plaintext_data.revision_counter == 0) { - continue; - } - - *nextline = tor_malloc_zero(sizeof(config_line_t)); - (*nextline)->key = tor_strdup("HidServRevCounter"); - (*nextline)->value = encode_desc_rev_counter_for_state(desc); - nextline = &(*nextline)->next; - } FOR_EACH_DESCRIPTOR_END; - } FOR_EACH_SERVICE_END; - - /* Remove the old rev counters, and replace them with the new ones */ - config_free_lines(state->HidServRevCounter); - state->HidServRevCounter = lines; - - /* Set the state as dirty since we just edited it */ - if (!get_options()->AvoidDiskWrites) { - or_state_mark_dirty(state, 0); - } -} - -/** Scan the string <b>state_line</b> for the revision counter of the service - * with <b>blinded_pubkey</b>. Set <b>service_found_out</b> to True if the - * line is relevant to this service, and return the cached revision - * counter. Else set <b>service_found_out</b> to False. */ -STATIC uint64_t -check_state_line_for_service_rev_counter(const char *state_line, - const ed25519_public_key_t *blinded_pubkey, - int *service_found_out) +set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc, time_t now, + bool is_current) { - smartlist_t *items = NULL; - int ok; - ed25519_public_key_t pubkey_in_state; uint64_t rev_counter = 0; - tor_assert(service_found_out); - tor_assert(state_line); - tor_assert(blinded_pubkey); + /* Get current time */ + time_t srv_start = 0; - /* Assume that the line is not for this service */ - *service_found_out = 0; - - /* Start parsing the state line */ - items = smartlist_new(); - smartlist_split_string(items, state_line, NULL, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); - if (smartlist_len(items) < 2) { - log_warn(LD_GENERAL, "Incomplete rev counter line. Ignoring."); - goto done; - } - - char *b64_key_str = smartlist_get(items, 0); - char *saved_rev_counter_str = smartlist_get(items, 1); - - /* Parse blinded key to check if it's for this hidden service */ - if (ed25519_public_from_base64(&pubkey_in_state, b64_key_str) < 0) { - log_warn(LD_GENERAL, "Unable to base64 key in revcount line. Ignoring."); - goto done; - } - /* State line not for this hidden service */ - if (!ed25519_pubkey_eq(&pubkey_in_state, blinded_pubkey)) { - goto done; - } - - rev_counter = tor_parse_uint64(saved_rev_counter_str, - 10, 0, UINT64_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_GENERAL, "Unable to parse rev counter. Ignoring."); - goto done; + /* As our revision counter plaintext value, we use the seconds since the + * start of the SR protocol run that is relevant to this descriptor. This is + * guaranteed to be a positive value since we need the SRV to start making a + * descriptor (so that we know where to upload it). + * + * Depending on whether we are building the current or the next descriptor, + * services use a different SRV value. See [SERVICEUPLOAD] in + * rend-spec-v3.txt: + * + * In particular, for the current descriptor (aka first descriptor), Tor + * always uses the previous SRV for uploading the descriptor, and hence we + * should use the start time of the previous protocol run here. + * + * Whereas for the next descriptor (aka second descriptor), Tor always uses + * the current SRV for uploading the descriptor. and hence we use the start + * time of the current protocol run. + */ + if (is_current) { + srv_start = sr_state_get_start_time_of_previous_protocol_run(); + } else { + srv_start = sr_state_get_start_time_of_current_protocol_run(); } - /* Since we got this far, the line was for this service */ - *service_found_out = 1; - - log_info(LD_GENERAL, "Found rev counter for %s: %" PRIu64, - b64_key_str, rev_counter); - - done: - tor_assert(items); - SMARTLIST_FOREACH(items, char*, s, tor_free(s)); - smartlist_free(items); - - return rev_counter; -} - -/** Dig into our state file and find the current revision counter for the - * service with blinded key <b>blinded_pubkey</b>. If no revision counter is - * found, return 0. */ -static uint64_t -get_rev_counter_for_service(const ed25519_public_key_t *blinded_pubkey) -{ - or_state_t *state = get_or_state(); - config_line_t *line; + log_info(LD_REND, "Setting rev counter for TP #%u: " + "SRV started at %d, now %d (%s)", + (unsigned) hs_desc->time_period_num, (int)srv_start, + (int)now, is_current ? "current" : "next"); - /* Set default value for rev counters (if not found) to 0 */ - uint64_t final_rev_counter = 0; + tor_assert_nonfatal(now >= srv_start); - for (line = state->HidServRevCounter ; line ; line = line->next) { - int service_found = 0; - uint64_t rev_counter = 0; + /* Compute seconds elapsed since the start of the time period. That's the + * number of seconds of how long this blinded key has been active. */ + time_t seconds_since_start_of_srv = now - srv_start; - tor_assert(!strcmp(line->key, "HidServRevCounter")); + /* Increment by one so that we are definitely sure this is strictly + * positive and not zero. */ + seconds_since_start_of_srv++; - /* Scan all the HidServRevCounter lines till we find the line for this - service: */ - rev_counter = check_state_line_for_service_rev_counter(line->value, - blinded_pubkey, - &service_found); - if (service_found) { - final_rev_counter = rev_counter; - goto done; - } + /* Check for too big inputs. */ + if (BUG(seconds_since_start_of_srv > OPE_INPUT_MAX)) { + seconds_since_start_of_srv = OPE_INPUT_MAX; } - done: - return final_rev_counter; -} - -/** Update the value of the revision counter for <b>hs_desc</b> and save it on - our state file. */ -static void -increment_descriptor_revision_counter(hs_descriptor_t *hs_desc) -{ - /* Find stored rev counter if it exists */ - uint64_t rev_counter = - get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey); + /* Now we compute the final revision counter value by encrypting the + plaintext using our OPE cipher: */ + tor_assert(hs_desc->ope_cipher); + rev_counter = crypto_ope_encrypt(hs_desc->ope_cipher, + (int) seconds_since_start_of_srv); - /* Increment the revision counter of <b>hs_desc</b> so the next update (which - * will trigger an upload) will have the right value. We do this at this - * stage to only do it once because a descriptor can have many updates before - * being uploaded. By doing it at upload, we are sure to only increment by 1 - * and thus avoid leaking how many operations we made on the descriptor from - * the previous one before uploading. */ - rev_counter++; - hs_desc->plaintext_data.revision_counter = rev_counter; + /* The OPE module returns CRYPTO_OPE_ERROR in case of errors. */ + tor_assert_nonfatal(rev_counter < CRYPTO_OPE_ERROR); - update_revision_counters_in_state(); -} + log_info(LD_REND, "Encrypted revision counter %d to %ld", + (int) seconds_since_start_of_srv, (long int) rev_counter); -/** Set the revision counter in <b>hs_desc</b>, using the state file to find - * the current counter value if it exists. */ -static void -set_descriptor_revision_counter(hs_descriptor_t *hs_desc) -{ - /* Find stored rev counter if it exists */ - uint64_t rev_counter = - get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey); - - hs_desc->plaintext_data.revision_counter = rev_counter; + hs_desc->desc->plaintext_data.revision_counter = rev_counter; } /* Encode and sign the service descriptor desc and upload it to the @@ -2592,9 +2505,6 @@ upload_descriptor_to_all(const hs_service_t *service, safe_str_client(service->onion_address), fmt_next_time); } - /* Update the revision counter of this descriptor */ - increment_descriptor_revision_counter(desc->desc); - smartlist_free(responsible_dirs); return; } @@ -2734,6 +2644,10 @@ run_upload_descriptor_event(time_t now) * accurate because all circuits have been established. */ build_desc_intro_points(service, desc, now); + /* Set the desc revision counter right before uploading */ + set_descriptor_revision_counter(desc, approx_time(), + service->desc_current == desc); + upload_descriptor_to_all(service, desc); } FOR_EACH_DESCRIPTOR_END; } FOR_EACH_SERVICE_END; diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 22bfcacc26..5c5443a35f 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -131,6 +131,10 @@ typedef struct hs_service_descriptor_t { * from this list, this means we received new dirinfo and we need to * reupload our descriptor. */ smartlist_t *previous_hsdirs; + + /** The OPE cipher for encrypting revision counters for this descriptor. + * Tied to the descriptor blinded key. */ + struct crypto_ope_t *ope_cipher; } hs_service_descriptor_t; /* Service key material. */ @@ -311,8 +315,9 @@ STATIC void remove_service(hs_service_ht *map, hs_service_t *service); STATIC int register_service(hs_service_ht *map, hs_service_t *service); /* Service introduction point functions. */ STATIC hs_service_intro_point_t *service_intro_point_new( - const extend_info_t *ei, - unsigned int is_legacy); + const extend_info_t *ei, + unsigned int is_legacy, + unsigned int supports_ed25519_link_handshake_any); STATIC void service_intro_point_free_(hs_service_intro_point_t *ip); #define service_intro_point_free(ip) \ FREE_AND_NULL(hs_service_intro_point_t, \ @@ -346,19 +351,10 @@ STATIC void build_all_descriptors(time_t now); STATIC void update_all_descriptors(time_t now); STATIC void run_upload_descriptor_event(time_t now); -STATIC char * -encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc); - STATIC void service_descriptor_free_(hs_service_descriptor_t *desc); #define service_descriptor_free(d) \ FREE_AND_NULL(hs_service_descriptor_t, \ service_descriptor_free_, (d)) - -STATIC uint64_t -check_state_line_for_service_rev_counter(const char *state_line, - const ed25519_public_key_t *blinded_pubkey, - int *service_found_out); - STATIC int write_address_to_file(const hs_service_t *service, const char *fname_); @@ -375,4 +371,3 @@ STATIC int service_desc_hsdirs_changed(const hs_service_t *service, #endif /* defined(HS_SERVICE_PRIVATE) */ #endif /* !defined(TOR_HS_SERVICE_H) */ - diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c index 329f053d3b..ff98a719db 100644 --- a/src/feature/hs_common/shared_random_client.c +++ b/src/feature/hs_common/shared_random_client.c @@ -222,24 +222,44 @@ sr_parse_srv(const smartlist_t *args) return srv; } -/** Return the start time of the current SR protocol run. For example, if the - * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this - * function should return 23/06/2017 00:00:00. */ +/** Return the start time of the current SR protocol run using the times from + * the current consensus. For example, if the latest consensus valid-after is + * 23/06/2017 23:00:00 and a full SR protocol run is 24 hours, this function + * returns 23/06/2017 00:00:00. */ time_t -sr_state_get_start_time_of_current_protocol_run(time_t now) +sr_state_get_start_time_of_current_protocol_run(void) { int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES; int voting_interval = get_voting_interval(); /* Find the time the current round started. */ - time_t beginning_of_current_round = get_start_time_of_current_round(); + time_t beginning_of_curr_round = get_start_time_of_current_round(); /* Get current SR protocol round */ - int current_round = (now / voting_interval) % total_rounds; + int curr_round_slot; + curr_round_slot = (beginning_of_curr_round / voting_interval) % total_rounds; /* Get start time by subtracting the time elapsed from the beginning of the protocol run */ - time_t time_elapsed_since_start_of_run = current_round * voting_interval; - return beginning_of_current_round - time_elapsed_since_start_of_run; + time_t time_elapsed_since_start_of_run = curr_round_slot * voting_interval; + + log_debug(LD_GENERAL, "Current SRV proto run: Start of current round: %u. " + "Time elapsed: %u (%d)", (unsigned) beginning_of_curr_round, + (unsigned) time_elapsed_since_start_of_run, voting_interval); + + return beginning_of_curr_round - time_elapsed_since_start_of_run; +} + +/** Return the start time of the previous SR protocol run. See + * sr_state_get_start_time_of_current_protocol_run() for more details. */ +time_t +sr_state_get_start_time_of_previous_protocol_run(void) +{ + time_t start_time_of_current_run = + sr_state_get_start_time_of_current_protocol_run(); + + /* We get the start time of previous protocol run, by getting the start time + * of current run and the subtracting a full protocol run from that. */ + return start_time_of_current_run - sr_state_get_protocol_run_duration(); } /** Return the time (in seconds) it takes to complete a full SR protocol phase diff --git a/src/feature/hs_common/shared_random_client.h b/src/feature/hs_common/shared_random_client.h index 497a015c18..0e26f530a4 100644 --- a/src/feature/hs_common/shared_random_client.h +++ b/src/feature/hs_common/shared_random_client.h @@ -34,7 +34,8 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args); /* Number of phase we have in a protocol. */ #define SHARED_RANDOM_N_PHASES 2 -time_t sr_state_get_start_time_of_current_protocol_run(time_t now); +time_t sr_state_get_start_time_of_current_protocol_run(void); +time_t sr_state_get_start_time_of_previous_protocol_run(void); unsigned int sr_state_get_phase_duration(void); unsigned int sr_state_get_protocol_run_duration(void); time_t get_start_time_of_current_round(void); diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index e9d36cbdcb..6492b828b1 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -385,6 +385,11 @@ networkstatus_vote_free_(networkstatus_t *ns) smartlist_free(ns->routerstatus_list); } + if (ns->bw_file_headers) { + SMARTLIST_FOREACH(ns->bw_file_headers, char *, c, tor_free(c)); + smartlist_free(ns->bw_file_headers); + } + digestmap_free(ns->desc_digest_map, NULL); if (ns->sr_info.commits) { @@ -2417,6 +2422,8 @@ get_net_param_from_list(smartlist_t *net_params, const char *param_name, res = max_val; } + tor_assert(res >= min_val); + tor_assert(res <= max_val); return res; } diff --git a/src/feature/nodelist/networkstatus_st.h b/src/feature/nodelist/networkstatus_st.h index 46b0f53c0b..2bb0e3ae35 100644 --- a/src/feature/nodelist/networkstatus_st.h +++ b/src/feature/nodelist/networkstatus_st.h @@ -96,6 +96,9 @@ struct networkstatus_t { /** Contains the shared random protocol data from a vote or consensus. */ networkstatus_sr_info_t sr_info; + + /** List of key=value strings from the headers of the bandwidth list file */ + smartlist_t *bw_file_headers; }; #endif diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index edd5ef5d56..7685760ac6 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -2687,6 +2687,9 @@ log_addr_has_changed(int severity, char addrbuf_prev[TOR_ADDR_BUF_LEN]; char addrbuf_cur[TOR_ADDR_BUF_LEN]; + if (BUG(!server_mode(get_options()))) + return; + if (tor_addr_to_str(addrbuf_prev, prev, sizeof(addrbuf_prev), 1) == NULL) strlcpy(addrbuf_prev, "???", TOR_ADDR_BUF_LEN); if (tor_addr_to_str(addrbuf_cur, cur, sizeof(addrbuf_cur), 1) == NULL) diff --git a/src/lib/cc/torint.h b/src/lib/cc/torint.h index b97fc8d975..5097724726 100644 --- a/src/lib/cc/torint.h +++ b/src/lib/cc/torint.h @@ -100,6 +100,16 @@ typedef int32_t ssize_t; # define TOR_PRIuSZ "zu" #endif +#ifdef _WIN32 +# ifdef _WIN64 +# define TOR_PRIdSZ PRId64 +# else +# define TOR_PRIdSZ PRId32 +# endif +#else +# define TOR_PRIdSZ "zd" +#endif + #ifndef SSIZE_MAX #if (SIZEOF_SIZE_T == 4) #define SSIZE_MAX INT32_MAX diff --git a/src/lib/compress/compress_zstd.c b/src/lib/compress/compress_zstd.c index 0a71fed4b8..fe88d4a544 100644 --- a/src/lib/compress/compress_zstd.c +++ b/src/lib/compress/compress_zstd.c @@ -28,10 +28,14 @@ #endif #ifdef HAVE_ZSTD +#ifdef HAVE_CFLAG_WUNUSED_CONST_VARIABLE DISABLE_GCC_WARNING(unused-const-variable) +#endif #include <zstd.h> +#ifdef HAVE_CFLAG_WUNUSED_CONST_VARIABLE ENABLE_GCC_WARNING(unused-const-variable) #endif +#endif /** Total number of bytes allocated for Zstandard state. */ static atomic_counter_t total_zstd_allocation; diff --git a/src/lib/container/smartlist.c b/src/lib/container/smartlist.c index dc283e5f50..4b29d834d9 100644 --- a/src/lib/container/smartlist.c +++ b/src/lib/container/smartlist.c @@ -189,6 +189,33 @@ smartlist_ints_eq(const smartlist_t *sl1, const smartlist_t *sl2) return 1; } +/** + * Return true if there is shallow equality between smartlists - + * i.e. all indices correspond to exactly same object (pointer + * values are matching). Otherwise, return false. + */ +int +smartlist_ptrs_eq(const smartlist_t *s1, const smartlist_t *s2) +{ + if (s1 == s2) + return 1; + + // Note: pointers cannot both be NULL at this point, because + // above check. + if (s1 == NULL || s2 == NULL) + return 0; + + if (smartlist_len(s1) != smartlist_len(s2)) + return 0; + + for (int i = 0; i < smartlist_len(s1); i++) { + if (smartlist_get(s1, i) != smartlist_get(s2, i)) + return 0; + } + + return 1; +} + /** Return true iff <b>sl</b> has some element E such that * tor_memeq(E,<b>element</b>,DIGEST_LEN) */ diff --git a/src/lib/container/smartlist.h b/src/lib/container/smartlist.h index 3b19cbfce4..9705396ac9 100644 --- a/src/lib/container/smartlist.h +++ b/src/lib/container/smartlist.h @@ -37,6 +37,9 @@ int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2); void smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2); void smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2); +int smartlist_ptrs_eq(const smartlist_t *s1, + const smartlist_t *s2); + void smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b)); void *smartlist_get_most_frequent_(const smartlist_t *sl, diff --git a/src/lib/crypt_ops/crypto_hkdf.c b/src/lib/crypt_ops/crypto_hkdf.c index 0200d0fe9c..1873632a9d 100644 --- a/src/lib/crypt_ops/crypto_hkdf.c +++ b/src/lib/crypt_ops/crypto_hkdf.c @@ -19,9 +19,9 @@ #include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) -#define HAVE_OPENSSL_HKDF 1 +#if defined(HAVE_ERR_LOAD_KDF_STRINGS) #include <openssl/kdf.h> +#define HAVE_OPENSSL_HKDF 1 #endif #include <string.h> diff --git a/src/lib/crypt_ops/crypto_ope.c b/src/lib/crypt_ops/crypto_ope.c new file mode 100644 index 0000000000..ca42ae4341 --- /dev/null +++ b/src/lib/crypt_ops/crypto_ope.c @@ -0,0 +1,185 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * A rudimentary order-preserving encryption scheme. + * + * To compute the encryption of N, this scheme uses an AES-CTR stream to + * generate M-byte values, and adds the first N of them together. (+1 each to + * insure that the ciphertexts are strictly decreasing.) + * + * We use this for generating onion service revision counters based on the + * current time, without leaking the amount of skew in our view of the current + * time. MUCH more analysis would be needed before using it for anything + * else! + */ + +#include "orconfig.h" + +#define CRYPTO_OPE_PRIVATE +#include "lib/crypt_ops/crypto_ope.h" +#include "lib/crypt_ops/crypto.h" +#include "lib/crypt_ops/crypto_util.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/arch/bytes.h" + +#include <string.h> + +/** + * How infrequent should the precomputed values be for this encryption? + * The choice of this value creates a space/time tradeoff. + * + * Note that this value must be a multiple of 16; see + * ope_get_cipher() + */ +#define SAMPLE_INTERVAL 1024 +/** Number of precomputed samples to make for each OPE key. */ +#define N_SAMPLES (OPE_INPUT_MAX / SAMPLE_INTERVAL) + +struct crypto_ope_t { + /** An AES key for use with this object. */ + uint8_t key[OPE_KEY_LEN]; + /** Cached intermediate encryption values at SAMPLE_INTERVAL, + * SAMPLE_INTERVAL*2,...SAMPLE_INTERVAL*N_SAMPLES */ + uint64_t samples[N_SAMPLES]; +}; + +/** The type to add up in order to produce our OPE ciphertexts */ +typedef uint16_t ope_val_t; + +#ifdef WORDS_BIG_ENDIAN +/** Convert an OPE value to little-endian */ +static inline ope_val_t +ope_val_to_le(ope_val_t x) +{ + return + ((x) >> 8) | + (((x)&0xff) << 8); +} +#else +#define ope_val_to_le(x) (x) +#endif + +/** + * Return a new AES256-CTR stream cipher object for <b>ope</b>, ready to yield + * bytes from the stream at position <b>initial_idx</b>. + * + * Note that because the index is converted directly to an IV, it must be a + * multiple of the AES block size (16). + */ +STATIC crypto_cipher_t * +ope_get_cipher(const crypto_ope_t *ope, uint32_t initial_idx) +{ + uint8_t iv[CIPHER_IV_LEN]; + tor_assert((initial_idx & 0xf) == 0); + uint32_t n = tor_htonl(initial_idx >> 4); + memset(iv, 0, sizeof(iv)); + memcpy(iv + CIPHER_IV_LEN - sizeof(n), &n, sizeof(n)); + + return crypto_cipher_new_with_iv_and_bits(ope->key, + iv, + OPE_KEY_LEN * 8); +} + +/** + * Retrieve and add the next <b>n</b> values from the stream cipher <b>c</b>, + * and return their sum. + * + * Note that values are taken in little-endian order (for performance on + * prevalent hardware), and are mapped from range 0..2^n-1 to range 1..2^n (so + * that each input encrypts to a different output). + * + * NOTE: this function is not constant-time. + */ +STATIC uint64_t +sum_values_from_cipher(crypto_cipher_t *c, size_t n) +{ +#define BUFSZ 256 + ope_val_t buf[BUFSZ]; + uint64_t total = 0; + unsigned i; + while (n >= BUFSZ) { + memset(buf, 0, sizeof(buf)); + crypto_cipher_crypt_inplace(c, (char*)buf, BUFSZ*sizeof(ope_val_t)); + + for (i = 0; i < BUFSZ; ++i) { + total += ope_val_to_le(buf[i]); + total += 1; + } + n -= BUFSZ; + } + + memset(buf, 0, n*sizeof(ope_val_t)); + crypto_cipher_crypt_inplace(c, (char*)buf, n*sizeof(ope_val_t)); + for (i = 0; i < n; ++i) { + total += ope_val_to_le(buf[i]); + total += 1; + } + + memset(buf, 0, sizeof(buf)); + return total; +} + +/** + * Return a new crypto_ope_t object, using the provided 256-bit key. + */ +crypto_ope_t * +crypto_ope_new(const uint8_t *key) +{ + crypto_ope_t *ope = tor_malloc_zero(sizeof(crypto_ope_t)); + memcpy(ope->key, key, OPE_KEY_LEN); + + crypto_cipher_t *cipher = ope_get_cipher(ope, 0); + + uint64_t v = 0; + int i; + for (i = 0; i < N_SAMPLES; ++i) { + v += sum_values_from_cipher(cipher, SAMPLE_INTERVAL); + ope->samples[i] = v; + } + + crypto_cipher_free(cipher); + return ope; +} + +/** Free all storage held in <>ope</b>. */ +void +crypto_ope_free_(crypto_ope_t *ope) +{ + if (!ope) + return; + memwipe(ope, 0, sizeof(*ope)); + tor_free(ope); +} + +/** + * Return the encrypted value corresponding to <b>input</b>. The input value + * must be in range 1..OPE_INPUT_MAX. Returns CRYPTO_OPE_ERROR on an invalid + * input. + * + * NOTE: this function is not constant-time. + */ +uint64_t +crypto_ope_encrypt(const crypto_ope_t *ope, int plaintext) +{ + if (plaintext <= 0 || plaintext > OPE_INPUT_MAX) + return CRYPTO_OPE_ERROR; + + const int sample_idx = (plaintext / SAMPLE_INTERVAL); + const int starting_iv = sample_idx * SAMPLE_INTERVAL; + const int remaining_values = plaintext - starting_iv; + uint64_t v; + if (sample_idx == 0) { + v = 0; + } else { + v = ope->samples[sample_idx - 1]; + } + crypto_cipher_t *cipher = ope_get_cipher(ope, starting_iv*sizeof(ope_val_t)); + + v += sum_values_from_cipher(cipher, remaining_values); + + crypto_cipher_free(cipher); + + return v; +} diff --git a/src/lib/crypt_ops/crypto_ope.h b/src/lib/crypt_ops/crypto_ope.h new file mode 100644 index 0000000000..c62ed2a942 --- /dev/null +++ b/src/lib/crypt_ops/crypto_ope.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef CRYPTO_OPE_H +#define CRYPTO_OPE_H + +#include "orconfig.h" +#include "lib/cc/torint.h" +#include "lib/crypt_ops/crypto_ope.h" +#include "lib/testsupport/testsupport.h" + +/** Length of OPE key, in bytes. */ +#define OPE_KEY_LEN 32 + +/** Largest value that can be passed to crypto_ope_encrypt(). + * + * Expressed as 2^18 because the OPE system prefers powers of two. + * + * The current max value stands for about 70 hours. The rationale here is as + * follows: The rev counter is the time of seconds since the start of an SRV + * period. SRVs are useful for about 48 hours (that's how long they stick + * around on the consensus). Let's also add 12 hours of drift for clock skewed + * services that might be using an old consensus and we arrive to 60 + * hours. The max value should be beyond that. + */ +#define OPE_INPUT_MAX (1<<18) + +#define CRYPTO_OPE_ERROR UINT64_MAX + +typedef struct crypto_ope_t crypto_ope_t; + +crypto_ope_t *crypto_ope_new(const uint8_t *key); +void crypto_ope_free_(crypto_ope_t *ope); +#define crypto_ope_free(ope) \ + FREE_AND_NULL(crypto_ope_t, crypto_ope_free_, (ope)) + +uint64_t crypto_ope_encrypt(const crypto_ope_t *ope, int plaintext); + +#ifdef CRYPTO_OPE_PRIVATE +struct aes_cnt_cipher; +STATIC struct aes_cnt_cipher *ope_get_cipher(const crypto_ope_t *ope, + uint32_t initial_idx); +STATIC uint64_t sum_values_from_cipher(struct aes_cnt_cipher *c, size_t n); +#endif + +#endif diff --git a/src/lib/crypt_ops/include.am b/src/lib/crypt_ops/include.am index 803d448852..8647a91e8c 100644 --- a/src/lib/crypt_ops/include.am +++ b/src/lib/crypt_ops/include.am @@ -15,6 +15,8 @@ src_lib_libtor_crypt_ops_a_SOURCES = \ src/lib/crypt_ops/crypto_format.c \ src/lib/crypt_ops/crypto_hkdf.c \ src/lib/crypt_ops/crypto_init.c \ + src/lib/crypt_ops/crypto_ope.c \ + src/lib/crypt_ops/crypto_openssl_mgt.c \ src/lib/crypt_ops/crypto_pwbox.c \ src/lib/crypt_ops/crypto_rand.c \ src/lib/crypt_ops/crypto_rsa.c \ @@ -58,6 +60,7 @@ noinst_HEADERS += \ src/lib/crypt_ops/crypto_init.h \ src/lib/crypt_ops/crypto_nss_mgt.h \ src/lib/crypt_ops/crypto_openssl_mgt.h \ + src/lib/crypt_ops/crypto_ope.h \ src/lib/crypt_ops/crypto_pwbox.h \ src/lib/crypt_ops/crypto_rand.h \ src/lib/crypt_ops/crypto_rsa.h \ diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c index 42b3670a71..b23f4edc97 100644 --- a/src/lib/log/util_bug.c +++ b/src/lib/log/util_bug.c @@ -20,10 +20,6 @@ #include <string.h> -#ifdef __COVERITY__ -int bug_macro_deadcode_dummy__ = 0; -#endif - #ifdef TOR_UNIT_TESTS static void (*failed_assertion_cb)(void) = NULL; static int n_bugs_to_capture = 0; diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h index 61ee60f729..44a4f8381c 100644 --- a/src/lib/log/util_bug.h +++ b/src/lib/log/util_bug.h @@ -86,13 +86,10 @@ */ #ifdef __COVERITY__ -extern int bug_macro_deadcode_dummy__; #undef BUG // Coverity defines this in global headers; let's override it. This is a // magic coverity-only preprocessor thing. -// We use this "deadcode_dummy__" trick to prevent coverity from -// complaining about unreachable bug cases. -#nodef BUG(x) ((x)?(__coverity_panic__(),1):(0+bug_macro_deadcode_dummy__)) +#nodef BUG(x) (x) #endif /* defined(__COVERITY__) */ #if defined(__COVERITY__) || defined(__clang_analyzer__) diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index c3e44d2a79..4bbadbe535 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -14,3 +14,12 @@ members = [ debug = true panic = "abort" +[features] +default = [] +# If this feature is enabled, test code which calls Tor C code from Rust will +# execute with `cargo test`. Due to numerous linker issues (#25386), this is +# currently disabled by default. Crates listed here are those which, in their +# unittests, doctests, and/or integration tests, call C code. +test-c-from-rust = [ + "crypto/test-c-from-rust", +] diff --git a/src/rust/build.rs b/src/rust/build.rs index a35e6868c6..78773915a5 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -149,14 +149,16 @@ pub fn main() { // tor uses. We must be careful with factoring and dependencies // moving forward! cfg.component("tor-crypt-ops-testing"); - cfg.component("tor-sandbox"); + cfg.component("tor-sandbox-testing"); cfg.component("tor-encoding-testing"); - cfg.component("tor-net"); + cfg.component("tor-fs-testing"); + cfg.component("tor-time-testing"); + cfg.component("tor-net-testing"); cfg.component("tor-thread-testing"); cfg.component("tor-memarea-testing"); - cfg.component("tor-log"); - cfg.component("tor-lock"); - cfg.component("tor-fdio"); + cfg.component("tor-log-testing"); + cfg.component("tor-lock-testing"); + cfg.component("tor-fdio-testing"); cfg.component("tor-container-testing"); cfg.component("tor-smartlist-core-testing"); cfg.component("tor-string-testing"); diff --git a/src/rust/crypto/Cargo.toml b/src/rust/crypto/Cargo.toml index 869e0d6256..d68ac48e28 100644 --- a/src/rust/crypto/Cargo.toml +++ b/src/rust/crypto/Cargo.toml @@ -26,3 +26,7 @@ rand = { version = "=0.5.0-pre.2", default-features = false } rand_core = { version = "=0.2.0-pre.0", default-features = false } [features] +# If this feature is enabled, test code which calls Tor C code from Rust will +# execute with `cargo test`. Due to numerous linker issues (#25386), this is +# currently disabled by default. +test-c-from-rust = [] diff --git a/src/rust/crypto/digests/sha2.rs b/src/rust/crypto/digests/sha2.rs index 03e0843dc0..d0246eeb94 100644 --- a/src/rust/crypto/digests/sha2.rs +++ b/src/rust/crypto/digests/sha2.rs @@ -165,15 +165,19 @@ impl FixedOutput for Sha512 { #[cfg(test)] mod test { + #[cfg(feature = "test-c-from-rust")] use digest::Digest; + #[cfg(feature = "test-c-from-rust")] use super::*; + #[cfg(feature = "test-c-from-rust")] #[test] fn sha256_default() { let _: Sha256 = Sha256::default(); } + #[cfg(feature = "test-c-from-rust")] #[test] fn sha256_digest() { let mut h: Sha256 = Sha256::new(); @@ -193,11 +197,13 @@ mod test { assert_eq!(result, expected); } + #[cfg(feature = "test-c-from-rust")] #[test] fn sha512_default() { let _: Sha512 = Sha512::default(); } + #[cfg(feature = "test-c-from-rust")] #[test] fn sha512_digest() { let mut h: Sha512 = Sha512::new(); diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 3055893d43..91bd83addf 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -42,7 +42,6 @@ pub extern "C" fn protover_all_supported( c_relay_version: *const c_char, missing_out: *mut *mut c_char, ) -> c_int { - if c_relay_version.is_null() { return 1; } @@ -58,14 +57,13 @@ pub extern "C" fn protover_all_supported( let relay_proto_entry: UnvalidatedProtoEntry = match UnvalidatedProtoEntry::from_str_any_len(relay_version) { - Ok(n) => n, - Err(_) => return 1, - }; - let maybe_unsupported: Option<UnvalidatedProtoEntry> = relay_proto_entry.all_supported(); + Ok(n) => n, + Err(_) => return 1, + }; - if maybe_unsupported.is_some() { - let unsupported: UnvalidatedProtoEntry = maybe_unsupported.unwrap(); - let c_unsupported: CString = match CString::new(unsupported.to_string()) { + if let Some(unsupported) = relay_proto_entry.all_supported() { + let c_unsupported: CString = match CString::new(unsupported.to_string()) + { Ok(n) => n, Err(_) => return 1, }; @@ -100,22 +98,23 @@ pub extern "C" fn protocol_list_supports_protocol( Err(_) => return 1, }; let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { - Ok(n) => n, + Ok(n) => n, Err(_) => return 0, }; let protocol: UnknownProtocol = match translate_to_rust(c_protocol) { Ok(n) => n.into(), Err(_) => return 0, }; - match proto_entry.supports_protocol(&protocol, &version) { - false => return 0, - true => return 1, + if proto_entry.supports_protocol(&protocol, &version) { + 1 + } else { + 0 } } #[no_mangle] pub extern "C" fn protover_contains_long_protocol_names_( - c_protocol_list: *const c_char + c_protocol_list: *const c_char, ) -> c_int { if c_protocol_list.is_null() { return 1; @@ -127,13 +126,10 @@ pub extern "C" fn protover_contains_long_protocol_names_( let protocol_list = match c_str.to_str() { Ok(n) => n, - Err(_) => return 1 + Err(_) => return 1, }; - let protocol_entry : Result<UnvalidatedProtoEntry,_> = - protocol_list.parse(); - - match protocol_entry { + match protocol_list.parse::<UnvalidatedProtoEntry>() { Ok(_) => 0, Err(_) => 1, } @@ -166,7 +162,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( }; let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { - Ok(n) => n, + Ok(n) => n, Err(_) => return 1, }; @@ -196,10 +192,8 @@ pub extern "C" fn protover_compute_vote( threshold: c_int, allow_long_proto_names: bool, ) -> *mut c_char { - if list.is_null() { - let empty = String::new(); - return allocate_and_copy_string(&empty); + return allocate_and_copy_string(""); } // Dereference of raw pointer requires an unsafe block. The pointer is @@ -209,17 +203,21 @@ pub extern "C" fn protover_compute_vote( let mut proto_entries: Vec<UnvalidatedProtoEntry> = Vec::new(); for datum in data { - let entry: UnvalidatedProtoEntry = match allow_long_proto_names { - true => match UnvalidatedProtoEntry::from_str_any_len(datum.as_str()) { - Ok(n) => n, - Err(_) => continue}, - false => match datum.parse() { - Ok(n) => n, - Err(_) => continue}, + let entry: UnvalidatedProtoEntry = if allow_long_proto_names { + match UnvalidatedProtoEntry::from_str_any_len(datum.as_str()) { + Ok(n) => n, + Err(_) => continue, + } + } else { + match datum.parse() { + Ok(n) => n, + Err(_) => continue, + } }; proto_entries.push(entry); } - let vote: UnvalidatedProtoEntry = ProtoverVote::compute(&proto_entries, &hold); + let vote: UnvalidatedProtoEntry = + ProtoverVote::compute(&proto_entries, &hold); allocate_and_copy_string(&vote.to_string()) } @@ -244,7 +242,9 @@ pub extern "C" fn protover_is_supported_here( /// Provide an interface for C to translate arguments and return types for /// protover::compute_for_old_tor #[no_mangle] -pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char { +pub extern "C" fn protover_compute_for_old_tor( + version: *const c_char, +) -> *const c_char { let supported: &'static CStr; let empty: &'static CStr; diff --git a/src/rust/tor_allocate/tor_allocate.rs b/src/rust/tor_allocate/tor_allocate.rs index 47fa5fc593..d0c0d79943 100644 --- a/src/rust/tor_allocate/tor_allocate.rs +++ b/src/rust/tor_allocate/tor_allocate.rs @@ -9,9 +9,9 @@ use libc::c_void; // Define a no-op implementation for testing Rust modules without linking to C #[cfg(feature = "testing")] -pub fn allocate_and_copy_string(s: &String) -> *mut c_char { +pub fn allocate_and_copy_string(s: &str) -> *mut c_char { use std::ffi::CString; - CString::new(s.as_str()).unwrap().into_raw() + CString::new(s).unwrap().into_raw() } // Defined only for tests, used for testing purposes, so that we don't need @@ -39,7 +39,7 @@ extern "C" { /// A `*mut c_char` that should be freed by tor_free in C /// #[cfg(not(feature = "testing"))] -pub fn allocate_and_copy_string(src: &String) -> *mut c_char { +pub fn allocate_and_copy_string(src: &str) -> *mut c_char { let bytes: &[u8] = src.as_bytes(); let size = mem::size_of_val::<[u8]>(bytes); @@ -77,8 +77,7 @@ mod test { use tor_allocate::allocate_and_copy_string; - let empty = String::new(); - let allocated_empty = allocate_and_copy_string(&empty); + let allocated_empty = allocate_and_copy_string(""); let allocated_empty_rust = unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; @@ -95,8 +94,7 @@ mod test { use tor_allocate::allocate_and_copy_string; - let empty = String::from("foo bar biz"); - let allocated_empty = allocate_and_copy_string(&empty); + let allocated_empty = allocate_and_copy_string("foo bar biz"); let allocated_empty_rust = unsafe { CStr::from_ptr(allocated_empty).to_str().unwrap() }; diff --git a/src/test/fuzz/fuzz_socks.c b/src/test/fuzz/fuzz_socks.c new file mode 100644 index 0000000000..14c25304b1 --- /dev/null +++ b/src/test/fuzz/fuzz_socks.c @@ -0,0 +1,50 @@ +/* Copyright (c) 2016-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define BUFFERS_PRIVATE +#include "core/or/or.h" + +#include "lib/container/buffers.h" +#include "lib/err/backtrace.h" +#include "lib/log/log.h" +#include "core/proto/proto_socks.h" +#include "feature/client/addressmap.h" + +#include "test/fuzz/fuzzing.h" + +int +fuzz_init(void) +{ + addressmap_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + addressmap_free_all(); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ + buf_t *buffer = buf_new_with_data((char*)stdin_buf, data_size); + if (!buffer) { + tor_assert(data_size==0); + buffer = buf_new(); + } + + socks_request_t *request = socks_request_new(); + + int r = fetch_from_buf_socks(buffer, request, 0, 0); + log_info(LD_GENERAL, "Socks request status: %d", r); + + /* Reset. */ + buf_free(buffer); + socks_request_free(request); + + return 0; +} diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index 9251e8fd5f..27eeced8c5 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -17,7 +17,7 @@ FUZZING_LIBS = \ @TOR_ZSTD_LIBS@ oss-fuzz-prereqs: \ - $(TOR_INTERNAL_TESTING_LIBS) + $(TOR_INTERNAL_TESTING_LIBS) noinst_HEADERS += \ src/test/fuzz/fuzzing.h @@ -32,6 +32,7 @@ LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) # ===== AFL fuzzers +if UNITTESTS_ENABLED src_test_fuzz_fuzz_consensus_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_consensus.c @@ -39,7 +40,9 @@ src_test_fuzz_fuzz_consensus_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_consensus_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_consensus_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_consensus_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_descriptor_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_descriptor.c @@ -47,7 +50,9 @@ src_test_fuzz_fuzz_descriptor_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_descriptor_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_descriptor_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_descriptor_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_diff_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_diff.c @@ -55,7 +60,9 @@ src_test_fuzz_fuzz_diff_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_diff_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_diff_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_diff_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_diff_apply_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_diff_apply.c @@ -63,7 +70,9 @@ src_test_fuzz_fuzz_diff_apply_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_diff_apply_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_diff_apply_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_diff_apply_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_extrainfo_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_extrainfo.c @@ -71,7 +80,9 @@ src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_hsdescv2_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_hsdescv2.c @@ -79,7 +90,9 @@ src_test_fuzz_fuzz_hsdescv2_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_hsdescv3_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_hsdescv3.c @@ -87,7 +100,9 @@ src_test_fuzz_fuzz_hsdescv3_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_hsdescv3_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_hsdescv3_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_hsdescv3_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_http_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_http.c @@ -95,7 +110,9 @@ src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_http_connect_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_http_connect.c @@ -103,7 +120,9 @@ src_test_fuzz_fuzz_http_connect_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_http_connect_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_http_connect_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_http_connect_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_iptsv2_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_iptsv2.c @@ -111,7 +130,9 @@ src_test_fuzz_fuzz_iptsv2_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_fuzz_microdesc_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_microdesc.c @@ -119,7 +140,19 @@ src_test_fuzz_fuzz_microdesc_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_microdesc_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_microdesc_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_microdesc_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED +src_test_fuzz_fuzz_socks_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_socks.c +src_test_fuzz_fuzz_socks_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_socks_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_socks_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_socks_LDADD = $(FUZZING_LIBS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_fuzz_vrs_SOURCES = \ src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzz_vrs.c @@ -127,7 +160,9 @@ src_test_fuzz_fuzz_vrs_CPPFLAGS = $(FUZZING_CPPFLAGS) src_test_fuzz_fuzz_vrs_CFLAGS = $(FUZZING_CFLAGS) src_test_fuzz_fuzz_vrs_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_vrs_LDADD = $(FUZZING_LIBS) +endif +if UNITTESTS_ENABLED FUZZERS = \ src/test/fuzz/fuzz-consensus \ src/test/fuzz/fuzz-descriptor \ @@ -140,94 +175,129 @@ FUZZERS = \ src/test/fuzz/fuzz-http-connect \ src/test/fuzz/fuzz-iptsv2 \ src/test/fuzz/fuzz-microdesc \ + src/test/fuzz/fuzz-socks \ src/test/fuzz/fuzz-vrs +endif # ===== libfuzzer if LIBFUZZER_ENABLED +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_consensus_SOURCES = \ $(src_test_fuzz_fuzz_consensus_SOURCES) src_test_fuzz_lf_fuzz_consensus_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_consensus_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_consensus_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_consensus_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_descriptor_SOURCES = \ $(src_test_fuzz_fuzz_descriptor_SOURCES) src_test_fuzz_lf_fuzz_descriptor_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_descriptor_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_descriptor_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_descriptor_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_diff_SOURCES = \ $(src_test_fuzz_fuzz_diff_SOURCES) src_test_fuzz_lf_fuzz_diff_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_diff_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_diff_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_diff_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_diff_apply_SOURCES = \ $(src_test_fuzz_fuzz_diff_apply_SOURCES) src_test_fuzz_lf_fuzz_diff_apply_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_diff_apply_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_diff_apply_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_diff_apply_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_extrainfo_SOURCES = \ $(src_test_fuzz_fuzz_extrainfo_SOURCES) src_test_fuzz_lf_fuzz_extrainfo_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_extrainfo_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv2_SOURCES) src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_hsdescv3_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv3_SOURCES) src_test_fuzz_lf_fuzz_hsdescv3_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_hsdescv3_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_hsdescv3_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_hsdescv3_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_http_SOURCES = \ $(src_test_fuzz_fuzz_http_SOURCES) src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_http_connect_SOURCES = \ $(src_test_fuzz_fuzz_http_connect_SOURCES) src_test_fuzz_lf_fuzz_http_connect_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_http_connect_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_http_connect_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_http_connect_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ $(src_test_fuzz_fuzz_iptsv2_SOURCES) src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_iptsv2_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_iptsv2_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_iptsv2_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_microdesc_SOURCES = \ $(src_test_fuzz_fuzz_microdesc_SOURCES) src_test_fuzz_lf_fuzz_microdesc_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_microdesc_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_microdesc_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_microdesc_LDADD = $(LIBFUZZER_LIBS) +endif +if UNITTESTS_ENABLED +src_test_fuzz_lf_fuzz_socks_SOURCES = \ + $(src_test_fuzz_fuzz_socks_SOURCES) +src_test_fuzz_lf_fuzz_socks_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_socks_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_socks_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_socks_LDADD = $(LIBFUZZER_LIBS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_lf_fuzz_vrs_SOURCES = \ $(src_test_fuzz_fuzz_vrs_SOURCES) src_test_fuzz_lf_fuzz_vrs_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_vrs_CFLAGS = $(LIBFUZZER_CFLAGS) src_test_fuzz_lf_fuzz_vrs_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) +endif LIBFUZZER_FUZZERS = \ src/test/fuzz/lf-fuzz-consensus \ @@ -241,6 +311,7 @@ LIBFUZZER_FUZZERS = \ src/test/fuzz/lf-fuzz-http-connect \ src/test/fuzz/lf-fuzz-iptsv2 \ src/test/fuzz/lf-fuzz-microdesc \ + src/test/fuzz/lf-fuzz-socks \ src/test/fuzz/lf-fuzz-vrs else @@ -250,65 +321,96 @@ endif # ===== oss-fuzz if OSS_FUZZ_ENABLED +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ $(src_test_fuzz_fuzz_consensus_SOURCES) src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_consensus_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_descriptor_a_SOURCES = \ $(src_test_fuzz_fuzz_descriptor_SOURCES) src_test_fuzz_liboss_fuzz_descriptor_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_descriptor_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_diff_a_SOURCES = \ $(src_test_fuzz_fuzz_diff_SOURCES) src_test_fuzz_liboss_fuzz_diff_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_diff_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_diff_apply_a_SOURCES = \ $(src_test_fuzz_fuzz_diff_apply_SOURCES) src_test_fuzz_liboss_fuzz_diff_apply_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_diff_apply_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_extrainfo_a_SOURCES = \ $(src_test_fuzz_fuzz_extrainfo_SOURCES) src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv2_SOURCES) src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_hsdescv3_a_SOURCES = \ $(src_test_fuzz_fuzz_hsdescv3_SOURCES) src_test_fuzz_liboss_fuzz_hsdescv3_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_hsdescv3_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ $(src_test_fuzz_fuzz_http_SOURCES) src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_http_connect_a_SOURCES = \ $(src_test_fuzz_fuzz_http_connect_SOURCES) src_test_fuzz_liboss_fuzz_http_connect_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_http_connect_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ $(src_test_fuzz_fuzz_iptsv2_SOURCES) src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_iptsv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_microdesc_a_SOURCES = \ $(src_test_fuzz_fuzz_microdesc_SOURCES) src_test_fuzz_liboss_fuzz_microdesc_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_microdesc_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif +if UNITTESTS_ENABLED +src_test_fuzz_liboss_fuzz_socks_a_SOURCES = \ + $(src_test_fuzz_fuzz_socks_SOURCES) +src_test_fuzz_liboss_fuzz_socks_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_socks_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif + +if UNITTESTS_ENABLED src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \ $(src_test_fuzz_fuzz_vrs_SOURCES) src_test_fuzz_liboss_fuzz_vrs_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) +endif OSS_FUZZ_FUZZERS = \ src/test/fuzz/liboss-fuzz-consensus.a \ @@ -322,6 +424,7 @@ OSS_FUZZ_FUZZERS = \ src/test/fuzz/liboss-fuzz-http-connect.a \ src/test/fuzz/liboss-fuzz-iptsv2.a \ src/test/fuzz/liboss-fuzz-microdesc.a \ + src/test/fuzz/liboss-fuzz-socks.a \ src/test/fuzz/liboss-fuzz-vrs.a else diff --git a/src/test/include.am b/src/test/include.am index 15ae79a521..46990597fd 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -117,6 +117,7 @@ src_test_test_SOURCES += \ src/test/test_controller.c \ src/test/test_controller_events.c \ src/test/test_crypto.c \ + src/test/test_crypto_ope.c \ src/test/test_crypto_openssl.c \ src/test/test_data.c \ src/test/test_dir.c \ @@ -321,6 +322,7 @@ src_test_test_hs_ntor_cl_AM_CPPFLAGS = \ $(AM_CPPFLAGS) +if UNITTESTS_ENABLED noinst_PROGRAMS += src/test/test-bt-cl src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c src_test_test_bt_cl_LDADD = \ @@ -330,6 +332,7 @@ src_test_test_bt_cl_LDADD = \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) +endif EXTRA_DIST += \ src/test/bt_test.py \ diff --git a/src/test/ope_ref.py b/src/test/ope_ref.py new file mode 100644 index 0000000000..3677e57a61 --- /dev/null +++ b/src/test/ope_ref.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +# Copyright 2018, The Tor Project, Inc. See LICENSE for licensing info. + +# Reference implementation for our rudimentary OPE code, used to +# generate test vectors. See crypto_ope.c for more details. + +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.ciphers.algorithms import AES +from cryptography.hazmat.backends import default_backend + +from binascii import a2b_hex + +#randomly generated and values. +KEY = a2b_hex( + "19e05891d55232c08c2cad91d612fdb9cbd6691949a0742434a76c80bc6992fe") +PTS = [ 121132, 82283, 72661, 72941, 123122, 12154, 121574, 11391, 65845, + 86301, 61284, 70505, 30438, 60150, 114800, 109403, 21893, 123569, + 95617, 48561, 53334, 92746, 7110, 9612, 106958, 46889, 87790, 68878, + 47917, 121128, 108602, 28217, 69498, 63870, 57542, 122148, 46254, + 42850, 92661, 57720] + +IV = b'\x00' * 16 + +backend = default_backend() + +def words(): + cipher = Cipher(algorithms.AES(KEY), modes.CTR(IV), backend=backend) + e = cipher.encryptor() + while True: + v = e.update(b'\x00\x00') + yield v[0] + 256 * v[1] + 1 + +def encrypt(n): + return sum(w for w, _ in zip(words(), range(n))) + +def example(n): + return ' {{ {}, UINT64_C({}) }},'.format(n, encrypt(n)) + +for v in PTS: + print(example(v)) diff --git a/src/test/test.c b/src/test/test.c index 2addeec968..745aa987aa 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -865,6 +865,7 @@ struct testgroup_t testgroups[] = { { "control/", controller_tests }, { "control/event/", controller_event_tests }, { "crypto/", crypto_tests }, + { "crypto/ope/", crypto_ope_tests }, { "crypto/openssl/", crypto_openssl_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, diff --git a/src/test/test.h b/src/test/test.h index 602acca1cd..bfe50cbb8c 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -203,6 +203,7 @@ extern struct testcase_t container_tests[]; extern struct testcase_t controller_tests[]; extern struct testcase_t controller_event_tests[]; extern struct testcase_t crypto_tests[]; +extern struct testcase_t crypto_ope_tests[]; extern struct testcase_t crypto_openssl_tests[]; extern struct testcase_t dir_tests[]; extern struct testcase_t dir_handle_get_tests[]; diff --git a/src/test/test_bt.sh b/src/test/test_bt.sh index 312905a4e2..df8bcb8eda 100755 --- a/src/test/test_bt.sh +++ b/src/test/test_bt.sh @@ -3,6 +3,8 @@ exitcode=0 +ulimit -c 0 + export ASAN_OPTIONS="handle_segv=0:allow_user_segv_handler=1" "${builddir:-.}/src/test/test-bt-cl" backtraces || exit $? "${builddir:-.}/src/test/test-bt-cl" assert 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?" diff --git a/src/test/test_config.c b/src/test/test_config.c index 393378b4c8..f5c759402c 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -4637,6 +4637,20 @@ test_config_parse_port_config__ports__ports_given(void *data) tor_addr_parse(&addr, "127.0.0.46"); tt_assert(tor_addr_eq(&port_cfg->addr, &addr)) + // Test success with a port of auto in mixed case + config_free_lines(config_port_valid); config_port_valid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_valid = mock_config_line("DNSPort", "AuTo"); + ret = parse_port_config(slout, config_port_valid, "DNS", 0, + "127.0.0.46", 0, 0); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(smartlist_len(slout), OP_EQ, 1); + port_cfg = (port_cfg_t *)smartlist_get(slout, 0); + tt_int_op(port_cfg->port, OP_EQ, CFG_AUTO_PORT); + tor_addr_parse(&addr, "127.0.0.46"); + tt_assert(tor_addr_eq(&port_cfg->addr, &addr)) + // Test success with parsing both an address and an auto port config_free_lines(config_port_valid); config_port_valid = NULL; SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); diff --git a/src/test/test_crypto_ope.c b/src/test/test_crypto_ope.c new file mode 100644 index 0000000000..7dcad7b4b2 --- /dev/null +++ b/src/test/test_crypto_ope.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define CRYPTO_OPE_PRIVATE + +#include "lib/crypt_ops/crypto_ope.h" +#include "lib/crypt_ops/crypto.h" +#include "lib/encoding/binascii.h" +#include "test/test.h" +#include "tinytest.h" + +#include <stddef.h> +#include <string.h> + +static void +test_crypto_ope_consistency(void *arg) +{ + (void)arg; + + crypto_ope_t *ope = NULL; + crypto_cipher_t *aes = NULL; + const int TEST_VALS[] = { 5, 500, 1023, 1024, 1025, 2046, 2047, 2048, 2049, + 10000, OPE_INPUT_MAX }; + unsigned i; + const uint8_t key[32] = "A fixed key, chosen arbitrarily."; + + ope = crypto_ope_new(key); + tt_assert(ope); + + uint64_t last_val = 0; + for (i = 0; i < ARRAY_LENGTH(TEST_VALS); ++i) { + aes = ope_get_cipher(ope, 0); + int val = TEST_VALS[i]; + uint64_t v1 = crypto_ope_encrypt(ope, val); + uint64_t v2 = sum_values_from_cipher(aes, val); + tt_u64_op(v1, OP_EQ, v2); + tt_u64_op(v2, OP_GT, last_val); + last_val = v2; + crypto_cipher_free(aes); + } + + done: + crypto_cipher_free(aes); + crypto_ope_free(ope); +} + +static void +test_crypto_ope_oob(void *arg) +{ + (void)arg; + + crypto_ope_t *ope = NULL; + const uint8_t key[32] = "A fixed key, chosen arbitrarily."; + ope = crypto_ope_new(key); + + tt_u64_op(UINT64_MAX, OP_EQ, crypto_ope_encrypt(ope,INT_MIN)); + tt_u64_op(UINT64_MAX, OP_EQ, crypto_ope_encrypt(ope,-100)); + tt_u64_op(UINT64_MAX, OP_EQ, crypto_ope_encrypt(ope,0)); + tt_u64_op(UINT64_MAX, OP_NE, crypto_ope_encrypt(ope,1)); + tt_u64_op(UINT64_MAX, OP_NE, crypto_ope_encrypt(ope,7000)); + tt_u64_op(UINT64_MAX, OP_NE, crypto_ope_encrypt(ope,OPE_INPUT_MAX)); + tt_u64_op(UINT64_MAX, OP_EQ, crypto_ope_encrypt(ope,OPE_INPUT_MAX+1)); + tt_u64_op(UINT64_MAX, OP_EQ, crypto_ope_encrypt(ope,INT_MAX)); + done: + crypto_ope_free(ope); +} + +static const char OPE_TEST_KEY[] = + "19e05891d55232c08c2cad91d612fdb9cbd6691949a0742434a76c80bc6992fe"; + +/* generated by a separate python implementation. */ +static const struct { + int v; + uint64_t r; +} OPE_TEST_VECTORS[] = { + { 121132, UINT64_C(3971694514) }, + { 82283, UINT64_C(2695743564) }, + { 72661, UINT64_C(2381548866) }, + { 72941, UINT64_C(2390408421) }, + { 123122, UINT64_C(4036781069) }, + { 12154, UINT64_C(402067100) }, + { 121574, UINT64_C(3986197593) }, + { 11391, UINT64_C(376696838) }, + { 65845, UINT64_C(2161801517) }, + { 86301, UINT64_C(2828270975) }, + { 61284, UINT64_C(2013616892) }, + { 70505, UINT64_C(2313368870) }, + { 30438, UINT64_C(1001394664) }, + { 60150, UINT64_C(1977329668) }, + { 114800, UINT64_C(3764946628) }, + { 109403, UINT64_C(3585352477) }, + { 21893, UINT64_C(721388468) }, + { 123569, UINT64_C(4051780471) }, + { 95617, UINT64_C(3134921876) }, + { 48561, UINT64_C(1597596985) }, + { 53334, UINT64_C(1753691710) }, + { 92746, UINT64_C(3040874493) }, + { 7110, UINT64_C(234966492) }, + { 9612, UINT64_C(318326551) }, + { 106958, UINT64_C(3506124249) }, + { 46889, UINT64_C(1542219146) }, + { 87790, UINT64_C(2877361609) }, + { 68878, UINT64_C(2260369112) }, + { 47917, UINT64_C(1576681737) }, + { 121128, UINT64_C(3971553290) }, + { 108602, UINT64_C(3559176081) }, + { 28217, UINT64_C(929692460) }, + { 69498, UINT64_C(2280554161) }, + { 63870, UINT64_C(2098322675) }, + { 57542, UINT64_C(1891698992) }, + { 122148, UINT64_C(4004515805) }, + { 46254, UINT64_C(1521227949) }, + { 42850, UINT64_C(1408996941) }, + { 92661, UINT64_C(3037901517) }, + { 57720, UINT64_C(1897369989) }, +}; + +static void +test_crypto_ope_vectors(void *arg) +{ + (void)arg; + uint8_t key[32]; + crypto_ope_t *ope = NULL, *ope2 = NULL; + + base16_decode((char*)key, 32, OPE_TEST_KEY, strlen(OPE_TEST_KEY)); + + ope = crypto_ope_new(key); + key[8] += 1; + ope2 = crypto_ope_new(key); + unsigned i; + for (i = 0; i < ARRAY_LENGTH(OPE_TEST_VECTORS); ++i) { + int val = OPE_TEST_VECTORS[i].v; + uint64_t res = OPE_TEST_VECTORS[i].r; + + tt_u64_op(crypto_ope_encrypt(ope, val), OP_EQ, res); + tt_u64_op(crypto_ope_encrypt(ope2, val), OP_NE, res); + } + done: + crypto_ope_free(ope); + crypto_ope_free(ope2); +} + +struct testcase_t crypto_ope_tests[] = { + { "consistency", test_crypto_ope_consistency, 0, NULL, NULL }, + { "oob", test_crypto_ope_oob, 0, NULL, NULL }, + { "vectors", test_crypto_ope_vectors, 0, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index bda56b3a8e..c2f3f5297d 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -23,6 +23,7 @@ #include "app/config/confparse.h" #include "app/config/config.h" #include "feature/control/control.h" +#include "lib/encoding/confline.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_rand.h" @@ -1591,25 +1592,6 @@ test_dir_measured_bw_kb(void *arg) return; } -/* Test dirserv_read_measured_bandwidths */ -static void -test_dir_dirserv_read_measured_bandwidths_empty(void *arg) -{ - char *fname=NULL; - (void)arg; - - fname = tor_strdup(get_fname("V3BandwidthsFile")); - /* Test an empty file */ - write_str_to_file(fname, "", 0); - setup_capture_of_logs(LOG_WARN); - tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - expect_log_msg("Empty bandwidth file\n"); - - done: - tor_free(fname); - teardown_capture_of_logs(); -} - /* Unit tests for measured_bw_line_parse using line_is_after_headers flag. * When the end of the header is detected (a first complete bw line is parsed), * incomplete lines fail and give warnings, but do not give warnings if @@ -1653,7 +1635,7 @@ test_dir_measured_bw_kb_line_is_after_headers(void *arg) teardown_capture_of_logs(); } -/* Test dirserv_read_measured_bandwidths with whole files. */ +/* Test dirserv_read_measured_bandwidths with headers and complete files. */ static void test_dir_dirserv_read_measured_bandwidths(void *arg) { @@ -1661,76 +1643,321 @@ test_dir_dirserv_read_measured_bandwidths(void *arg) char *content = NULL; time_t timestamp = time(NULL); char *fname = tor_strdup(get_fname("V3BandwidthsFile")); - - /* Test Torflow file only with timestamp*/ - tor_asprintf(&content, "%ld", (long)timestamp); - write_str_to_file(fname, content, 0); - tor_free(content); - tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow file with timestamp followed by '\n' */ - tor_asprintf(&content, "%ld\n", (long)timestamp); - write_str_to_file(fname, content, 0); - tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow complete file*/ - const char *torflow_relay_lines= + smartlist_t *bw_file_headers = smartlist_new(); + /* bw file strings in vote */ + char *bw_file_headers_str = NULL; + char *bw_file_headers_str_v100 = NULL; + char *bw_file_headers_str_v110 = NULL; + char *bw_file_headers_str_bad = NULL; + char *bw_file_headers_str_extra = NULL; + char bw_file_headers_str_long[MAX_BW_FILE_HEADER_COUNT_IN_VOTE * 8 + 1] = ""; + /* string header lines in bw file */ + char *header_lines_v100 = NULL; + char *header_lines_v110_no_terminator = NULL; + char *header_lines_v110 = NULL; + char header_lines_long[MAX_BW_FILE_HEADER_COUNT_IN_VOTE * 8 + 1] = ""; + int i; + const char *header_lines_v110_no_terminator_no_timestamp = + "version=1.1.0\n" + "software=sbws\n" + "software_version=0.1.0\n" + "earliest_bandwidth=2018-05-08T16:13:26\n" + "file_created=2018-04-16T21:49:18\n" + "generator_started=2018-05-08T16:13:25\n" + "latest_bandwidth=2018-04-16T20:49:18\n"; + const char *bw_file_headers_str_v110_no_timestamp = + "version=1.1.0 software=sbws " + "software_version=0.1.0 " + "earliest_bandwidth=2018-05-08T16:13:26 " + "file_created=2018-04-16T21:49:18 " + "generator_started=2018-05-08T16:13:25 " + "latest_bandwidth=2018-04-16T20:49:18"; + const char *relay_lines_v100 = "node_id=$557365204145532d32353620696e73746561642e bw=1024 " "nick=Test measured_at=1523911725 updated_at=1523911725 " "pid_error=4.11374090719 pid_error_sum=4.11374090719 " "pid_bw=57136645 pid_delta=2.12168374577 circ_fail=0.2 " "scanner=/filepath\n"; - - tor_asprintf(&content, "%ld\n%s", (long)timestamp, torflow_relay_lines); + const char *relay_lines_v110 = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 " + "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ " + "bw=760 nick=Test rtt=380 time=2018-05-08T16:13:26\n"; + const char *relay_lines_bad = + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A\n"; + + tor_asprintf(&header_lines_v100, "%ld\n", (long)timestamp); + tor_asprintf(&header_lines_v110_no_terminator, "%ld\n%s", (long)timestamp, + header_lines_v110_no_terminator_no_timestamp); + tor_asprintf(&header_lines_v110, "%s%s", + header_lines_v110_no_terminator, BW_FILE_HEADERS_TERMINATOR); + + tor_asprintf(&bw_file_headers_str_v100, "timestamp=%ld",(long)timestamp); + tor_asprintf(&bw_file_headers_str_v110, "timestamp=%ld %s", + (long)timestamp, bw_file_headers_str_v110_no_timestamp); + tor_asprintf(&bw_file_headers_str_bad, "%s " + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A", + bw_file_headers_str_v110); + + for (i=0; i<MAX_BW_FILE_HEADER_COUNT_IN_VOTE; i++) { + strlcat(header_lines_long, "foo=bar\n", + sizeof(header_lines_long)); + } + /* 8 is the number of v110 lines in header_lines_v110 */ + for (i=0; i<MAX_BW_FILE_HEADER_COUNT_IN_VOTE - 8 - 1; i++) { + strlcat(bw_file_headers_str_long, "foo=bar ", + sizeof(bw_file_headers_str_long)); + } + strlcat(bw_file_headers_str_long, "foo=bar", + sizeof(bw_file_headers_str_long)); + tor_asprintf(&bw_file_headers_str_extra, + "%s %s", + bw_file_headers_str_v110, + bw_file_headers_str_long); + + /* Test an empty bandwidth file. bw_file_headers will be empty string */ + write_str_to_file(fname, "", 0); + setup_capture_of_logs(LOG_WARN); + tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + expect_log_msg("Empty bandwidth file\n"); + teardown_capture_of_logs(); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op("", OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test bandwidth file with only timestamp. + * bw_file_headers will be empty string */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%ld", (long)timestamp); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow complete file including v1.1.0 headers */ - const char *v110_header_lines= - "version=1.1.0\n" - "software=sbws\n" - "software_version=0.1.0\n" - "generator_started=2018-05-08T16:13:25\n" - "earliest_bandwidth=2018-05-08T16:13:26\n" - "====\n"; - - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, v110_header_lines, - torflow_relay_lines); + tt_int_op(-1, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op("", OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 bandwidth file headers */ + write_str_to_file(fname, header_lines_v100, 0); + bw_file_headers = smartlist_new(); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", header_lines_v100, relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test Torflow with additional headers afer a correct bw line */ - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, torflow_relay_lines, - v110_header_lines); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file with NULL bw_file_headers. */ + tor_asprintf(&content, "%s%s", header_lines_v100, relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, NULL)); - /* Test Torflow with additional headers afer a correct bw line and more - * bw lines after the headers. */ - tor_asprintf(&content, "%ld\n%s%s%s", (long)timestamp, torflow_relay_lines, - v110_header_lines, torflow_relay_lines); + /* Test bandwidth file including v1.1.0 bandwidth headers and + * v1.0.0 relay lines. bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", header_lines_v100, header_lines_v110, + relay_lines_v100); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); - - /* Test sbws file */ - const char *sbws_relay_lines= - "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 " - "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ " - "bw=760 nick=Test rtt=380 time=2018-05-08T16:13:26\n"; - - tor_asprintf(&content, "%ld\n%s%s", (long)timestamp, v110_header_lines, - sbws_relay_lines); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file with v1.1.0 headers at the end. + * bw_file_headers will contain only v1.0.0 headers and the additional + * headers will be interpreted as malformed relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", header_lines_v100, relay_lines_v100, + header_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.0.0 complete bandwidth file, the v1.1.0 headers and more relay + * lines. bw_file_headers will contain only v1.0.0 headers, the additional + * headers will be interpreted as malformed relay lines and the last relay + * lines will be correctly interpreted as relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s%s", header_lines_v100, relay_lines_v100, + header_lines_v110, relay_lines_v100); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v100, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator */ + bw_file_headers = smartlist_new(); + write_str_to_file(fname, header_lines_v110_no_terminator, 0); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator */ + bw_file_headers = smartlist_new(); + write_str_to_file(fname, header_lines_v110, 0); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth file without terminator, then relay lines. + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", + header_lines_v110_no_terminator, relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator, then relay lines + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s", + header_lines_v110, relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers with terminator, then bad relay lines, + * then terminator, then relay_lines_bad. + * bw_file_headers will contain the v1.1.0 headers. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s%s", header_lines_v110, relay_lines_bad, + BW_FILE_HEADERS_TERMINATOR, relay_lines_bad); write_str_to_file(fname, content, 0); tor_free(content); - tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL)); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_v110, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, then bad relay lines, + * then relay lines. bw_file_headers will contain the v1.1.0 headers and + * the bad relay lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, relay_lines_bad, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_bad, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, + * then many bad relay lines, then relay lines. + * bw_file_headers will contain the v1.1.0 headers and the bad relay lines + * to a maximum of MAX_BW_FILE_HEADER_COUNT_IN_VOTE header lines. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, header_lines_long, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ, + smartlist_len(bw_file_headers)); + bw_file_headers_str = smartlist_join_strings(bw_file_headers, " ", 0, NULL); + tt_str_op(bw_file_headers_str_extra, OP_EQ, bw_file_headers_str); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); + + /* Test v1.1.0 bandwidth headers without terminator, + * then many bad relay lines, then relay lines. + * bw_file_headers will contain the v1.1.0 headers and the bad relay lines. + * Force bw_file_headers to have more than MAX_BW_FILE_HEADER_COUNT_IN_VOTE + * This test is needed while there is not dirvote test. */ + bw_file_headers = smartlist_new(); + tor_asprintf(&content, "%s%s%s", + header_lines_v110_no_terminator, header_lines_long, + relay_lines_v110); + write_str_to_file(fname, content, 0); + tor_free(content); + tt_int_op(0, OP_EQ, dirserv_read_measured_bandwidths(fname, NULL, + bw_file_headers)); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_EQ, + smartlist_len(bw_file_headers)); + /* force bw_file_headers to be bigger than + * MAX_BW_FILE_HEADER_COUNT_IN_VOTE */ + char line[8] = "foo=bar\0"; + smartlist_add_strdup(bw_file_headers, line); + tt_int_op(MAX_BW_FILE_HEADER_COUNT_IN_VOTE, OP_LT, + smartlist_len(bw_file_headers)); + SMARTLIST_FOREACH(bw_file_headers, char *, c, tor_free(c)); + smartlist_free(bw_file_headers); + tor_free(bw_file_headers_str); done: tor_free(fname); + tor_free(header_lines_v100); + tor_free(header_lines_v110_no_terminator); + tor_free(header_lines_v110); + tor_free(bw_file_headers_str_v100); + tor_free(bw_file_headers_str_v110); + tor_free(bw_file_headers_str_bad); + tor_free(bw_file_headers_str_extra); } #define MBWC_INIT_TIME 1000 @@ -5979,6 +6206,57 @@ test_dir_networkstatus_consensus_has_ipv6(void *arg) UNMOCK(networkstatus_get_latest_consensus_by_flavor); } +static void +test_dir_format_versions_list(void *arg) +{ + (void)arg; + char *s = NULL; + config_line_t *lines = NULL; + + setup_capture_of_logs(LOG_WARN); + s = format_recommended_version_list(lines, 1); + tt_str_op(s, OP_EQ, ""); + + tor_free(s); + config_line_append(&lines, "ignored", "0.3.4.1, 0.2.9.111-alpha, 4.4.4-rc"); + s = format_recommended_version_list(lines, 1); + tt_str_op(s, OP_EQ, "0.2.9.111-alpha,0.3.4.1,4.4.4-rc"); + + tor_free(s); + config_line_append(&lines, "ignored", "0.1.2.3,0.2.9.10 "); + s = format_recommended_version_list(lines, 1); + tt_str_op(s, OP_EQ, "0.1.2.3,0.2.9.10,0.2.9.111-alpha,0.3.4.1,4.4.4-rc"); + + /* There should be no warnings so far. */ + expect_no_log_entry(); + + /* Now try a line with a space in it. */ + tor_free(s); + config_line_append(&lines, "ignored", "1.3.3.8 1.3.3.7"); + s = format_recommended_version_list(lines, 1); + tt_str_op(s, OP_EQ, "0.1.2.3,0.2.9.10,0.2.9.111-alpha,0.3.4.1," + "1.3.3.7,1.3.3.8,4.4.4-rc"); + + expect_single_log_msg_containing( + "Unexpected space in versions list member \"1.3.3.8 1.3.3.7\"." ); + + /* Start over, with a line containing a bogus version */ + config_free_lines(lines); + lines = NULL; + tor_free(s); + mock_clean_saved_logs(); + config_line_append(&lines, "ignored", "0.1.2.3, alpha-complex, 0.1.1.8-rc"); + s = format_recommended_version_list(lines,1); + tt_str_op(s, OP_EQ, "0.1.1.8-rc,0.1.2.3,alpha-complex"); + expect_single_log_msg_containing( + "Recommended version \"alpha-complex\" does not look valid."); + + done: + tor_free(s); + config_free_lines(lines); + teardown_capture_of_logs(); +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -6001,7 +6279,6 @@ struct testcase_t dir_tests[] = { DIR_LEGACY(versions), DIR_LEGACY(fp_pairs), DIR(split_fps, 0), - DIR_LEGACY(dirserv_read_measured_bandwidths_empty), DIR_LEGACY(measured_bw_kb), DIR_LEGACY(measured_bw_kb_line_is_after_headers), DIR_LEGACY(measured_bw_kb_cache), @@ -6049,5 +6326,6 @@ struct testcase_t dir_tests[] = { DIR(networkstatus_compute_bw_weights_v10, 0), DIR(platform_str, 0), DIR(networkstatus_consensus_has_ipv6, TT_FORK), + DIR(format_versions_list, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c index b47929e8eb..5b48dd3785 100644 --- a/src/test/test_hs_cell.c +++ b/src/test/test_hs_cell.c @@ -39,7 +39,7 @@ test_gen_establish_intro_cell(void *arg) attempt to parse it. */ { /* We only need the auth key pair here. */ - hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0); + hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0); /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ ret = hs_cell_build_establish_intro(circ_nonce, ip, buf); @@ -107,7 +107,7 @@ test_gen_establish_intro_cell_bad(void *arg) ed25519_sign_prefixed() function and make it fail. */ cell = trn_cell_establish_intro_new(); tt_assert(cell); - ip = service_intro_point_new(NULL, 0); + ip = service_intro_point_new(NULL, 0, 0); cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL); service_intro_point_free(ip); expect_log_msg_containing("Unable to make signature for " diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index f17cc8aeb3..c1001ee5c4 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -1344,6 +1344,10 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_service_ns->fresh_until); voting_schedule_recalculate_timing(get_options(), mock_service_ns->valid_after); + /* Check that service is in the right time period point */ + tt_int_op(hs_in_period_between_tp_and_srv(mock_service_ns, 0), OP_EQ, + cfg->service_in_new_tp); + /* Set client consensus time. */ set_consensus_times(cfg->client_valid_after, &mock_client_ns->valid_after); @@ -1353,10 +1357,7 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) &mock_client_ns->fresh_until); voting_schedule_recalculate_timing(get_options(), mock_client_ns->valid_after); - - /* New time period checks for this scenario. */ - tt_int_op(hs_in_period_between_tp_and_srv(mock_service_ns, 0), OP_EQ, - cfg->service_in_new_tp); + /* Check that client is in the right time period point */ tt_int_op(hs_in_period_between_tp_and_srv(mock_client_ns, 0), OP_EQ, cfg->client_in_new_tp); @@ -1367,7 +1368,8 @@ run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario) mock_service_ns->sr_info.previous_srv = cfg->service_previous_srv; /* Initialize a service to get keys. */ - service = helper_init_service(time(NULL)); + update_approx_time(mock_service_ns->valid_after); + service = helper_init_service(mock_service_ns->valid_after+1); /* * === Client setup === diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index 7da376471b..628d99bfde 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -50,7 +50,7 @@ new_establish_intro_cell(const char *circ_nonce, /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ - ip = service_intro_point_new(NULL, 0); + ip = service_intro_point_new(NULL, 0, 0); tt_assert(ip); cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf); tt_i64_op(cell_len, OP_GT, 0); @@ -76,7 +76,7 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out) /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ - ip = service_intro_point_new(NULL, 0); + ip = service_intro_point_new(NULL, 0, 0); tt_assert(ip); cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out); tt_i64_op(cell_len, OP_GT, 0); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 8074d260a4..ad0b3ab342 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -250,7 +250,7 @@ static hs_service_intro_point_t * helper_create_service_ip(void) { hs_desc_link_specifier_t *ls; - hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0); + hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0, 0); tor_assert(ip); /* Add a first unused link specifier. */ ls = tor_malloc_zero(sizeof(*ls)); @@ -1044,7 +1044,7 @@ static void test_rotate_descriptors(void *arg) { int ret; - time_t next_rotation_time, now = time(NULL); + time_t next_rotation_time, now; hs_service_t *service; hs_service_descriptor_t *desc_next; @@ -1068,6 +1068,9 @@ test_rotate_descriptors(void *arg) tt_int_op(ret, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + update_approx_time(mock_ns.valid_after+1); + now = mock_ns.valid_after+1; + /* Create a service with a default descriptor and state. It's added to the * global map. */ service = helper_create_service(); @@ -1106,6 +1109,9 @@ test_rotate_descriptors(void *arg) tt_int_op(ret, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + update_approx_time(mock_ns.valid_after+1); + now = mock_ns.valid_after+1; + /* Note down what to expect for the next rotation time which is 01:00 + 23h * meaning 00:00:00. */ next_rotation_time = mock_ns.valid_after + (23 * 60 * 60); @@ -1168,6 +1174,9 @@ test_build_update_descriptors(void *arg) tt_int_op(ret, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + update_approx_time(mock_ns.valid_after+1); + now = mock_ns.valid_after+1; + /* Create a service without a current descriptor to trigger a build. */ service = helper_create_service(); tt_assert(service); @@ -1309,6 +1318,9 @@ test_build_update_descriptors(void *arg) &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); + update_approx_time(mock_ns.valid_after+1); + now = mock_ns.valid_after+1; + /* Create a service without a current descriptor to trigger a build. */ service = helper_create_service(); tt_assert(service); @@ -1363,7 +1375,7 @@ static void test_upload_descriptors(void *arg) { int ret; - time_t now = time(NULL); + time_t now; hs_service_t *service; (void) arg; @@ -1382,6 +1394,10 @@ test_upload_descriptors(void *arg) ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until); tt_int_op(ret, OP_EQ, 0); + voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after); + + update_approx_time(mock_ns.valid_after+1); + now = mock_ns.valid_after+1; /* Create a service with no descriptor. It's added to the global map. */ service = hs_service_new(get_options()); @@ -1416,66 +1432,6 @@ test_upload_descriptors(void *arg) UNMOCK(get_or_state); } -/** Test the functions that save and load HS revision counters to state. */ -static void -test_revision_counter_state(void *arg) -{ - char *state_line_one = NULL; - char *state_line_two = NULL; - - hs_service_descriptor_t *desc_one = service_descriptor_new(); - hs_service_descriptor_t *desc_two = service_descriptor_new(); - - (void) arg; - - /* Prepare both descriptors */ - desc_one->desc->plaintext_data.revision_counter = 42; - desc_two->desc->plaintext_data.revision_counter = 240; - memset(&desc_one->blinded_kp.pubkey.pubkey, 66, - sizeof(desc_one->blinded_kp.pubkey.pubkey)); - memset(&desc_two->blinded_kp.pubkey.pubkey, 240, - sizeof(desc_one->blinded_kp.pubkey.pubkey)); - - /* Turn the descriptor rev counters into state lines */ - state_line_one = encode_desc_rev_counter_for_state(desc_one); - tt_str_op(state_line_one, OP_EQ, - "QkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkI 42"); - - state_line_two = encode_desc_rev_counter_for_state(desc_two); - tt_str_op(state_line_two, OP_EQ, - "8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PA 240"); - - /* Now let's test our state parsing function: */ - int service_found; - uint64_t cached_rev_counter; - - /* First's try with wrong pubkey and check that no service was found */ - cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one, - &desc_two->blinded_kp.pubkey, - &service_found); - tt_int_op(service_found, OP_EQ, 0); - tt_u64_op(cached_rev_counter, OP_EQ, 0); - - /* Now let's try with the right pubkeys */ - cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one, - &desc_one->blinded_kp.pubkey, - &service_found); - tt_int_op(service_found, OP_EQ, 1); - tt_u64_op(cached_rev_counter, OP_EQ, 42); - - cached_rev_counter =check_state_line_for_service_rev_counter(state_line_two, - &desc_two->blinded_kp.pubkey, - &service_found); - tt_int_op(service_found, OP_EQ, 1); - tt_u64_op(cached_rev_counter, OP_EQ, 240); - - done: - tor_free(state_line_one); - tor_free(state_line_two); - service_descriptor_free(desc_one); - service_descriptor_free(desc_two); -} - /** Global vars used by test_rendezvous1_parsing() */ static char rend1_payload[RELAY_PAYLOAD_SIZE]; static size_t rend1_payload_len = 0; @@ -1629,8 +1585,6 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "upload_descriptors", test_upload_descriptors, TT_FORK, NULL, NULL }, - { "revision_counter_state", test_revision_counter_state, TT_FORK, - NULL, NULL }, { "rendezvous1_parsing", test_rendezvous1_parsing, TT_FORK, NULL, NULL }, diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh index 5511dbf18c..cf6608634d 100755 --- a/src/test/test_key_expiration.sh +++ b/src/test/test_key_expiration.sh @@ -13,6 +13,14 @@ if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then fi fi +UNAME_OS=`uname -s | cut -d_ -f1` +if test "$UNAME_OS" = 'CYGWIN' || \ + test "$UNAME_OS" = 'MSYS' || \ + test "$UNAME_OS" = 'MINGW'; then + echo "This test is unreliable on Windows. See trac #26076. Skipping." >&2 + exit 77 +fi + if [ $# -ge 1 ]; then TOR_BINARY="${1}" shift diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index b3d4d8e39a..455f9e7d42 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -13,6 +13,14 @@ if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then fi fi +UNAME_OS=`uname -s | cut -d_ -f1` +if test "$UNAME_OS" = 'CYGWIN' || \ + test "$UNAME_OS" = 'MSYS' || \ + test "$UNAME_OS" = 'MINGW'; then + echo "This test is unreliable on Windows. See trac #26076. Skipping." >&2 + exit 77 +fi + if [ $# -ge 1 ]; then TOR_BINARY="${1}" shift diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index b0a9da0033..d2defdf680 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -259,8 +259,7 @@ test_get_start_time_of_current_run(void *arg) ¤t_time); tt_int_op(retval, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), current_time); - run_start_time = - sr_state_get_start_time_of_current_protocol_run(current_time); + run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ format_iso_time(tbuf, run_start_time); @@ -272,8 +271,7 @@ test_get_start_time_of_current_run(void *arg) ¤t_time); tt_int_op(retval, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), current_time); - run_start_time = - sr_state_get_start_time_of_current_protocol_run(current_time); + run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ format_iso_time(tbuf, run_start_time); @@ -285,8 +283,7 @@ test_get_start_time_of_current_run(void *arg) ¤t_time); tt_int_op(retval, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), current_time); - run_start_time = - sr_state_get_start_time_of_current_protocol_run(current_time); + run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ format_iso_time(tbuf, run_start_time); @@ -308,8 +305,7 @@ test_get_start_time_of_current_run(void *arg) ¤t_time); tt_int_op(retval, OP_EQ, 0); voting_schedule_recalculate_timing(get_options(), current_time); - run_start_time = - sr_state_get_start_time_of_current_protocol_run(current_time); + run_start_time = sr_state_get_start_time_of_current_protocol_run(); /* Compare it with the correct result */ format_iso_time(tbuf, run_start_time); @@ -342,7 +338,7 @@ test_get_start_time_functions(void *arg) voting_schedule_recalculate_timing(get_options(), now); time_t start_time_of_protocol_run = - sr_state_get_start_time_of_current_protocol_run(now); + sr_state_get_start_time_of_current_protocol_run(); tt_assert(start_time_of_protocol_run); /* Check that the round start time of the beginning of the run, is itself */ diff --git a/src/test/test_socks.c b/src/test/test_socks.c index e064cc8db1..7f6d8a48f1 100644 --- a/src/test/test_socks.c +++ b/src/test/test_socks.c @@ -84,7 +84,7 @@ test_socks_4_supported_commands(void *ptr) tt_int_op(0,OP_EQ, buf_datalen(buf)); - /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */ + /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.3:4370 */ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00"); tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, get_options()->SafeSocks), @@ -100,7 +100,7 @@ test_socks_4_supported_commands(void *ptr) tt_int_op(0,OP_EQ, buf_datalen(buf)); socks_request_clear(socks); - /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/ + /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.4:4369 with userid*/ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00"); tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, 1); @@ -166,7 +166,7 @@ test_socks_4_bad_arguments(void *ptr) tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, -1); buf_clear(buf); - expect_log_msg_containing("user name too long; rejecting."); + expect_log_msg_containing("socks4: parsing failed - invalid request."); mock_clean_saved_logs(); /* Try with 2000-byte hostname */ @@ -194,7 +194,7 @@ test_socks_4_bad_arguments(void *ptr) tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, -1); buf_clear(buf); - expect_log_msg_containing("Destaddr too long."); + expect_log_msg_containing("parsing failed - invalid request."); mock_clean_saved_logs(); /* Socks4, bogus hostname */ @@ -648,7 +648,8 @@ test_socks_5_malformed_commands(void *ptr) tt_int_op(5,OP_EQ,socks->socks_version); tt_int_op(10,OP_EQ,socks->replylen); tt_int_op(5,OP_EQ,socks->reply[0]); - tt_int_op(SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED,OP_EQ,socks->reply[1]); + /* trunnel parsing will fail with -1 */ + tt_int_op(SOCKS5_GENERAL_ERROR,OP_EQ,socks->reply[1]); tt_int_op(1,OP_EQ,socks->reply[3]); done: diff --git a/src/tools/Makefile.nmake b/src/tools/Makefile.nmake index fda1990e0b..e223d9b135 100644 --- a/src/tools/Makefile.nmake +++ b/src/tools/Makefile.nmake @@ -1,4 +1,4 @@ -all: tor-resolve.exe tor-gencert.exe +all: tor-resolve.exe tor-gencert.exe tor-print-ed-signing-cert.exe CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common /I ..\or @@ -15,5 +15,8 @@ tor-gencert.exe: tor-gencert.obj tor-resolve.exe: tor-resolve.obj $(CC) $(CFLAGS) $(LIBS) ..\common\*.lib tor-resolve.obj +tor-print-ed-signing-cert.exe: tor-print-ed-signing-cert.obj + $(CC) $(CFLAGS) $(LIBS) ..\common\*.lib tor-print-ed-signing-cert.obj + clean: del *.obj *.lib *.exe diff --git a/src/tools/include.am b/src/tools/include.am index 7c5d3f0bc8..cdd5616fb1 100644 --- a/src/tools/include.am +++ b/src/tools/include.am @@ -1,4 +1,4 @@ -bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert +bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert src/tools/tor-print-ed-signing-cert if COVERAGE_ENABLED noinst_PROGRAMS+= src/tools/tor-cov-resolve src/tools/tor-cov-gencert @@ -29,6 +29,15 @@ src_tools_tor_gencert_LDADD = \ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ +src_tools_tor_print_ed_signing_cert_SOURCES = src/tools/tor-print-ed-signing-cert.c +src_tools_tor_print_ed_signing_cert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ +src_tools_tor_print_ed_signing_cert_LDADD = \ + src/trunnel/libor-trunnel.a \ + $(TOR_CRYPTO_LIBS) \ + $(TOR_UTIL_LIBS) \ + @TOR_LIB_MATH@ $(TOR_LIBS_CRYPTLIB) \ + @TOR_LIB_WS32@ @TOR_LIB_USERENV@ + if COVERAGE_ENABLED src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) diff --git a/src/tools/tor-print-ed-signing-cert.c b/src/tools/tor-print-ed-signing-cert.c new file mode 100644 index 0000000000..0f64059d84 --- /dev/null +++ b/src/tools/tor-print-ed-signing-cert.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "ed25519_cert.h" +#include "lib/cc/torint.h" /* TOR_PRIdSZ */ +#include "lib/crypt_ops/crypto_format.h" +#include "lib/malloc/malloc.h" + +int +main(int argc, char **argv) +{ + ed25519_cert_t *cert = NULL; + + if (argc != 2) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s <path to ed25519_signing_cert file>\n", argv[0]); + return -1; + } + + const char *filepath = argv[1]; + char *got_tag = NULL; + + uint8_t certbuf[256]; + ssize_t cert_body_len = crypto_read_tagged_contents_from_file( + filepath, "ed25519v1-cert", + &got_tag, certbuf, sizeof(certbuf)); + + if (cert_body_len <= 0) { + fprintf(stderr, "crypto_read_tagged_contents_from_file failed with " + "error: %s\n", strerror(errno)); + return -2; + } + + if (!got_tag) { + fprintf(stderr, "Found no tag\n"); + return -3; + } + + if (strcmp(got_tag, "type4") != 0) { + fprintf(stderr, "Wrong tag: %s\n", got_tag); + return -4; + } + + tor_free(got_tag); + + ssize_t parsed = ed25519_cert_parse(&cert, certbuf, cert_body_len); + if (parsed <= 0) { + fprintf(stderr, "ed25519_cert_parse failed with return value %" TOR_PRIdSZ + "\n", parsed); + return -5; + } + + time_t expires_at = (time_t)cert->exp_field * 60 * 60; + + printf("Expires at: %s", ctime(&expires_at)); + + ed25519_cert_free(cert); + + return 0; +} diff --git a/src/trunnel/include.am b/src/trunnel/include.am index 5a0a79c3a0..03c1753e96 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -10,7 +10,8 @@ TRUNNELINPUTS = \ src/trunnel/ed25519_cert.trunnel \ src/trunnel/link_handshake.trunnel \ src/trunnel/pwbox.trunnel \ - src/trunnel/channelpadding_negotiation.trunnel + src/trunnel/channelpadding_negotiation.trunnel \ + src/trunner/socks5.trunnel TRUNNELSOURCES = \ src/ext/trunnel/trunnel.c \ @@ -21,7 +22,8 @@ TRUNNELSOURCES = \ src/trunnel/hs/cell_establish_intro.c \ src/trunnel/hs/cell_introduce1.c \ src/trunnel/hs/cell_rendezvous.c \ - src/trunnel/channelpadding_negotiation.c + src/trunnel/channelpadding_negotiation.c \ + src/trunnel/socks5.c TRUNNELHEADERS = \ src/ext/trunnel/trunnel.h \ @@ -34,7 +36,8 @@ TRUNNELHEADERS = \ src/trunnel/hs/cell_establish_intro.h \ src/trunnel/hs/cell_introduce1.h \ src/trunnel/hs/cell_rendezvous.h \ - src/trunnel/channelpadding_negotiation.h + src/trunnel/channelpadding_negotiation.h \ + src/trunnel/socks5.h src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) src_trunnel_libor_trunnel_a_CPPFLAGS = \ diff --git a/src/trunnel/socks5.c b/src/trunnel/socks5.c new file mode 100644 index 0000000000..9e5f6fcfed --- /dev/null +++ b/src/trunnel/socks5.c @@ -0,0 +1,3978 @@ +/* socks5.c -- generated by Trunnel v1.5.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include <stdlib.h> +#include "trunnel-impl.h" + +#include "socks5.h" + +#define TRUNNEL_SET_ERROR_CODE(obj) \ + do { \ + (obj)->trunnel_error_code_ = 1; \ + } while (0) + +#if defined(__COVERITY__) || defined(__clang_analyzer__) +/* If we're running a static analysis tool, we don't want it to complain + * that some of our remaining-bytes checks are dead-code. */ +int socks_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || socks_deadcode_dummy__ +#else +#define OR_DEADCODE_DUMMY +#endif + +#define CHECK_REMAINING(nbytes, label) \ + do { \ + if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \ + goto label; \ + } \ + } while (0) + +domainname_t * +domainname_new(void) +{ + domainname_t *val = trunnel_calloc(1, sizeof(domainname_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +domainname_clear(domainname_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->name); + TRUNNEL_DYNARRAY_CLEAR(&obj->name); +} + +void +domainname_free(domainname_t *obj) +{ + if (obj == NULL) + return; + domainname_clear(obj); + trunnel_memwipe(obj, sizeof(domainname_t)); + trunnel_free_(obj); +} + +uint8_t +domainname_get_len(const domainname_t *inp) +{ + return inp->len; +} +int +domainname_set_len(domainname_t *inp, uint8_t val) +{ + inp->len = val; + return 0; +} +size_t +domainname_getlen_name(const domainname_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->name); +} + +char +domainname_get_name(domainname_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->name, idx); +} + +char +domainname_getconst_name(const domainname_t *inp, size_t idx) +{ + return domainname_get_name((domainname_t*)inp, idx); +} +int +domainname_set_name(domainname_t *inp, size_t idx, char elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->name, idx, elt); + return 0; +} +int +domainname_add_name(domainname_t *inp, char elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->name.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(char, &inp->name, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +char * +domainname_getarray_name(domainname_t *inp) +{ + return inp->name.elts_; +} +const char * +domainname_getconstarray_name(const domainname_t *inp) +{ + return (const char *)domainname_getarray_name((domainname_t*)inp); +} +int +domainname_setlen_name(domainname_t *inp, size_t newlen) +{ +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + return trunnel_string_setlen(&inp->name, newlen, + &inp->trunnel_error_code_); + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +domainname_getstr_name(domainname_t *inp) +{ + return trunnel_string_getstr(&inp->name); +} +int +domainname_setstr0_name(domainname_t *inp, const char *val, size_t len) +{ +#if UINT8_MAX < SIZE_MAX + if (len > UINT8_MAX) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } +#endif + return trunnel_string_setstr0(&inp->name, val, len, &inp->trunnel_error_code_); +} +int +domainname_setstr_name(domainname_t *inp, const char *val) +{ + return domainname_setstr0_name(inp, val, strlen(val)); +} +const char * +domainname_check(const domainname_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (TRUNNEL_DYNARRAY_LEN(&obj->name) != obj->len) + return "Length mismatch for name"; + return NULL; +} + +ssize_t +domainname_encoded_len(const domainname_t *obj) +{ + ssize_t result = 0; + + if (NULL != domainname_check(obj)) + return -1; + + + /* Length of u8 len */ + result += 1; + + /* Length of char name[len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->name); + return result; +} +int +domainname_clear_errors(domainname_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +domainname_encode(uint8_t *output, const size_t avail, const domainname_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = domainname_encoded_len(obj); +#endif + + if (NULL != (msg = domainname_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 len */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->len)); + written += 1; ptr += 1; + + /* Encode char name[len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->name); + trunnel_assert(obj->len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->name.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As domainname_parse(), but do not allocate the output object. + */ +static ssize_t +domainname_parse_into(domainname_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 len */ + CHECK_REMAINING(1, truncated); + obj->len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse char name[len] */ + CHECK_REMAINING(obj->len, truncated); + if (domainname_setstr0_name(obj, (const char*)ptr, obj->len)) + goto fail; + ptr += obj->len; remaining -= obj->len; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +domainname_parse(domainname_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = domainname_new(); + if (NULL == *output) + return -1; + result = domainname_parse_into(*output, input, len_in); + if (result < 0) { + domainname_free(*output); + *output = NULL; + } + return result; +} +socks4_client_request_t * +socks4_client_request_new(void) +{ + socks4_client_request_t *val = trunnel_calloc(1, sizeof(socks4_client_request_t)); + if (NULL == val) + return NULL; + val->version = 4; + val->command = CMD_BIND; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks4_client_request_clear(socks4_client_request_t *obj) +{ + (void) obj; + trunnel_wipestr(obj->username); + trunnel_free(obj->username); + trunnel_wipestr(obj->socks4a_addr_hostname); + trunnel_free(obj->socks4a_addr_hostname); +} + +void +socks4_client_request_free(socks4_client_request_t *obj) +{ + if (obj == NULL) + return; + socks4_client_request_clear(obj); + trunnel_memwipe(obj, sizeof(socks4_client_request_t)); + trunnel_free_(obj); +} + +uint8_t +socks4_client_request_get_version(const socks4_client_request_t *inp) +{ + return inp->version; +} +int +socks4_client_request_set_version(socks4_client_request_t *inp, uint8_t val) +{ + if (! ((val == 4))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks4_client_request_get_command(const socks4_client_request_t *inp) +{ + return inp->command; +} +int +socks4_client_request_set_command(socks4_client_request_t *inp, uint8_t val) +{ + if (! ((val == CMD_BIND || val == CMD_CONNECT || val == CMD_RESOLVE || val == CMD_RESOLVE_PTR))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->command = val; + return 0; +} +uint16_t +socks4_client_request_get_port(const socks4_client_request_t *inp) +{ + return inp->port; +} +int +socks4_client_request_set_port(socks4_client_request_t *inp, uint16_t val) +{ + inp->port = val; + return 0; +} +uint32_t +socks4_client_request_get_addr(const socks4_client_request_t *inp) +{ + return inp->addr; +} +int +socks4_client_request_set_addr(socks4_client_request_t *inp, uint32_t val) +{ + inp->addr = val; + return 0; +} +const char * +socks4_client_request_get_username(const socks4_client_request_t *inp) +{ + return inp->username; +} +int +socks4_client_request_set_username(socks4_client_request_t *inp, const char *val) +{ + trunnel_free(inp->username); + if (NULL == (inp->username = trunnel_strdup(val))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + return 0; +} +const char * +socks4_client_request_get_socks4a_addr_hostname(const socks4_client_request_t *inp) +{ + return inp->socks4a_addr_hostname; +} +int +socks4_client_request_set_socks4a_addr_hostname(socks4_client_request_t *inp, const char *val) +{ + trunnel_free(inp->socks4a_addr_hostname); + if (NULL == (inp->socks4a_addr_hostname = trunnel_strdup(val))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + return 0; +} +const char * +socks4_client_request_check(const socks4_client_request_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 4)) + return "Integer out of bounds"; + if (! (obj->command == CMD_BIND || obj->command == CMD_CONNECT || obj->command == CMD_RESOLVE || obj->command == CMD_RESOLVE_PTR)) + return "Integer out of bounds"; + if (NULL == obj->username) + return "Missing username"; + switch (obj->addr) { + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 141: + case 142: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 230: + case 231: + case 232: + case 233: + case 234: + case 235: + case 236: + case 237: + case 238: + case 239: + case 240: + case 241: + case 242: + case 243: + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + if (NULL == obj->socks4a_addr_hostname) + return "Missing socks4a_addr_hostname"; + break; + + default: + break; + } + return NULL; +} + +ssize_t +socks4_client_request_encoded_len(const socks4_client_request_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks4_client_request_check(obj)) + return -1; + + + /* Length of u8 version IN [4] */ + result += 1; + + /* Length of u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR] */ + result += 1; + + /* Length of u16 port */ + result += 2; + + /* Length of u32 addr */ + result += 4; + + /* Length of nulterm username */ + result += strlen(obj->username) + 1; + switch (obj->addr) { + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 141: + case 142: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 230: + case 231: + case 232: + case 233: + case 234: + case 235: + case 236: + case 237: + case 238: + case 239: + case 240: + case 241: + case 242: + case 243: + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + + /* Length of nulterm socks4a_addr_hostname */ + result += strlen(obj->socks4a_addr_hostname) + 1; + break; + + default: + break; + } + return result; +} +int +socks4_client_request_clear_errors(socks4_client_request_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks4_client_request_encode(uint8_t *output, const size_t avail, const socks4_client_request_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks4_client_request_encoded_len(obj); +#endif + + if (NULL != (msg = socks4_client_request_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [4] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->command)); + written += 1; ptr += 1; + + /* Encode u16 port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->port)); + written += 2; ptr += 2; + + /* Encode u32 addr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->addr)); + written += 4; ptr += 4; + + /* Encode nulterm username */ + { + size_t len = strlen(obj->username); + trunnel_assert(written <= avail); + if (avail - written < len + 1) + goto truncated; + memcpy(ptr, obj->username, len + 1); + ptr += len + 1; written += len + 1; + } + + /* Encode union socks4a_addr[addr] */ + trunnel_assert(written <= avail); + switch (obj->addr) { + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 141: + case 142: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 230: + case 231: + case 232: + case 233: + case 234: + case 235: + case 236: + case 237: + case 238: + case 239: + case 240: + case 241: + case 242: + case 243: + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + + /* Encode nulterm socks4a_addr_hostname */ + { + size_t len = strlen(obj->socks4a_addr_hostname); + trunnel_assert(written <= avail); + if (avail - written < len + 1) + goto truncated; + memcpy(ptr, obj->socks4a_addr_hostname, len + 1); + ptr += len + 1; written += len + 1; + } + break; + + default: + break; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks4_client_request_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks4_client_request_parse_into(socks4_client_request_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [4] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 4)) + goto fail; + + /* Parse u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR] */ + CHECK_REMAINING(1, truncated); + obj->command = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->command == CMD_BIND || obj->command == CMD_CONNECT || obj->command == CMD_RESOLVE || obj->command == CMD_RESOLVE_PTR)) + goto fail; + + /* Parse u16 port */ + CHECK_REMAINING(2, truncated); + obj->port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + + /* Parse u32 addr */ + CHECK_REMAINING(4, truncated); + obj->addr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + + /* Parse nulterm username */ + { + uint8_t *eos = (uint8_t*)memchr(ptr, 0, remaining); + size_t memlen; + if (eos == NULL) + goto truncated; + trunnel_assert(eos >= ptr); + trunnel_assert((size_t)(eos - ptr) < SIZE_MAX - 1); + memlen = ((size_t)(eos - ptr)) + 1; + if (!(obj->username = trunnel_malloc(memlen))) + goto fail; + memcpy(obj->username, ptr, memlen); + remaining -= memlen; ptr += memlen; + } + + /* Parse union socks4a_addr[addr] */ + switch (obj->addr) { + + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + case 61: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 141: + case 142: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + case 204: + case 205: + case 206: + case 207: + case 208: + case 209: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + case 224: + case 225: + case 226: + case 227: + case 228: + case 229: + case 230: + case 231: + case 232: + case 233: + case 234: + case 235: + case 236: + case 237: + case 238: + case 239: + case 240: + case 241: + case 242: + case 243: + case 244: + case 245: + case 246: + case 247: + case 248: + case 249: + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + + /* Parse nulterm socks4a_addr_hostname */ + { + uint8_t *eos = (uint8_t*)memchr(ptr, 0, remaining); + size_t memlen; + if (eos == NULL) + goto truncated; + trunnel_assert(eos >= ptr); + trunnel_assert((size_t)(eos - ptr) < SIZE_MAX - 1); + memlen = ((size_t)(eos - ptr)) + 1; + if (!(obj->socks4a_addr_hostname = trunnel_malloc(memlen))) + goto fail; + memcpy(obj->socks4a_addr_hostname, ptr, memlen); + remaining -= memlen; ptr += memlen; + } + break; + + default: + break; + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +socks4_client_request_parse(socks4_client_request_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks4_client_request_new(); + if (NULL == *output) + return -1; + result = socks4_client_request_parse_into(*output, input, len_in); + if (result < 0) { + socks4_client_request_free(*output); + *output = NULL; + } + return result; +} +socks4_server_reply_t * +socks4_server_reply_new(void) +{ + socks4_server_reply_t *val = trunnel_calloc(1, sizeof(socks4_server_reply_t)); + if (NULL == val) + return NULL; + val->version = 4; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks4_server_reply_clear(socks4_server_reply_t *obj) +{ + (void) obj; +} + +void +socks4_server_reply_free(socks4_server_reply_t *obj) +{ + if (obj == NULL) + return; + socks4_server_reply_clear(obj); + trunnel_memwipe(obj, sizeof(socks4_server_reply_t)); + trunnel_free_(obj); +} + +uint8_t +socks4_server_reply_get_version(const socks4_server_reply_t *inp) +{ + return inp->version; +} +int +socks4_server_reply_set_version(socks4_server_reply_t *inp, uint8_t val) +{ + if (! ((val == 4))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks4_server_reply_get_status(const socks4_server_reply_t *inp) +{ + return inp->status; +} +int +socks4_server_reply_set_status(socks4_server_reply_t *inp, uint8_t val) +{ + inp->status = val; + return 0; +} +uint16_t +socks4_server_reply_get_port(const socks4_server_reply_t *inp) +{ + return inp->port; +} +int +socks4_server_reply_set_port(socks4_server_reply_t *inp, uint16_t val) +{ + inp->port = val; + return 0; +} +uint32_t +socks4_server_reply_get_addr(const socks4_server_reply_t *inp) +{ + return inp->addr; +} +int +socks4_server_reply_set_addr(socks4_server_reply_t *inp, uint32_t val) +{ + inp->addr = val; + return 0; +} +const char * +socks4_server_reply_check(const socks4_server_reply_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 4)) + return "Integer out of bounds"; + return NULL; +} + +ssize_t +socks4_server_reply_encoded_len(const socks4_server_reply_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks4_server_reply_check(obj)) + return -1; + + + /* Length of u8 version IN [4] */ + result += 1; + + /* Length of u8 status */ + result += 1; + + /* Length of u16 port */ + result += 2; + + /* Length of u32 addr */ + result += 4; + return result; +} +int +socks4_server_reply_clear_errors(socks4_server_reply_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks4_server_reply_encode(uint8_t *output, const size_t avail, const socks4_server_reply_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks4_server_reply_encoded_len(obj); +#endif + + if (NULL != (msg = socks4_server_reply_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [4] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 status */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->status)); + written += 1; ptr += 1; + + /* Encode u16 port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->port)); + written += 2; ptr += 2; + + /* Encode u32 addr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->addr)); + written += 4; ptr += 4; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks4_server_reply_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks4_server_reply_parse_into(socks4_server_reply_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [4] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 4)) + goto fail; + + /* Parse u8 status */ + CHECK_REMAINING(1, truncated); + obj->status = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u16 port */ + CHECK_REMAINING(2, truncated); + obj->port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + + /* Parse u32 addr */ + CHECK_REMAINING(4, truncated); + obj->addr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +socks4_server_reply_parse(socks4_server_reply_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks4_server_reply_new(); + if (NULL == *output) + return -1; + result = socks4_server_reply_parse_into(*output, input, len_in); + if (result < 0) { + socks4_server_reply_free(*output); + *output = NULL; + } + return result; +} +socks5_client_userpass_auth_t * +socks5_client_userpass_auth_new(void) +{ + socks5_client_userpass_auth_t *val = trunnel_calloc(1, sizeof(socks5_client_userpass_auth_t)); + if (NULL == val) + return NULL; + val->version = 1; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_client_userpass_auth_clear(socks5_client_userpass_auth_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->username); + TRUNNEL_DYNARRAY_CLEAR(&obj->username); + TRUNNEL_DYNARRAY_WIPE(&obj->passwd); + TRUNNEL_DYNARRAY_CLEAR(&obj->passwd); +} + +void +socks5_client_userpass_auth_free(socks5_client_userpass_auth_t *obj) +{ + if (obj == NULL) + return; + socks5_client_userpass_auth_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_client_userpass_auth_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_client_userpass_auth_get_version(const socks5_client_userpass_auth_t *inp) +{ + return inp->version; +} +int +socks5_client_userpass_auth_set_version(socks5_client_userpass_auth_t *inp, uint8_t val) +{ + if (! ((val == 1))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_client_userpass_auth_get_username_len(const socks5_client_userpass_auth_t *inp) +{ + return inp->username_len; +} +int +socks5_client_userpass_auth_set_username_len(socks5_client_userpass_auth_t *inp, uint8_t val) +{ + inp->username_len = val; + return 0; +} +size_t +socks5_client_userpass_auth_getlen_username(const socks5_client_userpass_auth_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->username); +} + +char +socks5_client_userpass_auth_get_username(socks5_client_userpass_auth_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->username, idx); +} + +char +socks5_client_userpass_auth_getconst_username(const socks5_client_userpass_auth_t *inp, size_t idx) +{ + return socks5_client_userpass_auth_get_username((socks5_client_userpass_auth_t*)inp, idx); +} +int +socks5_client_userpass_auth_set_username(socks5_client_userpass_auth_t *inp, size_t idx, char elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->username, idx, elt); + return 0; +} +int +socks5_client_userpass_auth_add_username(socks5_client_userpass_auth_t *inp, char elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->username.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(char, &inp->username, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +char * +socks5_client_userpass_auth_getarray_username(socks5_client_userpass_auth_t *inp) +{ + return inp->username.elts_; +} +const char * +socks5_client_userpass_auth_getconstarray_username(const socks5_client_userpass_auth_t *inp) +{ + return (const char *)socks5_client_userpass_auth_getarray_username((socks5_client_userpass_auth_t*)inp); +} +int +socks5_client_userpass_auth_setlen_username(socks5_client_userpass_auth_t *inp, size_t newlen) +{ +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + return trunnel_string_setlen(&inp->username, newlen, + &inp->trunnel_error_code_); + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +socks5_client_userpass_auth_getstr_username(socks5_client_userpass_auth_t *inp) +{ + return trunnel_string_getstr(&inp->username); +} +int +socks5_client_userpass_auth_setstr0_username(socks5_client_userpass_auth_t *inp, const char *val, size_t len) +{ +#if UINT8_MAX < SIZE_MAX + if (len > UINT8_MAX) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } +#endif + return trunnel_string_setstr0(&inp->username, val, len, &inp->trunnel_error_code_); +} +int +socks5_client_userpass_auth_setstr_username(socks5_client_userpass_auth_t *inp, const char *val) +{ + return socks5_client_userpass_auth_setstr0_username(inp, val, strlen(val)); +} +uint8_t +socks5_client_userpass_auth_get_passwd_len(const socks5_client_userpass_auth_t *inp) +{ + return inp->passwd_len; +} +int +socks5_client_userpass_auth_set_passwd_len(socks5_client_userpass_auth_t *inp, uint8_t val) +{ + inp->passwd_len = val; + return 0; +} +size_t +socks5_client_userpass_auth_getlen_passwd(const socks5_client_userpass_auth_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->passwd); +} + +char +socks5_client_userpass_auth_get_passwd(socks5_client_userpass_auth_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->passwd, idx); +} + +char +socks5_client_userpass_auth_getconst_passwd(const socks5_client_userpass_auth_t *inp, size_t idx) +{ + return socks5_client_userpass_auth_get_passwd((socks5_client_userpass_auth_t*)inp, idx); +} +int +socks5_client_userpass_auth_set_passwd(socks5_client_userpass_auth_t *inp, size_t idx, char elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->passwd, idx, elt); + return 0; +} +int +socks5_client_userpass_auth_add_passwd(socks5_client_userpass_auth_t *inp, char elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->passwd.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(char, &inp->passwd, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +char * +socks5_client_userpass_auth_getarray_passwd(socks5_client_userpass_auth_t *inp) +{ + return inp->passwd.elts_; +} +const char * +socks5_client_userpass_auth_getconstarray_passwd(const socks5_client_userpass_auth_t *inp) +{ + return (const char *)socks5_client_userpass_auth_getarray_passwd((socks5_client_userpass_auth_t*)inp); +} +int +socks5_client_userpass_auth_setlen_passwd(socks5_client_userpass_auth_t *inp, size_t newlen) +{ +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + return trunnel_string_setlen(&inp->passwd, newlen, + &inp->trunnel_error_code_); + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +socks5_client_userpass_auth_getstr_passwd(socks5_client_userpass_auth_t *inp) +{ + return trunnel_string_getstr(&inp->passwd); +} +int +socks5_client_userpass_auth_setstr0_passwd(socks5_client_userpass_auth_t *inp, const char *val, size_t len) +{ +#if UINT8_MAX < SIZE_MAX + if (len > UINT8_MAX) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } +#endif + return trunnel_string_setstr0(&inp->passwd, val, len, &inp->trunnel_error_code_); +} +int +socks5_client_userpass_auth_setstr_passwd(socks5_client_userpass_auth_t *inp, const char *val) +{ + return socks5_client_userpass_auth_setstr0_passwd(inp, val, strlen(val)); +} +const char * +socks5_client_userpass_auth_check(const socks5_client_userpass_auth_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 1)) + return "Integer out of bounds"; + if (TRUNNEL_DYNARRAY_LEN(&obj->username) != obj->username_len) + return "Length mismatch for username"; + if (TRUNNEL_DYNARRAY_LEN(&obj->passwd) != obj->passwd_len) + return "Length mismatch for passwd"; + return NULL; +} + +ssize_t +socks5_client_userpass_auth_encoded_len(const socks5_client_userpass_auth_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_client_userpass_auth_check(obj)) + return -1; + + + /* Length of u8 version IN [1] */ + result += 1; + + /* Length of u8 username_len */ + result += 1; + + /* Length of char username[username_len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->username); + + /* Length of u8 passwd_len */ + result += 1; + + /* Length of char passwd[passwd_len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->passwd); + return result; +} +int +socks5_client_userpass_auth_clear_errors(socks5_client_userpass_auth_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_client_userpass_auth_encode(uint8_t *output, const size_t avail, const socks5_client_userpass_auth_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_client_userpass_auth_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_client_userpass_auth_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [1] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 username_len */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->username_len)); + written += 1; ptr += 1; + + /* Encode char username[username_len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->username); + trunnel_assert(obj->username_len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->username.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + /* Encode u8 passwd_len */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->passwd_len)); + written += 1; ptr += 1; + + /* Encode char passwd[passwd_len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->passwd); + trunnel_assert(obj->passwd_len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->passwd.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_client_userpass_auth_parse(), but do not allocate the + * output object. + */ +static ssize_t +socks5_client_userpass_auth_parse_into(socks5_client_userpass_auth_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [1] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 1)) + goto fail; + + /* Parse u8 username_len */ + CHECK_REMAINING(1, truncated); + obj->username_len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse char username[username_len] */ + CHECK_REMAINING(obj->username_len, truncated); + if (socks5_client_userpass_auth_setstr0_username(obj, (const char*)ptr, obj->username_len)) + goto fail; + ptr += obj->username_len; remaining -= obj->username_len; + + /* Parse u8 passwd_len */ + CHECK_REMAINING(1, truncated); + obj->passwd_len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse char passwd[passwd_len] */ + CHECK_REMAINING(obj->passwd_len, truncated); + if (socks5_client_userpass_auth_setstr0_passwd(obj, (const char*)ptr, obj->passwd_len)) + goto fail; + ptr += obj->passwd_len; remaining -= obj->passwd_len; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +socks5_client_userpass_auth_parse(socks5_client_userpass_auth_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_client_userpass_auth_new(); + if (NULL == *output) + return -1; + result = socks5_client_userpass_auth_parse_into(*output, input, len_in); + if (result < 0) { + socks5_client_userpass_auth_free(*output); + *output = NULL; + } + return result; +} +socks5_client_version_t * +socks5_client_version_new(void) +{ + socks5_client_version_t *val = trunnel_calloc(1, sizeof(socks5_client_version_t)); + if (NULL == val) + return NULL; + val->version = 5; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_client_version_clear(socks5_client_version_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->methods); + TRUNNEL_DYNARRAY_CLEAR(&obj->methods); +} + +void +socks5_client_version_free(socks5_client_version_t *obj) +{ + if (obj == NULL) + return; + socks5_client_version_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_client_version_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_client_version_get_version(const socks5_client_version_t *inp) +{ + return inp->version; +} +int +socks5_client_version_set_version(socks5_client_version_t *inp, uint8_t val) +{ + if (! ((val == 5))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_client_version_get_n_methods(const socks5_client_version_t *inp) +{ + return inp->n_methods; +} +int +socks5_client_version_set_n_methods(socks5_client_version_t *inp, uint8_t val) +{ + inp->n_methods = val; + return 0; +} +size_t +socks5_client_version_getlen_methods(const socks5_client_version_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->methods); +} + +uint8_t +socks5_client_version_get_methods(socks5_client_version_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->methods, idx); +} + +uint8_t +socks5_client_version_getconst_methods(const socks5_client_version_t *inp, size_t idx) +{ + return socks5_client_version_get_methods((socks5_client_version_t*)inp, idx); +} +int +socks5_client_version_set_methods(socks5_client_version_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->methods, idx, elt); + return 0; +} +int +socks5_client_version_add_methods(socks5_client_version_t *inp, uint8_t elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->methods.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->methods, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +socks5_client_version_getarray_methods(socks5_client_version_t *inp) +{ + return inp->methods.elts_; +} +const uint8_t * +socks5_client_version_getconstarray_methods(const socks5_client_version_t *inp) +{ + return (const uint8_t *)socks5_client_version_getarray_methods((socks5_client_version_t*)inp); +} +int +socks5_client_version_setlen_methods(socks5_client_version_t *inp, size_t newlen) +{ + uint8_t *newptr; +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->methods.allocated_, + &inp->methods.n_, inp->methods.elts_, newlen, + sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newlen != 0 && newptr == NULL) + goto trunnel_alloc_failed; + inp->methods.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +socks5_client_version_check(const socks5_client_version_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 5)) + return "Integer out of bounds"; + if (TRUNNEL_DYNARRAY_LEN(&obj->methods) != obj->n_methods) + return "Length mismatch for methods"; + return NULL; +} + +ssize_t +socks5_client_version_encoded_len(const socks5_client_version_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_client_version_check(obj)) + return -1; + + + /* Length of u8 version IN [5] */ + result += 1; + + /* Length of u8 n_methods */ + result += 1; + + /* Length of u8 methods[n_methods] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->methods); + return result; +} +int +socks5_client_version_clear_errors(socks5_client_version_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_client_version_encode(uint8_t *output, const size_t avail, const socks5_client_version_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_client_version_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_client_version_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [5] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 n_methods */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->n_methods)); + written += 1; ptr += 1; + + /* Encode u8 methods[n_methods] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->methods); + trunnel_assert(obj->n_methods == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->methods.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_client_version_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks5_client_version_parse_into(socks5_client_version_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [5] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 5)) + goto fail; + + /* Parse u8 n_methods */ + CHECK_REMAINING(1, truncated); + obj->n_methods = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 methods[n_methods] */ + CHECK_REMAINING(obj->n_methods, truncated); + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->methods, obj->n_methods, {}); + obj->methods.n_ = obj->n_methods; + if (obj->n_methods) + memcpy(obj->methods.elts_, ptr, obj->n_methods); + ptr += obj->n_methods; remaining -= obj->n_methods; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +socks5_client_version_parse(socks5_client_version_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_client_version_new(); + if (NULL == *output) + return -1; + result = socks5_client_version_parse_into(*output, input, len_in); + if (result < 0) { + socks5_client_version_free(*output); + *output = NULL; + } + return result; +} +socks5_server_method_t * +socks5_server_method_new(void) +{ + socks5_server_method_t *val = trunnel_calloc(1, sizeof(socks5_server_method_t)); + if (NULL == val) + return NULL; + val->version = 5; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_server_method_clear(socks5_server_method_t *obj) +{ + (void) obj; +} + +void +socks5_server_method_free(socks5_server_method_t *obj) +{ + if (obj == NULL) + return; + socks5_server_method_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_server_method_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_server_method_get_version(const socks5_server_method_t *inp) +{ + return inp->version; +} +int +socks5_server_method_set_version(socks5_server_method_t *inp, uint8_t val) +{ + if (! ((val == 5))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_server_method_get_method(const socks5_server_method_t *inp) +{ + return inp->method; +} +int +socks5_server_method_set_method(socks5_server_method_t *inp, uint8_t val) +{ + inp->method = val; + return 0; +} +const char * +socks5_server_method_check(const socks5_server_method_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 5)) + return "Integer out of bounds"; + return NULL; +} + +ssize_t +socks5_server_method_encoded_len(const socks5_server_method_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_server_method_check(obj)) + return -1; + + + /* Length of u8 version IN [5] */ + result += 1; + + /* Length of u8 method */ + result += 1; + return result; +} +int +socks5_server_method_clear_errors(socks5_server_method_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_server_method_encode(uint8_t *output, const size_t avail, const socks5_server_method_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_server_method_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_server_method_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [5] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 method */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->method)); + written += 1; ptr += 1; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_server_method_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks5_server_method_parse_into(socks5_server_method_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [5] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 5)) + goto fail; + + /* Parse u8 method */ + CHECK_REMAINING(1, truncated); + obj->method = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +socks5_server_method_parse(socks5_server_method_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_server_method_new(); + if (NULL == *output) + return -1; + result = socks5_server_method_parse_into(*output, input, len_in); + if (result < 0) { + socks5_server_method_free(*output); + *output = NULL; + } + return result; +} +socks5_server_userpass_auth_t * +socks5_server_userpass_auth_new(void) +{ + socks5_server_userpass_auth_t *val = trunnel_calloc(1, sizeof(socks5_server_userpass_auth_t)); + if (NULL == val) + return NULL; + val->version = 1; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_server_userpass_auth_clear(socks5_server_userpass_auth_t *obj) +{ + (void) obj; +} + +void +socks5_server_userpass_auth_free(socks5_server_userpass_auth_t *obj) +{ + if (obj == NULL) + return; + socks5_server_userpass_auth_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_server_userpass_auth_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_server_userpass_auth_get_version(const socks5_server_userpass_auth_t *inp) +{ + return inp->version; +} +int +socks5_server_userpass_auth_set_version(socks5_server_userpass_auth_t *inp, uint8_t val) +{ + if (! ((val == 1))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_server_userpass_auth_get_status(const socks5_server_userpass_auth_t *inp) +{ + return inp->status; +} +int +socks5_server_userpass_auth_set_status(socks5_server_userpass_auth_t *inp, uint8_t val) +{ + inp->status = val; + return 0; +} +const char * +socks5_server_userpass_auth_check(const socks5_server_userpass_auth_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 1)) + return "Integer out of bounds"; + return NULL; +} + +ssize_t +socks5_server_userpass_auth_encoded_len(const socks5_server_userpass_auth_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_server_userpass_auth_check(obj)) + return -1; + + + /* Length of u8 version IN [1] */ + result += 1; + + /* Length of u8 status */ + result += 1; + return result; +} +int +socks5_server_userpass_auth_clear_errors(socks5_server_userpass_auth_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_server_userpass_auth_encode(uint8_t *output, const size_t avail, const socks5_server_userpass_auth_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_server_userpass_auth_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_server_userpass_auth_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [1] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 status */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->status)); + written += 1; ptr += 1; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_server_userpass_auth_parse(), but do not allocate the + * output object. + */ +static ssize_t +socks5_server_userpass_auth_parse_into(socks5_server_userpass_auth_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [1] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 1)) + goto fail; + + /* Parse u8 status */ + CHECK_REMAINING(1, truncated); + obj->status = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + fail: + result = -1; + return result; +} + +ssize_t +socks5_server_userpass_auth_parse(socks5_server_userpass_auth_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_server_userpass_auth_new(); + if (NULL == *output) + return -1; + result = socks5_server_userpass_auth_parse_into(*output, input, len_in); + if (result < 0) { + socks5_server_userpass_auth_free(*output); + *output = NULL; + } + return result; +} +socks5_client_request_t * +socks5_client_request_new(void) +{ + socks5_client_request_t *val = trunnel_calloc(1, sizeof(socks5_client_request_t)); + if (NULL == val) + return NULL; + val->version = 5; + val->command = CMD_BIND; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_client_request_clear(socks5_client_request_t *obj) +{ + (void) obj; + domainname_free(obj->dest_addr_domainname); + obj->dest_addr_domainname = NULL; +} + +void +socks5_client_request_free(socks5_client_request_t *obj) +{ + if (obj == NULL) + return; + socks5_client_request_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_client_request_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_client_request_get_version(const socks5_client_request_t *inp) +{ + return inp->version; +} +int +socks5_client_request_set_version(socks5_client_request_t *inp, uint8_t val) +{ + if (! ((val == 5))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_client_request_get_command(const socks5_client_request_t *inp) +{ + return inp->command; +} +int +socks5_client_request_set_command(socks5_client_request_t *inp, uint8_t val) +{ + if (! ((val == CMD_BIND || val == CMD_CONNECT || val == CMD_RESOLVE || val == CMD_RESOLVE_PTR || val == CMD_UDP_ASSOCIATE))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->command = val; + return 0; +} +uint8_t +socks5_client_request_get_reserved(const socks5_client_request_t *inp) +{ + return inp->reserved; +} +int +socks5_client_request_set_reserved(socks5_client_request_t *inp, uint8_t val) +{ + if (! ((val == 0))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->reserved = val; + return 0; +} +uint8_t +socks5_client_request_get_atype(const socks5_client_request_t *inp) +{ + return inp->atype; +} +int +socks5_client_request_set_atype(socks5_client_request_t *inp, uint8_t val) +{ + inp->atype = val; + return 0; +} +uint32_t +socks5_client_request_get_dest_addr_ipv4(const socks5_client_request_t *inp) +{ + return inp->dest_addr_ipv4; +} +int +socks5_client_request_set_dest_addr_ipv4(socks5_client_request_t *inp, uint32_t val) +{ + inp->dest_addr_ipv4 = val; + return 0; +} +size_t +socks5_client_request_getlen_dest_addr_ipv6(const socks5_client_request_t *inp) +{ + (void)inp; return 16; +} + +uint8_t +socks5_client_request_get_dest_addr_ipv6(socks5_client_request_t *inp, size_t idx) +{ + trunnel_assert(idx < 16); + return inp->dest_addr_ipv6[idx]; +} + +uint8_t +socks5_client_request_getconst_dest_addr_ipv6(const socks5_client_request_t *inp, size_t idx) +{ + return socks5_client_request_get_dest_addr_ipv6((socks5_client_request_t*)inp, idx); +} +int +socks5_client_request_set_dest_addr_ipv6(socks5_client_request_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 16); + inp->dest_addr_ipv6[idx] = elt; + return 0; +} + +uint8_t * +socks5_client_request_getarray_dest_addr_ipv6(socks5_client_request_t *inp) +{ + return inp->dest_addr_ipv6; +} +const uint8_t * +socks5_client_request_getconstarray_dest_addr_ipv6(const socks5_client_request_t *inp) +{ + return (const uint8_t *)socks5_client_request_getarray_dest_addr_ipv6((socks5_client_request_t*)inp); +} +struct domainname_st * +socks5_client_request_get_dest_addr_domainname(socks5_client_request_t *inp) +{ + return inp->dest_addr_domainname; +} +const struct domainname_st * +socks5_client_request_getconst_dest_addr_domainname(const socks5_client_request_t *inp) +{ + return socks5_client_request_get_dest_addr_domainname((socks5_client_request_t*) inp); +} +int +socks5_client_request_set_dest_addr_domainname(socks5_client_request_t *inp, struct domainname_st *val) +{ + if (inp->dest_addr_domainname && inp->dest_addr_domainname != val) + domainname_free(inp->dest_addr_domainname); + return socks5_client_request_set0_dest_addr_domainname(inp, val); +} +int +socks5_client_request_set0_dest_addr_domainname(socks5_client_request_t *inp, struct domainname_st *val) +{ + inp->dest_addr_domainname = val; + return 0; +} +uint16_t +socks5_client_request_get_dest_port(const socks5_client_request_t *inp) +{ + return inp->dest_port; +} +int +socks5_client_request_set_dest_port(socks5_client_request_t *inp, uint16_t val) +{ + inp->dest_port = val; + return 0; +} +const char * +socks5_client_request_check(const socks5_client_request_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 5)) + return "Integer out of bounds"; + if (! (obj->command == CMD_BIND || obj->command == CMD_CONNECT || obj->command == CMD_RESOLVE || obj->command == CMD_RESOLVE_PTR || obj->command == CMD_UDP_ASSOCIATE)) + return "Integer out of bounds"; + if (! (obj->reserved == 0)) + return "Integer out of bounds"; + switch (obj->atype) { + + case ATYPE_IPV4: + break; + + case ATYPE_IPV6: + break; + + case ATYPE_DOMAINNAME: + { + const char *msg; + if (NULL != (msg = domainname_check(obj->dest_addr_domainname))) + return msg; + } + break; + + default: + return "Bad tag for union"; + break; + } + return NULL; +} + +ssize_t +socks5_client_request_encoded_len(const socks5_client_request_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_client_request_check(obj)) + return -1; + + + /* Length of u8 version IN [5] */ + result += 1; + + /* Length of u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR, CMD_UDP_ASSOCIATE] */ + result += 1; + + /* Length of u8 reserved IN [0] */ + result += 1; + + /* Length of u8 atype */ + result += 1; + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Length of u32 dest_addr_ipv4 */ + result += 4; + break; + + case ATYPE_IPV6: + + /* Length of u8 dest_addr_ipv6[16] */ + result += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Length of struct domainname dest_addr_domainname */ + result += domainname_encoded_len(obj->dest_addr_domainname); + break; + + default: + trunnel_assert(0); + break; + } + + /* Length of u16 dest_port */ + result += 2; + return result; +} +int +socks5_client_request_clear_errors(socks5_client_request_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_client_request_encode(uint8_t *output, const size_t avail, const socks5_client_request_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_client_request_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_client_request_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [5] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR, CMD_UDP_ASSOCIATE] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->command)); + written += 1; ptr += 1; + + /* Encode u8 reserved IN [0] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->reserved)); + written += 1; ptr += 1; + + /* Encode u8 atype */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->atype)); + written += 1; ptr += 1; + + /* Encode union dest_addr[atype] */ + trunnel_assert(written <= avail); + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Encode u32 dest_addr_ipv4 */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->dest_addr_ipv4)); + written += 4; ptr += 4; + break; + + case ATYPE_IPV6: + + /* Encode u8 dest_addr_ipv6[16] */ + trunnel_assert(written <= avail); + if (avail - written < 16) + goto truncated; + memcpy(ptr, obj->dest_addr_ipv6, 16); + written += 16; ptr += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Encode struct domainname dest_addr_domainname */ + trunnel_assert(written <= avail); + result = domainname_encode(ptr, avail - written, obj->dest_addr_domainname); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + break; + + default: + trunnel_assert(0); + break; + } + + /* Encode u16 dest_port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->dest_port)); + written += 2; ptr += 2; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_client_request_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks5_client_request_parse_into(socks5_client_request_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [5] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 5)) + goto fail; + + /* Parse u8 command IN [CMD_BIND, CMD_CONNECT, CMD_RESOLVE, CMD_RESOLVE_PTR, CMD_UDP_ASSOCIATE] */ + CHECK_REMAINING(1, truncated); + obj->command = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->command == CMD_BIND || obj->command == CMD_CONNECT || obj->command == CMD_RESOLVE || obj->command == CMD_RESOLVE_PTR || obj->command == CMD_UDP_ASSOCIATE)) + goto fail; + + /* Parse u8 reserved IN [0] */ + CHECK_REMAINING(1, truncated); + obj->reserved = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->reserved == 0)) + goto fail; + + /* Parse u8 atype */ + CHECK_REMAINING(1, truncated); + obj->atype = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse union dest_addr[atype] */ + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Parse u32 dest_addr_ipv4 */ + CHECK_REMAINING(4, truncated); + obj->dest_addr_ipv4 = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + break; + + case ATYPE_IPV6: + + /* Parse u8 dest_addr_ipv6[16] */ + CHECK_REMAINING(16, truncated); + memcpy(obj->dest_addr_ipv6, ptr, 16); + remaining -= 16; ptr += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Parse struct domainname dest_addr_domainname */ + result = domainname_parse(&obj->dest_addr_domainname, ptr, remaining); + if (result < 0) + goto relay_fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + break; + + default: + goto fail; + break; + } + + /* Parse u16 dest_port */ + CHECK_REMAINING(2, truncated); + obj->dest_port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + relay_fail: + trunnel_assert(result < 0); + return result; + fail: + result = -1; + return result; +} + +ssize_t +socks5_client_request_parse(socks5_client_request_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_client_request_new(); + if (NULL == *output) + return -1; + result = socks5_client_request_parse_into(*output, input, len_in); + if (result < 0) { + socks5_client_request_free(*output); + *output = NULL; + } + return result; +} +socks5_server_reply_t * +socks5_server_reply_new(void) +{ + socks5_server_reply_t *val = trunnel_calloc(1, sizeof(socks5_server_reply_t)); + if (NULL == val) + return NULL; + val->version = 5; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +socks5_server_reply_clear(socks5_server_reply_t *obj) +{ + (void) obj; + domainname_free(obj->bind_addr_domainname); + obj->bind_addr_domainname = NULL; +} + +void +socks5_server_reply_free(socks5_server_reply_t *obj) +{ + if (obj == NULL) + return; + socks5_server_reply_clear(obj); + trunnel_memwipe(obj, sizeof(socks5_server_reply_t)); + trunnel_free_(obj); +} + +uint8_t +socks5_server_reply_get_version(const socks5_server_reply_t *inp) +{ + return inp->version; +} +int +socks5_server_reply_set_version(socks5_server_reply_t *inp, uint8_t val) +{ + if (! ((val == 5))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +socks5_server_reply_get_reply(const socks5_server_reply_t *inp) +{ + return inp->reply; +} +int +socks5_server_reply_set_reply(socks5_server_reply_t *inp, uint8_t val) +{ + inp->reply = val; + return 0; +} +uint8_t +socks5_server_reply_get_reserved(const socks5_server_reply_t *inp) +{ + return inp->reserved; +} +int +socks5_server_reply_set_reserved(socks5_server_reply_t *inp, uint8_t val) +{ + if (! ((val == 0))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->reserved = val; + return 0; +} +uint8_t +socks5_server_reply_get_atype(const socks5_server_reply_t *inp) +{ + return inp->atype; +} +int +socks5_server_reply_set_atype(socks5_server_reply_t *inp, uint8_t val) +{ + inp->atype = val; + return 0; +} +uint32_t +socks5_server_reply_get_bind_addr_ipv4(const socks5_server_reply_t *inp) +{ + return inp->bind_addr_ipv4; +} +int +socks5_server_reply_set_bind_addr_ipv4(socks5_server_reply_t *inp, uint32_t val) +{ + inp->bind_addr_ipv4 = val; + return 0; +} +size_t +socks5_server_reply_getlen_bind_addr_ipv6(const socks5_server_reply_t *inp) +{ + (void)inp; return 16; +} + +uint8_t +socks5_server_reply_get_bind_addr_ipv6(socks5_server_reply_t *inp, size_t idx) +{ + trunnel_assert(idx < 16); + return inp->bind_addr_ipv6[idx]; +} + +uint8_t +socks5_server_reply_getconst_bind_addr_ipv6(const socks5_server_reply_t *inp, size_t idx) +{ + return socks5_server_reply_get_bind_addr_ipv6((socks5_server_reply_t*)inp, idx); +} +int +socks5_server_reply_set_bind_addr_ipv6(socks5_server_reply_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 16); + inp->bind_addr_ipv6[idx] = elt; + return 0; +} + +uint8_t * +socks5_server_reply_getarray_bind_addr_ipv6(socks5_server_reply_t *inp) +{ + return inp->bind_addr_ipv6; +} +const uint8_t * +socks5_server_reply_getconstarray_bind_addr_ipv6(const socks5_server_reply_t *inp) +{ + return (const uint8_t *)socks5_server_reply_getarray_bind_addr_ipv6((socks5_server_reply_t*)inp); +} +struct domainname_st * +socks5_server_reply_get_bind_addr_domainname(socks5_server_reply_t *inp) +{ + return inp->bind_addr_domainname; +} +const struct domainname_st * +socks5_server_reply_getconst_bind_addr_domainname(const socks5_server_reply_t *inp) +{ + return socks5_server_reply_get_bind_addr_domainname((socks5_server_reply_t*) inp); +} +int +socks5_server_reply_set_bind_addr_domainname(socks5_server_reply_t *inp, struct domainname_st *val) +{ + if (inp->bind_addr_domainname && inp->bind_addr_domainname != val) + domainname_free(inp->bind_addr_domainname); + return socks5_server_reply_set0_bind_addr_domainname(inp, val); +} +int +socks5_server_reply_set0_bind_addr_domainname(socks5_server_reply_t *inp, struct domainname_st *val) +{ + inp->bind_addr_domainname = val; + return 0; +} +uint16_t +socks5_server_reply_get_bind_port(const socks5_server_reply_t *inp) +{ + return inp->bind_port; +} +int +socks5_server_reply_set_bind_port(socks5_server_reply_t *inp, uint16_t val) +{ + inp->bind_port = val; + return 0; +} +const char * +socks5_server_reply_check(const socks5_server_reply_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 5)) + return "Integer out of bounds"; + if (! (obj->reserved == 0)) + return "Integer out of bounds"; + switch (obj->atype) { + + case ATYPE_IPV4: + break; + + case ATYPE_IPV6: + break; + + case ATYPE_DOMAINNAME: + { + const char *msg; + if (NULL != (msg = domainname_check(obj->bind_addr_domainname))) + return msg; + } + break; + + default: + return "Bad tag for union"; + break; + } + return NULL; +} + +ssize_t +socks5_server_reply_encoded_len(const socks5_server_reply_t *obj) +{ + ssize_t result = 0; + + if (NULL != socks5_server_reply_check(obj)) + return -1; + + + /* Length of u8 version IN [5] */ + result += 1; + + /* Length of u8 reply */ + result += 1; + + /* Length of u8 reserved IN [0] */ + result += 1; + + /* Length of u8 atype */ + result += 1; + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Length of u32 bind_addr_ipv4 */ + result += 4; + break; + + case ATYPE_IPV6: + + /* Length of u8 bind_addr_ipv6[16] */ + result += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Length of struct domainname bind_addr_domainname */ + result += domainname_encoded_len(obj->bind_addr_domainname); + break; + + default: + trunnel_assert(0); + break; + } + + /* Length of u16 bind_port */ + result += 2; + return result; +} +int +socks5_server_reply_clear_errors(socks5_server_reply_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +socks5_server_reply_encode(uint8_t *output, const size_t avail, const socks5_server_reply_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = socks5_server_reply_encoded_len(obj); +#endif + + if (NULL != (msg = socks5_server_reply_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [5] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 reply */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->reply)); + written += 1; ptr += 1; + + /* Encode u8 reserved IN [0] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->reserved)); + written += 1; ptr += 1; + + /* Encode u8 atype */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->atype)); + written += 1; ptr += 1; + + /* Encode union bind_addr[atype] */ + trunnel_assert(written <= avail); + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Encode u32 bind_addr_ipv4 */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->bind_addr_ipv4)); + written += 4; ptr += 4; + break; + + case ATYPE_IPV6: + + /* Encode u8 bind_addr_ipv6[16] */ + trunnel_assert(written <= avail); + if (avail - written < 16) + goto truncated; + memcpy(ptr, obj->bind_addr_ipv6, 16); + written += 16; ptr += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Encode struct domainname bind_addr_domainname */ + trunnel_assert(written <= avail); + result = domainname_encode(ptr, avail - written, obj->bind_addr_domainname); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + break; + + default: + trunnel_assert(0); + break; + } + + /* Encode u16 bind_port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->bind_port)); + written += 2; ptr += 2; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As socks5_server_reply_parse(), but do not allocate the output + * object. + */ +static ssize_t +socks5_server_reply_parse_into(socks5_server_reply_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [5] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 5)) + goto fail; + + /* Parse u8 reply */ + CHECK_REMAINING(1, truncated); + obj->reply = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 reserved IN [0] */ + CHECK_REMAINING(1, truncated); + obj->reserved = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->reserved == 0)) + goto fail; + + /* Parse u8 atype */ + CHECK_REMAINING(1, truncated); + obj->atype = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse union bind_addr[atype] */ + switch (obj->atype) { + + case ATYPE_IPV4: + + /* Parse u32 bind_addr_ipv4 */ + CHECK_REMAINING(4, truncated); + obj->bind_addr_ipv4 = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + break; + + case ATYPE_IPV6: + + /* Parse u8 bind_addr_ipv6[16] */ + CHECK_REMAINING(16, truncated); + memcpy(obj->bind_addr_ipv6, ptr, 16); + remaining -= 16; ptr += 16; + break; + + case ATYPE_DOMAINNAME: + + /* Parse struct domainname bind_addr_domainname */ + result = domainname_parse(&obj->bind_addr_domainname, ptr, remaining); + if (result < 0) + goto relay_fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + break; + + default: + goto fail; + break; + } + + /* Parse u16 bind_port */ + CHECK_REMAINING(2, truncated); + obj->bind_port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + relay_fail: + trunnel_assert(result < 0); + return result; + fail: + result = -1; + return result; +} + +ssize_t +socks5_server_reply_parse(socks5_server_reply_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = socks5_server_reply_new(); + if (NULL == *output) + return -1; + result = socks5_server_reply_parse_into(*output, input, len_in); + if (result < 0) { + socks5_server_reply_free(*output); + *output = NULL; + } + return result; +} diff --git a/src/trunnel/socks5.h b/src/trunnel/socks5.h new file mode 100644 index 0000000000..d3bea152e7 --- /dev/null +++ b/src/trunnel/socks5.h @@ -0,0 +1,995 @@ +/* socks5.h -- generated by Trunnel v1.5.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_SOCKS5_H +#define TRUNNEL_SOCKS5_H + +#include <stdint.h> +#include "trunnel.h" + +#define CMD_CONNECT 1 +#define CMD_BIND 2 +#define CMD_UDP_ASSOCIATE 3 +#define CMD_RESOLVE 240 +#define CMD_RESOLVE_PTR 241 +#define ATYPE_IPV4 1 +#define ATYPE_IPV6 4 +#define ATYPE_DOMAINNAME 3 +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_DOMAINNAME) +struct domainname_st { + uint8_t len; + trunnel_string_t name; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct domainname_st domainname_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS4_CLIENT_REQUEST) +struct socks4_client_request_st { + uint8_t version; + uint8_t command; + uint16_t port; + uint32_t addr; + char *username; + char *socks4a_addr_hostname; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks4_client_request_st socks4_client_request_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS4_SERVER_REPLY) +struct socks4_server_reply_st { + uint8_t version; + uint8_t status; + uint16_t port; + uint32_t addr; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks4_server_reply_st socks4_server_reply_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_CLIENT_USERPASS_AUTH) +struct socks5_client_userpass_auth_st { + uint8_t version; + uint8_t username_len; + trunnel_string_t username; + uint8_t passwd_len; + trunnel_string_t passwd; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_client_userpass_auth_st socks5_client_userpass_auth_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_CLIENT_VERSION) +struct socks5_client_version_st { + uint8_t version; + uint8_t n_methods; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) methods; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_client_version_st socks5_client_version_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_SERVER_METHOD) +struct socks5_server_method_st { + uint8_t version; + uint8_t method; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_server_method_st socks5_server_method_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_SERVER_USERPASS_AUTH) +struct socks5_server_userpass_auth_st { + uint8_t version; + uint8_t status; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_server_userpass_auth_st socks5_server_userpass_auth_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_CLIENT_REQUEST) +struct socks5_client_request_st { + uint8_t version; + uint8_t command; + uint8_t reserved; + uint8_t atype; + uint32_t dest_addr_ipv4; + uint8_t dest_addr_ipv6[16]; + struct domainname_st *dest_addr_domainname; + uint16_t dest_port; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_client_request_st socks5_client_request_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_SOCKS5_SERVER_REPLY) +struct socks5_server_reply_st { + uint8_t version; + uint8_t reply; + uint8_t reserved; + uint8_t atype; + uint32_t bind_addr_ipv4; + uint8_t bind_addr_ipv6[16]; + struct domainname_st *bind_addr_domainname; + uint16_t bind_port; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct socks5_server_reply_st socks5_server_reply_t; +/** Return a newly allocated domainname with all elements set to zero. + */ +domainname_t *domainname_new(void); +/** Release all storage held by the domainname in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void domainname_free(domainname_t *victim); +/** Try to parse a domainname from the buffer in 'input', using up to + * 'len_in' bytes from the input buffer. On success, return the number + * of bytes consumed and set *output to the newly allocated + * domainname_t. On failure, return -2 if the input appears truncated, + * and -1 if the input is otherwise invalid. + */ +ssize_t domainname_parse(domainname_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * domainname in 'obj'. On failure, return a negative value. Note that + * this value may be an overestimate, and can even be an underestimate + * for certain unencodeable objects. + */ +ssize_t domainname_encoded_len(const domainname_t *obj); +/** Try to encode the domainname from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t domainname_encode(uint8_t *output, size_t avail, const domainname_t *input); +/** Check whether the internal state of the domainname in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ +const char *domainname_check(const domainname_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int domainname_clear_errors(domainname_t *obj); +/** Return the value of the len field of the domainname_t in 'inp' + */ +uint8_t domainname_get_len(const domainname_t *inp); +/** Set the value of the len field of the domainname_t in 'inp' to + * 'val'. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ +int domainname_set_len(domainname_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the name field of + * the domainname_t in 'inp'. + */ +size_t domainname_getlen_name(const domainname_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * name of the domainname_t in 'inp'. + */ +char domainname_get_name(domainname_t *inp, size_t idx); +/** As domainname_get_name, but take and return a const pointer + */ +char domainname_getconst_name(const domainname_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * name of the domainname_t in 'inp', so that it will hold the value + * 'elt'. + */ +int domainname_set_name(domainname_t *inp, size_t idx, char elt); +/** Append a new element 'elt' to the dynamic array field name of the + * domainname_t in 'inp'. + */ +int domainname_add_name(domainname_t *inp, char elt); +/** Return a pointer to the variable-length array field name of 'inp'. + */ +char * domainname_getarray_name(domainname_t *inp); +/** As domainname_get_name, but take and return a const pointer + */ +const char * domainname_getconstarray_name(const domainname_t *inp); +/** Change the length of the variable-length array field name of 'inp' + * to 'newlen'.Fill extra elements with 0. Return 0 on success; return + * -1 and set the error code on 'inp' on failure. + */ +int domainname_setlen_name(domainname_t *inp, size_t newlen); +/** Return the value of the name field of a domainname_t as a NUL- + * terminated string. + */ +const char * domainname_getstr_name(domainname_t *inp); +/** Set the value of the name field of a domainname_t to a given + * string of length 'len'. Return 0 on success; return -1 and set the + * error code on 'inp' on failure. + */ +int domainname_setstr0_name(domainname_t *inp, const char *val, size_t len); +/** Set the value of the name field of a domainname_t to a given NUL- + * terminated string. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int domainname_setstr_name(domainname_t *inp, const char *val); +/** Return a newly allocated socks4_client_request with all elements + * set to zero. + */ +socks4_client_request_t *socks4_client_request_new(void); +/** Release all storage held by the socks4_client_request in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks4_client_request_free(socks4_client_request_t *victim); +/** Try to parse a socks4_client_request from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks4_client_request_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks4_client_request_parse(socks4_client_request_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks4_client_request in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t socks4_client_request_encoded_len(const socks4_client_request_t *obj); +/** Try to encode the socks4_client_request from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks4_client_request_encode(uint8_t *output, size_t avail, const socks4_client_request_t *input); +/** Check whether the internal state of the socks4_client_request in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks4_client_request_check(const socks4_client_request_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks4_client_request_clear_errors(socks4_client_request_t *obj); +/** Return the value of the version field of the + * socks4_client_request_t in 'inp' + */ +uint8_t socks4_client_request_get_version(const socks4_client_request_t *inp); +/** Set the value of the version field of the socks4_client_request_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_client_request_set_version(socks4_client_request_t *inp, uint8_t val); +/** Return the value of the command field of the + * socks4_client_request_t in 'inp' + */ +uint8_t socks4_client_request_get_command(const socks4_client_request_t *inp); +/** Set the value of the command field of the socks4_client_request_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_client_request_set_command(socks4_client_request_t *inp, uint8_t val); +/** Return the value of the port field of the socks4_client_request_t + * in 'inp' + */ +uint16_t socks4_client_request_get_port(const socks4_client_request_t *inp); +/** Set the value of the port field of the socks4_client_request_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_client_request_set_port(socks4_client_request_t *inp, uint16_t val); +/** Return the value of the addr field of the socks4_client_request_t + * in 'inp' + */ +uint32_t socks4_client_request_get_addr(const socks4_client_request_t *inp); +/** Set the value of the addr field of the socks4_client_request_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_client_request_set_addr(socks4_client_request_t *inp, uint32_t val); +/** Return the value of the username field of the + * socks4_client_request_t in 'inp' + */ +const char * socks4_client_request_get_username(const socks4_client_request_t *inp); +/** Set the value of the username field of the socks4_client_request_t + * in 'inp' to 'val'. Free the old value if any. Does not steal the + * reference to 'val'.Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_client_request_set_username(socks4_client_request_t *inp, const char *val); +/** Return the value of the socks4a_addr_hostname field of the + * socks4_client_request_t in 'inp' + */ +const char * socks4_client_request_get_socks4a_addr_hostname(const socks4_client_request_t *inp); +/** Set the value of the socks4a_addr_hostname field of the + * socks4_client_request_t in 'inp' to 'val'. Free the old value if + * any. Does not steal the reference to 'val'.Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks4_client_request_set_socks4a_addr_hostname(socks4_client_request_t *inp, const char *val); +/** Return a newly allocated socks4_server_reply with all elements set + * to zero. + */ +socks4_server_reply_t *socks4_server_reply_new(void); +/** Release all storage held by the socks4_server_reply in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks4_server_reply_free(socks4_server_reply_t *victim); +/** Try to parse a socks4_server_reply from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks4_server_reply_t. On failure, return -2 if the input + * appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks4_server_reply_parse(socks4_server_reply_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks4_server_reply in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t socks4_server_reply_encoded_len(const socks4_server_reply_t *obj); +/** Try to encode the socks4_server_reply from 'input' into the buffer + * at 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks4_server_reply_encode(uint8_t *output, size_t avail, const socks4_server_reply_t *input); +/** Check whether the internal state of the socks4_server_reply in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks4_server_reply_check(const socks4_server_reply_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks4_server_reply_clear_errors(socks4_server_reply_t *obj); +/** Return the value of the version field of the socks4_server_reply_t + * in 'inp' + */ +uint8_t socks4_server_reply_get_version(const socks4_server_reply_t *inp); +/** Set the value of the version field of the socks4_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_server_reply_set_version(socks4_server_reply_t *inp, uint8_t val); +/** Return the value of the status field of the socks4_server_reply_t + * in 'inp' + */ +uint8_t socks4_server_reply_get_status(const socks4_server_reply_t *inp); +/** Set the value of the status field of the socks4_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_server_reply_set_status(socks4_server_reply_t *inp, uint8_t val); +/** Return the value of the port field of the socks4_server_reply_t in + * 'inp' + */ +uint16_t socks4_server_reply_get_port(const socks4_server_reply_t *inp); +/** Set the value of the port field of the socks4_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_server_reply_set_port(socks4_server_reply_t *inp, uint16_t val); +/** Return the value of the addr field of the socks4_server_reply_t in + * 'inp' + */ +uint32_t socks4_server_reply_get_addr(const socks4_server_reply_t *inp); +/** Set the value of the addr field of the socks4_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks4_server_reply_set_addr(socks4_server_reply_t *inp, uint32_t val); +/** Return a newly allocated socks5_client_userpass_auth with all + * elements set to zero. + */ +socks5_client_userpass_auth_t *socks5_client_userpass_auth_new(void); +/** Release all storage held by the socks5_client_userpass_auth in + * 'victim'. (Do nothing if 'victim' is NULL.) + */ +void socks5_client_userpass_auth_free(socks5_client_userpass_auth_t *victim); +/** Try to parse a socks5_client_userpass_auth from the buffer in + * 'input', using up to 'len_in' bytes from the input buffer. On + * success, return the number of bytes consumed and set *output to the + * newly allocated socks5_client_userpass_auth_t. On failure, return + * -2 if the input appears truncated, and -1 if the input is otherwise + * invalid. + */ +ssize_t socks5_client_userpass_auth_parse(socks5_client_userpass_auth_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_client_userpass_auth in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t socks5_client_userpass_auth_encoded_len(const socks5_client_userpass_auth_t *obj); +/** Try to encode the socks5_client_userpass_auth from 'input' into + * the buffer at 'output', using up to 'avail' bytes of the output + * buffer. On success, return the number of bytes used. On failure, + * return -2 if the buffer was not long enough, and -1 if the input + * was invalid. + */ +ssize_t socks5_client_userpass_auth_encode(uint8_t *output, size_t avail, const socks5_client_userpass_auth_t *input); +/** Check whether the internal state of the + * socks5_client_userpass_auth in 'obj' is consistent. Return NULL if + * it is, and a short message if it is not. + */ +const char *socks5_client_userpass_auth_check(const socks5_client_userpass_auth_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_client_userpass_auth_clear_errors(socks5_client_userpass_auth_t *obj); +/** Return the value of the version field of the + * socks5_client_userpass_auth_t in 'inp' + */ +uint8_t socks5_client_userpass_auth_get_version(const socks5_client_userpass_auth_t *inp); +/** Set the value of the version field of the + * socks5_client_userpass_auth_t in 'inp' to 'val'. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_userpass_auth_set_version(socks5_client_userpass_auth_t *inp, uint8_t val); +/** Return the value of the username_len field of the + * socks5_client_userpass_auth_t in 'inp' + */ +uint8_t socks5_client_userpass_auth_get_username_len(const socks5_client_userpass_auth_t *inp); +/** Set the value of the username_len field of the + * socks5_client_userpass_auth_t in 'inp' to 'val'. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_userpass_auth_set_username_len(socks5_client_userpass_auth_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the username field + * of the socks5_client_userpass_auth_t in 'inp'. + */ +size_t socks5_client_userpass_auth_getlen_username(const socks5_client_userpass_auth_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * username of the socks5_client_userpass_auth_t in 'inp'. + */ +char socks5_client_userpass_auth_get_username(socks5_client_userpass_auth_t *inp, size_t idx); +/** As socks5_client_userpass_auth_get_username, but take and return a + * const pointer + */ +char socks5_client_userpass_auth_getconst_username(const socks5_client_userpass_auth_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * username of the socks5_client_userpass_auth_t in 'inp', so that it + * will hold the value 'elt'. + */ +int socks5_client_userpass_auth_set_username(socks5_client_userpass_auth_t *inp, size_t idx, char elt); +/** Append a new element 'elt' to the dynamic array field username of + * the socks5_client_userpass_auth_t in 'inp'. + */ +int socks5_client_userpass_auth_add_username(socks5_client_userpass_auth_t *inp, char elt); +/** Return a pointer to the variable-length array field username of + * 'inp'. + */ +char * socks5_client_userpass_auth_getarray_username(socks5_client_userpass_auth_t *inp); +/** As socks5_client_userpass_auth_get_username, but take and return a + * const pointer + */ +const char * socks5_client_userpass_auth_getconstarray_username(const socks5_client_userpass_auth_t *inp); +/** Change the length of the variable-length array field username of + * 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_userpass_auth_setlen_username(socks5_client_userpass_auth_t *inp, size_t newlen); +/** Return the value of the username field of a + * socks5_client_userpass_auth_t as a NUL-terminated string. + */ +const char * socks5_client_userpass_auth_getstr_username(socks5_client_userpass_auth_t *inp); +/** Set the value of the username field of a + * socks5_client_userpass_auth_t to a given string of length 'len'. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int socks5_client_userpass_auth_setstr0_username(socks5_client_userpass_auth_t *inp, const char *val, size_t len); +/** Set the value of the username field of a + * socks5_client_userpass_auth_t to a given NUL-terminated string. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int socks5_client_userpass_auth_setstr_username(socks5_client_userpass_auth_t *inp, const char *val); +/** Return the value of the passwd_len field of the + * socks5_client_userpass_auth_t in 'inp' + */ +uint8_t socks5_client_userpass_auth_get_passwd_len(const socks5_client_userpass_auth_t *inp); +/** Set the value of the passwd_len field of the + * socks5_client_userpass_auth_t in 'inp' to 'val'. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_userpass_auth_set_passwd_len(socks5_client_userpass_auth_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the passwd field of + * the socks5_client_userpass_auth_t in 'inp'. + */ +size_t socks5_client_userpass_auth_getlen_passwd(const socks5_client_userpass_auth_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * passwd of the socks5_client_userpass_auth_t in 'inp'. + */ +char socks5_client_userpass_auth_get_passwd(socks5_client_userpass_auth_t *inp, size_t idx); +/** As socks5_client_userpass_auth_get_passwd, but take and return a + * const pointer + */ +char socks5_client_userpass_auth_getconst_passwd(const socks5_client_userpass_auth_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * passwd of the socks5_client_userpass_auth_t in 'inp', so that it + * will hold the value 'elt'. + */ +int socks5_client_userpass_auth_set_passwd(socks5_client_userpass_auth_t *inp, size_t idx, char elt); +/** Append a new element 'elt' to the dynamic array field passwd of + * the socks5_client_userpass_auth_t in 'inp'. + */ +int socks5_client_userpass_auth_add_passwd(socks5_client_userpass_auth_t *inp, char elt); +/** Return a pointer to the variable-length array field passwd of + * 'inp'. + */ +char * socks5_client_userpass_auth_getarray_passwd(socks5_client_userpass_auth_t *inp); +/** As socks5_client_userpass_auth_get_passwd, but take and return a + * const pointer + */ +const char * socks5_client_userpass_auth_getconstarray_passwd(const socks5_client_userpass_auth_t *inp); +/** Change the length of the variable-length array field passwd of + * 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_userpass_auth_setlen_passwd(socks5_client_userpass_auth_t *inp, size_t newlen); +/** Return the value of the passwd field of a + * socks5_client_userpass_auth_t as a NUL-terminated string. + */ +const char * socks5_client_userpass_auth_getstr_passwd(socks5_client_userpass_auth_t *inp); +/** Set the value of the passwd field of a + * socks5_client_userpass_auth_t to a given string of length 'len'. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int socks5_client_userpass_auth_setstr0_passwd(socks5_client_userpass_auth_t *inp, const char *val, size_t len); +/** Set the value of the passwd field of a + * socks5_client_userpass_auth_t to a given NUL-terminated string. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int socks5_client_userpass_auth_setstr_passwd(socks5_client_userpass_auth_t *inp, const char *val); +/** Return a newly allocated socks5_client_version with all elements + * set to zero. + */ +socks5_client_version_t *socks5_client_version_new(void); +/** Release all storage held by the socks5_client_version in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks5_client_version_free(socks5_client_version_t *victim); +/** Try to parse a socks5_client_version from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks5_client_version_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks5_client_version_parse(socks5_client_version_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_client_version in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t socks5_client_version_encoded_len(const socks5_client_version_t *obj); +/** Try to encode the socks5_client_version from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks5_client_version_encode(uint8_t *output, size_t avail, const socks5_client_version_t *input); +/** Check whether the internal state of the socks5_client_version in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks5_client_version_check(const socks5_client_version_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_client_version_clear_errors(socks5_client_version_t *obj); +/** Return the value of the version field of the + * socks5_client_version_t in 'inp' + */ +uint8_t socks5_client_version_get_version(const socks5_client_version_t *inp); +/** Set the value of the version field of the socks5_client_version_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_client_version_set_version(socks5_client_version_t *inp, uint8_t val); +/** Return the value of the n_methods field of the + * socks5_client_version_t in 'inp' + */ +uint8_t socks5_client_version_get_n_methods(const socks5_client_version_t *inp); +/** Set the value of the n_methods field of the + * socks5_client_version_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_version_set_n_methods(socks5_client_version_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the methods field + * of the socks5_client_version_t in 'inp'. + */ +size_t socks5_client_version_getlen_methods(const socks5_client_version_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * methods of the socks5_client_version_t in 'inp'. + */ +uint8_t socks5_client_version_get_methods(socks5_client_version_t *inp, size_t idx); +/** As socks5_client_version_get_methods, but take and return a const + * pointer + */ +uint8_t socks5_client_version_getconst_methods(const socks5_client_version_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * methods of the socks5_client_version_t in 'inp', so that it will + * hold the value 'elt'. + */ +int socks5_client_version_set_methods(socks5_client_version_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field methods of + * the socks5_client_version_t in 'inp'. + */ +int socks5_client_version_add_methods(socks5_client_version_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field methods of + * 'inp'. + */ +uint8_t * socks5_client_version_getarray_methods(socks5_client_version_t *inp); +/** As socks5_client_version_get_methods, but take and return a const + * pointer + */ +const uint8_t * socks5_client_version_getconstarray_methods(const socks5_client_version_t *inp); +/** Change the length of the variable-length array field methods of + * 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_version_setlen_methods(socks5_client_version_t *inp, size_t newlen); +/** Return a newly allocated socks5_server_method with all elements + * set to zero. + */ +socks5_server_method_t *socks5_server_method_new(void); +/** Release all storage held by the socks5_server_method in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks5_server_method_free(socks5_server_method_t *victim); +/** Try to parse a socks5_server_method from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks5_server_method_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks5_server_method_parse(socks5_server_method_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_server_method in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t socks5_server_method_encoded_len(const socks5_server_method_t *obj); +/** Try to encode the socks5_server_method from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks5_server_method_encode(uint8_t *output, size_t avail, const socks5_server_method_t *input); +/** Check whether the internal state of the socks5_server_method in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks5_server_method_check(const socks5_server_method_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_server_method_clear_errors(socks5_server_method_t *obj); +/** Return the value of the version field of the + * socks5_server_method_t in 'inp' + */ +uint8_t socks5_server_method_get_version(const socks5_server_method_t *inp); +/** Set the value of the version field of the socks5_server_method_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_method_set_version(socks5_server_method_t *inp, uint8_t val); +/** Return the value of the method field of the socks5_server_method_t + * in 'inp' + */ +uint8_t socks5_server_method_get_method(const socks5_server_method_t *inp); +/** Set the value of the method field of the socks5_server_method_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_method_set_method(socks5_server_method_t *inp, uint8_t val); +/** Return a newly allocated socks5_server_userpass_auth with all + * elements set to zero. + */ +socks5_server_userpass_auth_t *socks5_server_userpass_auth_new(void); +/** Release all storage held by the socks5_server_userpass_auth in + * 'victim'. (Do nothing if 'victim' is NULL.) + */ +void socks5_server_userpass_auth_free(socks5_server_userpass_auth_t *victim); +/** Try to parse a socks5_server_userpass_auth from the buffer in + * 'input', using up to 'len_in' bytes from the input buffer. On + * success, return the number of bytes consumed and set *output to the + * newly allocated socks5_server_userpass_auth_t. On failure, return + * -2 if the input appears truncated, and -1 if the input is otherwise + * invalid. + */ +ssize_t socks5_server_userpass_auth_parse(socks5_server_userpass_auth_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_server_userpass_auth in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t socks5_server_userpass_auth_encoded_len(const socks5_server_userpass_auth_t *obj); +/** Try to encode the socks5_server_userpass_auth from 'input' into + * the buffer at 'output', using up to 'avail' bytes of the output + * buffer. On success, return the number of bytes used. On failure, + * return -2 if the buffer was not long enough, and -1 if the input + * was invalid. + */ +ssize_t socks5_server_userpass_auth_encode(uint8_t *output, size_t avail, const socks5_server_userpass_auth_t *input); +/** Check whether the internal state of the + * socks5_server_userpass_auth in 'obj' is consistent. Return NULL if + * it is, and a short message if it is not. + */ +const char *socks5_server_userpass_auth_check(const socks5_server_userpass_auth_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_server_userpass_auth_clear_errors(socks5_server_userpass_auth_t *obj); +/** Return the value of the version field of the + * socks5_server_userpass_auth_t in 'inp' + */ +uint8_t socks5_server_userpass_auth_get_version(const socks5_server_userpass_auth_t *inp); +/** Set the value of the version field of the + * socks5_server_userpass_auth_t in 'inp' to 'val'. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int socks5_server_userpass_auth_set_version(socks5_server_userpass_auth_t *inp, uint8_t val); +/** Return the value of the status field of the + * socks5_server_userpass_auth_t in 'inp' + */ +uint8_t socks5_server_userpass_auth_get_status(const socks5_server_userpass_auth_t *inp); +/** Set the value of the status field of the + * socks5_server_userpass_auth_t in 'inp' to 'val'. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int socks5_server_userpass_auth_set_status(socks5_server_userpass_auth_t *inp, uint8_t val); +/** Return a newly allocated socks5_client_request with all elements + * set to zero. + */ +socks5_client_request_t *socks5_client_request_new(void); +/** Release all storage held by the socks5_client_request in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks5_client_request_free(socks5_client_request_t *victim); +/** Try to parse a socks5_client_request from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks5_client_request_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks5_client_request_parse(socks5_client_request_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_client_request in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t socks5_client_request_encoded_len(const socks5_client_request_t *obj); +/** Try to encode the socks5_client_request from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks5_client_request_encode(uint8_t *output, size_t avail, const socks5_client_request_t *input); +/** Check whether the internal state of the socks5_client_request in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks5_client_request_check(const socks5_client_request_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_client_request_clear_errors(socks5_client_request_t *obj); +/** Return the value of the version field of the + * socks5_client_request_t in 'inp' + */ +uint8_t socks5_client_request_get_version(const socks5_client_request_t *inp); +/** Set the value of the version field of the socks5_client_request_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_client_request_set_version(socks5_client_request_t *inp, uint8_t val); +/** Return the value of the command field of the + * socks5_client_request_t in 'inp' + */ +uint8_t socks5_client_request_get_command(const socks5_client_request_t *inp); +/** Set the value of the command field of the socks5_client_request_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_client_request_set_command(socks5_client_request_t *inp, uint8_t val); +/** Return the value of the reserved field of the + * socks5_client_request_t in 'inp' + */ +uint8_t socks5_client_request_get_reserved(const socks5_client_request_t *inp); +/** Set the value of the reserved field of the socks5_client_request_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_client_request_set_reserved(socks5_client_request_t *inp, uint8_t val); +/** Return the value of the atype field of the socks5_client_request_t + * in 'inp' + */ +uint8_t socks5_client_request_get_atype(const socks5_client_request_t *inp); +/** Set the value of the atype field of the socks5_client_request_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_client_request_set_atype(socks5_client_request_t *inp, uint8_t val); +/** Return the value of the dest_addr_ipv4 field of the + * socks5_client_request_t in 'inp' + */ +uint32_t socks5_client_request_get_dest_addr_ipv4(const socks5_client_request_t *inp); +/** Set the value of the dest_addr_ipv4 field of the + * socks5_client_request_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_request_set_dest_addr_ipv4(socks5_client_request_t *inp, uint32_t val); +/** Return the (constant) length of the array holding the + * dest_addr_ipv6 field of the socks5_client_request_t in 'inp'. + */ +size_t socks5_client_request_getlen_dest_addr_ipv6(const socks5_client_request_t *inp); +/** Return the element at position 'idx' of the fixed array field + * dest_addr_ipv6 of the socks5_client_request_t in 'inp'. + */ +uint8_t socks5_client_request_get_dest_addr_ipv6(socks5_client_request_t *inp, size_t idx); +/** As socks5_client_request_get_dest_addr_ipv6, but take and return a + * const pointer + */ +uint8_t socks5_client_request_getconst_dest_addr_ipv6(const socks5_client_request_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * dest_addr_ipv6 of the socks5_client_request_t in 'inp', so that it + * will hold the value 'elt'. + */ +int socks5_client_request_set_dest_addr_ipv6(socks5_client_request_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 16-element array field dest_addr_ipv6 of + * 'inp'. + */ +uint8_t * socks5_client_request_getarray_dest_addr_ipv6(socks5_client_request_t *inp); +/** As socks5_client_request_get_dest_addr_ipv6, but take and return a + * const pointer + */ +const uint8_t * socks5_client_request_getconstarray_dest_addr_ipv6(const socks5_client_request_t *inp); +/** Return the value of the dest_addr_domainname field of the + * socks5_client_request_t in 'inp' + */ +struct domainname_st * socks5_client_request_get_dest_addr_domainname(socks5_client_request_t *inp); +/** As socks5_client_request_get_dest_addr_domainname, but take and + * return a const pointer + */ +const struct domainname_st * socks5_client_request_getconst_dest_addr_domainname(const socks5_client_request_t *inp); +/** Set the value of the dest_addr_domainname field of the + * socks5_client_request_t in 'inp' to 'val'. Free the old value if + * any. Steals the referenceto 'val'.Return 0 on success; return -1 + * and set the error code on 'inp' on failure. + */ +int socks5_client_request_set_dest_addr_domainname(socks5_client_request_t *inp, struct domainname_st *val); +/** As socks5_client_request_set_dest_addr_domainname, but does not + * free the previous value. + */ +int socks5_client_request_set0_dest_addr_domainname(socks5_client_request_t *inp, struct domainname_st *val); +/** Return the value of the dest_port field of the + * socks5_client_request_t in 'inp' + */ +uint16_t socks5_client_request_get_dest_port(const socks5_client_request_t *inp); +/** Set the value of the dest_port field of the + * socks5_client_request_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_client_request_set_dest_port(socks5_client_request_t *inp, uint16_t val); +/** Return a newly allocated socks5_server_reply with all elements set + * to zero. + */ +socks5_server_reply_t *socks5_server_reply_new(void); +/** Release all storage held by the socks5_server_reply in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void socks5_server_reply_free(socks5_server_reply_t *victim); +/** Try to parse a socks5_server_reply from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated socks5_server_reply_t. On failure, return -2 if the input + * appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t socks5_server_reply_parse(socks5_server_reply_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * socks5_server_reply in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t socks5_server_reply_encoded_len(const socks5_server_reply_t *obj); +/** Try to encode the socks5_server_reply from 'input' into the buffer + * at 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t socks5_server_reply_encode(uint8_t *output, size_t avail, const socks5_server_reply_t *input); +/** Check whether the internal state of the socks5_server_reply in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *socks5_server_reply_check(const socks5_server_reply_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int socks5_server_reply_clear_errors(socks5_server_reply_t *obj); +/** Return the value of the version field of the socks5_server_reply_t + * in 'inp' + */ +uint8_t socks5_server_reply_get_version(const socks5_server_reply_t *inp); +/** Set the value of the version field of the socks5_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_reply_set_version(socks5_server_reply_t *inp, uint8_t val); +/** Return the value of the reply field of the socks5_server_reply_t + * in 'inp' + */ +uint8_t socks5_server_reply_get_reply(const socks5_server_reply_t *inp); +/** Set the value of the reply field of the socks5_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_reply_set_reply(socks5_server_reply_t *inp, uint8_t val); +/** Return the value of the reserved field of the + * socks5_server_reply_t in 'inp' + */ +uint8_t socks5_server_reply_get_reserved(const socks5_server_reply_t *inp); +/** Set the value of the reserved field of the socks5_server_reply_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_reply_set_reserved(socks5_server_reply_t *inp, uint8_t val); +/** Return the value of the atype field of the socks5_server_reply_t + * in 'inp' + */ +uint8_t socks5_server_reply_get_atype(const socks5_server_reply_t *inp); +/** Set the value of the atype field of the socks5_server_reply_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_reply_set_atype(socks5_server_reply_t *inp, uint8_t val); +/** Return the value of the bind_addr_ipv4 field of the + * socks5_server_reply_t in 'inp' + */ +uint32_t socks5_server_reply_get_bind_addr_ipv4(const socks5_server_reply_t *inp); +/** Set the value of the bind_addr_ipv4 field of the + * socks5_server_reply_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int socks5_server_reply_set_bind_addr_ipv4(socks5_server_reply_t *inp, uint32_t val); +/** Return the (constant) length of the array holding the + * bind_addr_ipv6 field of the socks5_server_reply_t in 'inp'. + */ +size_t socks5_server_reply_getlen_bind_addr_ipv6(const socks5_server_reply_t *inp); +/** Return the element at position 'idx' of the fixed array field + * bind_addr_ipv6 of the socks5_server_reply_t in 'inp'. + */ +uint8_t socks5_server_reply_get_bind_addr_ipv6(socks5_server_reply_t *inp, size_t idx); +/** As socks5_server_reply_get_bind_addr_ipv6, but take and return a + * const pointer + */ +uint8_t socks5_server_reply_getconst_bind_addr_ipv6(const socks5_server_reply_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * bind_addr_ipv6 of the socks5_server_reply_t in 'inp', so that it + * will hold the value 'elt'. + */ +int socks5_server_reply_set_bind_addr_ipv6(socks5_server_reply_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 16-element array field bind_addr_ipv6 of + * 'inp'. + */ +uint8_t * socks5_server_reply_getarray_bind_addr_ipv6(socks5_server_reply_t *inp); +/** As socks5_server_reply_get_bind_addr_ipv6, but take and return a + * const pointer + */ +const uint8_t * socks5_server_reply_getconstarray_bind_addr_ipv6(const socks5_server_reply_t *inp); +/** Return the value of the bind_addr_domainname field of the + * socks5_server_reply_t in 'inp' + */ +struct domainname_st * socks5_server_reply_get_bind_addr_domainname(socks5_server_reply_t *inp); +/** As socks5_server_reply_get_bind_addr_domainname, but take and + * return a const pointer + */ +const struct domainname_st * socks5_server_reply_getconst_bind_addr_domainname(const socks5_server_reply_t *inp); +/** Set the value of the bind_addr_domainname field of the + * socks5_server_reply_t in 'inp' to 'val'. Free the old value if any. + * Steals the referenceto 'val'.Return 0 on success; return -1 and set + * the error code on 'inp' on failure. + */ +int socks5_server_reply_set_bind_addr_domainname(socks5_server_reply_t *inp, struct domainname_st *val); +/** As socks5_server_reply_set_bind_addr_domainname, but does not free + * the previous value. + */ +int socks5_server_reply_set0_bind_addr_domainname(socks5_server_reply_t *inp, struct domainname_st *val); +/** Return the value of the bind_port field of the + * socks5_server_reply_t in 'inp' + */ +uint16_t socks5_server_reply_get_bind_port(const socks5_server_reply_t *inp); +/** Set the value of the bind_port field of the socks5_server_reply_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int socks5_server_reply_set_bind_port(socks5_server_reply_t *inp, uint16_t val); + + +#endif diff --git a/src/trunnel/socks5.trunnel b/src/trunnel/socks5.trunnel new file mode 100644 index 0000000000..b86ec03b9d --- /dev/null +++ b/src/trunnel/socks5.trunnel @@ -0,0 +1,94 @@ +// Example: here's a quickie implementation of the messages in the +// socks5 protocol. + +struct socks5_client_version { + u8 version IN [5]; + u8 n_methods; + u8 methods[n_methods]; +} + +struct socks5_server_method { + u8 version IN [5]; + u8 method; +} + +const CMD_CONNECT = 1; +const CMD_BIND = 2; +const CMD_UDP_ASSOCIATE = 3; +// This is a tor extension +const CMD_RESOLVE = 0xF0; +const CMD_RESOLVE_PTR = 0xF1; + +const ATYPE_IPV4 = 1; +const ATYPE_IPV6 = 4; +const ATYPE_DOMAINNAME = 3; + +struct domainname { + u8 len; + char name[len]; +} + +struct socks5_client_request { + u8 version IN [5]; + u8 command IN [CMD_CONNECT, CMD_BIND, CMD_UDP_ASSOCIATE, CMD_RESOLVE, CMD_RESOLVE_PTR]; + u8 reserved IN [0]; + u8 atype; + union dest_addr[atype] { + ATYPE_IPV4: u32 ipv4; + ATYPE_IPV6: u8 ipv6[16]; + ATYPE_DOMAINNAME: struct domainname domainname; + default: fail; + }; + u16 dest_port; +} + +struct socks5_server_reply { + u8 version IN [5]; + u8 reply; + u8 reserved IN [0]; + u8 atype; + union bind_addr[atype] { + ATYPE_IPV4: u32 ipv4; + ATYPE_IPV6: u8 ipv6[16]; + ATYPE_DOMAINNAME: struct domainname domainname; + default: fail; + }; + u16 bind_port; +} + +struct socks5_client_userpass_auth { + u8 version IN [1]; + u8 username_len; + char username[username_len]; + u8 passwd_len; + char passwd[passwd_len]; +} + +struct socks5_server_userpass_auth { + u8 version IN [1]; + u8 status; +} + +// Oh why not. Here's socks4 and socks4a. + +struct socks4_client_request { + u8 version IN [4]; + u8 command IN [CMD_CONNECT,CMD_BIND,CMD_RESOLVE,CMD_RESOLVE_PTR]; + u16 port; + u32 addr; + nulterm username; + union socks4a_addr[addr] { + 1..255: + nulterm hostname; + default: + ; + }; +} + +struct socks4_server_reply { + u8 version IN [4]; + u8 status; + u16 port; + u32 addr; +} + |