diff options
Diffstat (limited to 'src')
31 files changed, 1022 insertions, 460 deletions
diff --git a/src/common/address.c b/src/common/address.c index e88869f1d8..dffbcaff44 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -1697,3 +1697,15 @@ tor_addr_hostname_is_local(const char *name) !strcasecmpend(name, ".local"); } +/** Return a newly allocated tor_addr_port_t with <b>addr</b> and + <b>port</b> filled in. */ +tor_addr_port_t * +tor_addr_port_new(const tor_addr_t *addr, uint16_t port) +{ + tor_addr_port_t *ap = tor_malloc_zero(sizeof(tor_addr_port_t)); + if (addr) + tor_addr_copy(&ap->addr, addr); + ap->port = port; + return ap; +} + diff --git a/src/common/address.h b/src/common/address.h index c6c126862a..7a779d8880 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -40,7 +40,7 @@ typedef struct tor_addr_port_t uint16_t port; } tor_addr_port_t; -#define TOR_ADDR_NULL {AF_UNSPEC, {0}}; +#define TOR_ADDR_NULL {AF_UNSPEC, {0}} static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); @@ -221,5 +221,7 @@ int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len); char *tor_dup_ip(uint32_t addr) ATTR_MALLOC; int get_interface_address(int severity, uint32_t *addr); +tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); + #endif diff --git a/src/common/include.am b/src/common/include.am index 0ab4769847..299c92e065 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -36,7 +36,7 @@ src_common_libor_crypto_a_SOURCES = \ src_common_libor_event_a_SOURCES = src/common/compat_libevent.c -noinst_HEADERS+= \ +COMMONHEADERS = \ src/common/address.h \ src/common/aes.h \ src/common/ciphers.inc \ @@ -57,17 +57,19 @@ noinst_HEADERS+= \ src/common/tortls.h \ src/common/util.h +noinst_HEADERS+= $(COMMONHEADERS) + DISTCLEANFILES+= src/common/common_sha1.i -src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS) +src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(COMMONHEADERS) $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \ + (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \ elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(noinst_HEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \ + (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \ else \ rm $@; \ touch $@; \ fi src/common/util_codedigest.o: src/common/common_sha1.i -src/common/crypto.c: src/common/sha256.c +src/common/crypto.o: src/common/sha256.c diff --git a/src/common/util.c b/src/common/util.c index ce67252852..3efc25378d 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3897,10 +3897,15 @@ tor_process_handle_destroy(process_handle_t *process_handle, if (also_terminate_process) { if (tor_terminate_process(process_handle) < 0) { - log_notice(LD_GENERAL, "Failed to terminate process with PID '%d'", - tor_process_get_pid(process_handle)); + log_notice(LD_GENERAL, "Failed to terminate process with " + "PID '%d' ('%s').", tor_process_get_pid(process_handle), +#ifdef _WIN32 + format_win32_error(GetLastError())); +#else + strerror(errno)); +#endif } else { - log_info(LD_GENERAL, "Terminated process with PID '%d'", + log_info(LD_GENERAL, "Terminated process with PID '%d'.", tor_process_get_pid(process_handle)); } } @@ -4369,6 +4374,50 @@ tor_split_lines(smartlist_t *sl, char *buf, int len) } #ifdef _WIN32 + +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +smartlist_t * +tor_get_lines_from_handle(HANDLE *handle, + enum stream_status *stream_status_out) +{ + int pos; + char stdout_buf[600] = {0}; + smartlist_t *lines = NULL; + + tor_assert(stream_status_out); + + *stream_status_out = IO_STREAM_TERM; + + pos = tor_read_all_handle(handle, stdout_buf, sizeof(stdout_buf) - 1, NULL); + if (pos < 0) { + *stream_status_out = IO_STREAM_TERM; + return NULL; + } + if (pos == 0) { + *stream_status_out = IO_STREAM_EAGAIN; + return NULL; + } + + /* End with a null even if there isn't a \r\n at the end */ + /* TODO: What if this is a partial line? */ + stdout_buf[pos] = '\0'; + + /* Split up the buffer */ + lines = smartlist_new(); + tor_split_lines(lines, stdout_buf, pos); + + /* Currently 'lines' is populated with strings residing on the + stack. Replace them with their exact copies on the heap: */ + SMARTLIST_FOREACH(lines, char *, line, + SMARTLIST_REPLACE_CURRENT(lines, line, tor_strdup(line))); + + *stream_status_out = IO_STREAM_OKAY; + + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns -1 if there is a error reading, and 0 otherwise. * If the generated stream is flushed more often than on new lines, or @@ -4416,6 +4465,33 @@ log_from_handle(HANDLE *pipe, int severity) #else +/** Return a smartlist containing lines outputted from + * <b>handle</b>. Return NULL on error, and set + * <b>stream_status_out</b> appropriately. */ +smartlist_t * +tor_get_lines_from_handle(FILE *handle, enum stream_status *stream_status_out) +{ + enum stream_status stream_status; + char stdout_buf[400]; + smartlist_t *lines = NULL; + + while (1) { + memset(stdout_buf, 0, sizeof(stdout_buf)); + + stream_status = get_string_from_pipe(handle, + stdout_buf, sizeof(stdout_buf) - 1); + if (stream_status != IO_STREAM_OKAY) + goto done; + + if (!lines) lines = smartlist_new(); + smartlist_add(lines, tor_strdup(stdout_buf)); + } + + done: + *stream_status_out = stream_status; + return lines; +} + /** Read from stream, and send lines to log at the specified log level. * Returns 1 if stream is closed normally, -1 if there is a error reading, and * 0 otherwise. Handles lines from tor-fw-helper and @@ -4534,9 +4610,130 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count) return IO_STREAM_TERM; } -/* DOCDOC tor_check_port_forwarding */ +/** Parse a <b>line</b> from tor-fw-helper and issue an appropriate + * log message to our user. */ +static void +handle_fw_helper_line(const char *line) +{ + smartlist_t *tokens = smartlist_new(); + char *message = NULL; + char *message_for_log = NULL; + const char *external_port = NULL; + const char *internal_port = NULL; + const char *result = NULL; + int port = 0; + int success = 0; + + smartlist_split_string(tokens, line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1); + + if (smartlist_len(tokens) < 5) + goto err; + + if (strcmp(smartlist_get(tokens, 0), "tor-fw-helper") || + strcmp(smartlist_get(tokens, 1), "tcp-forward")) + goto err; + + external_port = smartlist_get(tokens, 2); + internal_port = smartlist_get(tokens, 3); + result = smartlist_get(tokens, 4); + + if (smartlist_len(tokens) > 5) { + /* If there are more than 5 tokens, they are part of [<message>]. + Let's use a second smartlist to form the whole message; + strncat loops suck. */ + int i; + int message_words_n = smartlist_len(tokens) - 5; + smartlist_t *message_sl = smartlist_new(); + for (i = 0; i < message_words_n; i++) + smartlist_add(message_sl, smartlist_get(tokens, 5+i)); + + tor_assert(smartlist_len(message_sl) > 0); + message = smartlist_join_strings(message_sl, " ", 0, NULL); + + /* wrap the message in log-friendly wrapping */ + tor_asprintf(&message_for_log, " ('%s')", message); + + smartlist_free(message_sl); + } + + port = atoi(external_port); + if (port < 1 || port > 65535) + goto err; + + port = atoi(internal_port); + if (port < 1 || port > 65535) + goto err; + + if (!strcmp(result, "SUCCESS")) + success = 1; + else if (!strcmp(result, "FAIL")) + success = 0; + else + goto err; + + if (!success) { + log_warn(LD_GENERAL, "Tor was unable to forward TCP port '%s' to '%s'%s. " + "Please make sure that your router supports port " + "forwarding protocols (like NAT-PMP). Note that if '%s' is " + "your ORPort, your relay will be unable to receive inbound " + "traffic.", external_port, internal_port, + message_for_log ? message_for_log : "", + internal_port); + } else { + log_info(LD_GENERAL, + "Tor successfully forwarded TCP port '%s' to '%s'%s.", + external_port, internal_port, + message_for_log ? message_for_log : ""); + } + + goto done; + + err: + log_warn(LD_GENERAL, "tor-fw-helper sent us a string we could not " + "parse (%s).", line); + + done: + SMARTLIST_FOREACH(tokens, char *, cp, tor_free(cp)); + smartlist_free(tokens); + tor_free(message); + tor_free(message_for_log); +} + +/** Read what tor-fw-helper has to say in its stdout and handle it + * appropriately */ +static int +handle_fw_helper_output(process_handle_t *process_handle) +{ + smartlist_t *fw_helper_output = NULL; + enum stream_status stream_status = 0; + + fw_helper_output = + tor_get_lines_from_handle(tor_process_get_stdout_pipe(process_handle), + &stream_status); + if (!fw_helper_output) { /* didn't get any output from tor-fw-helper */ + /* if EAGAIN we should retry in the future */ + return (stream_status == IO_STREAM_EAGAIN) ? 0 : -1; + } + + /* Handle the lines we got: */ + SMARTLIST_FOREACH_BEGIN(fw_helper_output, char *, line) { + handle_fw_helper_line(line); + tor_free(line); + } SMARTLIST_FOREACH_END(line); + + smartlist_free(fw_helper_output); + + return 0; +} + +/** Spawn tor-fw-helper and ask it to forward the ports in + * <b>ports_to_forward</b>. <b>ports_to_forward</b> contains strings + * of the form "<external port>:<internal port>", which is the format + * that tor-fw-helper expects. */ void -tor_check_port_forwarding(const char *filename, int dir_port, int or_port, +tor_check_port_forwarding(const char *filename, + smartlist_t *ports_to_forward, time_t now) { /* When fw-helper succeeds, how long do we wait until running it again */ @@ -4550,32 +4747,51 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, static process_handle_t *child_handle=NULL; static time_t time_to_run_helper = 0; - int stdout_status, stderr_status, retval; - const char *argv[10]; - char s_dirport[6], s_orport[6]; + int stderr_status, retval; + int stdout_status = 0; tor_assert(filename); - /* Set up command line for tor-fw-helper */ - snprintf(s_dirport, sizeof s_dirport, "%d", dir_port); - snprintf(s_orport, sizeof s_orport, "%d", or_port); - - /* TODO: Allow different internal and external ports */ - argv[0] = filename; - argv[1] = "--internal-or-port"; - argv[2] = s_orport; - argv[3] = "--external-or-port"; - argv[4] = s_orport; - argv[5] = "--internal-dir-port"; - argv[6] = s_dirport; - argv[7] = "--external-dir-port"; - argv[8] = s_dirport; - argv[9] = NULL; - /* Start the child, if it is not already running */ if ((!child_handle || child_handle->status != PROCESS_STATUS_RUNNING) && time_to_run_helper < now) { - int status; + /*tor-fw-helper cli looks like this: tor_fw_helper -p :5555 -p 4555:1111 */ + const char **argv; /* cli arguments */ + int args_n, status; + int argv_index = 0; /* index inside 'argv' */ + + tor_assert(smartlist_len(ports_to_forward) > 0); + + /* check for overflow during 'argv' allocation: + (len(ports_to_forward)*2 + 2)*sizeof(char*) > SIZE_MAX == + len(ports_to_forward) > (((SIZE_MAX/sizeof(char*)) - 2)/2) */ + if ((size_t) smartlist_len(ports_to_forward) > + (((SIZE_MAX/sizeof(char*)) - 2)/2)) { + log_warn(LD_GENERAL, + "Overflow during argv allocation. This shouldn't happen."); + return; + } + /* check for overflow during 'argv_index' increase: + ((len(ports_to_forward)*2 + 2) > INT_MAX) == + len(ports_to_forward) > (INT_MAX - 2)/2 */ + if (smartlist_len(ports_to_forward) > (INT_MAX - 2)/2) { + log_warn(LD_GENERAL, + "Overflow during argv_index increase. This shouldn't happen."); + return; + } + + /* Calculate number of cli arguments: one for the filename, two + for each smartlist element (one for "-p" and one for the + ports), and one for the final NULL. */ + args_n = 1 + 2*smartlist_len(ports_to_forward) + 1; + argv = tor_malloc_zero(sizeof(char*)*args_n); + + argv[argv_index++] = filename; + SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) { + argv[argv_index++] = "-p"; + argv[argv_index++] = port; + } SMARTLIST_FOREACH_END(port); + argv[argv_index] = NULL; /* Assume tor-fw-helper will succeed, start it later*/ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS; @@ -4592,6 +4808,8 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, status = tor_spawn_background(filename, argv, NULL, &child_handle); #endif + tor_free(argv); + if (PROCESS_STATUS_ERROR == status) { log_warn(LD_GENERAL, "Failed to start port forwarding helper %s", filename); @@ -4609,16 +4827,17 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port, /* Read from stdout/stderr and log result */ retval = 0; #ifdef _WIN32 - stdout_status = log_from_handle(child_handle->stdout_pipe, LOG_INFO); - stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_WARN); - /* If we got this far (on Windows), the process started */ - retval = 0; + stderr_status = log_from_handle(child_handle->stderr_pipe, LOG_INFO); #else - stdout_status = log_from_pipe(child_handle->stdout_handle, - LOG_INFO, filename, &retval); stderr_status = log_from_pipe(child_handle->stderr_handle, - LOG_WARN, filename, &retval); + LOG_INFO, filename, &retval); #endif + if (handle_fw_helper_output(child_handle) < 0) { + log_warn(LD_GENERAL, "Failed to handle fw helper output."); + stdout_status = -1; + retval = -1; + } + if (retval) { /* There was a problem in the child process */ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL; diff --git a/src/common/util.h b/src/common/util.h index c059730db6..a6944ac894 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -373,7 +373,8 @@ void write_pidfile(char *filename); /* Port forwarding */ void tor_check_port_forwarding(const char *filename, - int dir_port, int or_port, time_t now); + struct smartlist_t *ports_to_forward, + time_t now); typedef struct process_handle_t process_handle_t; typedef struct process_environment_t process_environment_t; @@ -464,6 +465,16 @@ HANDLE tor_process_get_stdout_pipe(process_handle_t *process_handle); FILE *tor_process_get_stdout_pipe(process_handle_t *process_handle); #endif +#ifdef _WIN32 +struct smartlist_t * +tor_get_lines_from_handle(HANDLE *handle, + enum stream_status *stream_status); +#else +struct smartlist_t * +tor_get_lines_from_handle(FILE *handle, + enum stream_status *stream_status); +#endif + int tor_terminate_process(process_handle_t *process_handle); void tor_process_handle_destroy(process_handle_t *process_handle, int also_terminate_process); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index ba617ffa45..87a3fa67b1 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -3792,12 +3792,10 @@ onion_extend_cpath(origin_circuit_t *circ) } else if (cur_len == 0) { /* picking first node */ const node_t *r = choose_good_entry_server(purpose, state); if (r) { - /* If we're extending to a bridge, use the preferred address - rather than the primary, for potentially extending to an IPv6 - bridge. */ - int use_pref_addr = (r->ri != NULL && - r->ri->purpose == ROUTER_PURPOSE_BRIDGE); - info = extend_info_from_node(r, use_pref_addr); + /* If we're a client, use the preferred address rather than the + primary address, for potentially connecting to an IPv6 OR + port. */ + info = extend_info_from_node(r, server_mode(get_options()) == 0); tor_assert(info); } } else { @@ -3865,34 +3863,43 @@ extend_info_alloc(const char *nickname, const char *digest, /** Allocate and return a new extend_info that can be used to build a * circuit to or through the node <b>node</b>. Use the primary address - * of the node unless <b>for_direct_connect</b> is true, in which case - * the preferred address is used instead. May return NULL if there is - * not enough info about <b>node</b> to extend to it--for example, if - * there is no routerinfo_t or microdesc_t. + * of the node (i.e. its IPv4 address) unless + * <b>for_direct_connect</b> is true, in which case the preferred + * address is used instead. May return NULL if there is not enough + * info about <b>node</b> to extend to it--for example, if there is no + * routerinfo_t or microdesc_t. **/ extend_info_t * extend_info_from_node(const node_t *node, int for_direct_connect) { - if (node->ri) { - const routerinfo_t *r = node->ri; - tor_addr_port_t ap; - if (for_direct_connect) - node_get_pref_orport(node, &ap); - else - node_get_prim_orport(node, &ap); - return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &ap.addr, ap.port); - } else if (node->rs && node->md) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); + tor_addr_port_t ap; + + if (node->ri == NULL && (node->rs == NULL || node->md == NULL)) + return NULL; + + if (for_direct_connect) + node_get_pref_orport(node, &ap); + else + node_get_prim_orport(node, &ap); + + log_debug(LD_CIRC, "using %s:%d for %s", + fmt_and_decorate_addr(&ap.addr), ap.port, + node->ri ? node->ri->nickname : node->rs->nickname); + + if (node->ri) + return extend_info_alloc(node->ri->nickname, + node->identity, + node->ri->onion_pkey, + &ap.addr, + ap.port); + else if (node->rs && node->md) return extend_info_alloc(node->rs->nickname, node->identity, node->md->onion_pkey, - &addr, - node->rs->or_port); - } else { + &ap.addr, + ap.port); + else return NULL; - } } /** Release storage held by an extend_info_t struct. */ @@ -4897,6 +4904,12 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) const or_options_t *options = get_options(); unsigned hop_cnt, success_cnt; + if (!node) { + *msg = tor_strdup("Unable to parse entry nodes: " + "EntryGuardPathBias without EntryGuard"); + break; + } + if (tor_sscanf(line->value, "%u %u", &success_cnt, &hop_cnt) != 2) { log_warn(LD_GENERAL, "Unable to parse guard path bias info: " "Misformated EntryGuardPathBias %s", escaped(line->value)); @@ -5607,10 +5620,15 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } } - /* Indicate that we prefer connecting to this bridge over the - protocol that the bridge address indicates. Last bridge - descriptor handled wins. */ - node->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + /* Mark bridge as preferably connected to over IPv6 if its IPv6 + address is in a Bridge line and ClientPreferIPv6ORPort is + set. Unless both is true, a potential IPv6 OR port of this + bridge won't get selected. + + XXX ipv6_preferred is never reset (#6757) */ + if (get_options()->ClientPreferIPv6ORPort == 1 && + tor_addr_family(&bridge->addr) == AF_INET6) + node->ipv6_preferred = 1; /* XXXipv6 we lack support for falling back to another address for the same relay, warn the user */ diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index b3ebf094a8..7083db12fb 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -137,6 +137,10 @@ const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, struct transport_t; int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port, const struct transport_t **transport); +const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr, + uint16_t port); + +int validate_pluggable_transports_config(void); #endif diff --git a/src/or/circuituse.c b/src/or/circuituse.c index c34b698fa1..20f124eb4e 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -372,7 +372,7 @@ circuit_expire_building(void) } while (0) SET_CUTOFF(general_cutoff, circ_times.timeout_ms); - SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms / 2.0); + SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms); SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (4/3.0)); SET_CUTOFF(cannibalize_cutoff, circ_times.timeout_ms / 2.0); SET_CUTOFF(close_cutoff, circ_times.close_ms); diff --git a/src/or/config.c b/src/or/config.c index a5a8fec507..c6a4fe4303 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -204,8 +204,7 @@ static config_var_t _option_vars[] = { V(AuthDirListBadExits, BOOL, "0"), V(AuthDirMaxServersPerAddr, UINT, "2"), V(AuthDirMaxServersPerAuthAddr,UINT, "5"), - V(AuthDirHasIPv6Connectivity, AUTOBOOL, "auto"), - V(AuthDirPublishIPv6, BOOL, "0"), + V(AuthDirHasIPv6Connectivity, BOOL, "0"), VAR("AuthoritativeDirectory", BOOL, AuthoritativeDir, "0"), V(AutomapHostsOnResolve, BOOL, "0"), V(AutomapHostsSuffixes, CSV, ".onion,.exit"), @@ -225,8 +224,10 @@ static config_var_t _option_vars[] = { V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), + V(ClientPreferIPv6ORPort, BOOL, "0"), V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientTransportPlugin, LINELIST, NULL), + V(ClientUseIPv6, BOOL, "0"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), @@ -7252,6 +7253,43 @@ remove_file_if_very_old(const char *fname, time_t now) } } +/** Return a smartlist of ports that must be forwarded by + * tor-fw-helper. The smartlist contains the ports in a string format + * that is understandable by tor-fw-helper. */ +smartlist_t * +get_list_of_ports_to_forward(void) +{ + smartlist_t *ports_to_forward = smartlist_new(); + int port = 0; + + /** XXX TODO tor-fw-helper does not support forwarding ports to + other hosts than the local one. If the user is binding to a + different IP address, tor-fw-helper won't work. */ + port = router_get_advertised_or_port(get_options()); /* Get ORPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + port = router_get_advertised_dir_port(get_options(), 0); /* Get DirPort */ + if (port) + smartlist_add_asprintf(ports_to_forward, "%d:%d", port, port); + + /* Get ports of transport proxies */ + { + smartlist_t *transport_ports = get_transport_proxy_ports(); + if (transport_ports) { + smartlist_add_all(ports_to_forward, transport_ports); + smartlist_free(transport_ports); + } + } + + if (!smartlist_len(ports_to_forward)) { + smartlist_free(ports_to_forward); + ports_to_forward = NULL; + } + + return ports_to_forward; +} + /** Helper to implement GETINFO functions about configuration variables (not * their values). Given a "config/names" question, set *<b>answer</b> to a * new string describing the supported configuration variables and their diff --git a/src/or/config.h b/src/or/config.h index dd76edcf1d..d207965849 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -82,6 +82,8 @@ void save_transport_to_state(const char *transport_name, const tor_addr_t *addr, uint16_t port); char *get_stored_bindaddr_for_server_transport(const char *transport); +smartlist_t *get_list_of_ports_to_forward(void); + int getinfo_helper_config(control_connection_t *conn, const char *question, char **answer, const char **errmsg); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index da27cba32d..5a3b6205a9 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -327,7 +327,7 @@ var_cell_t * var_cell_new(uint16_t payload_len) { size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len; - var_cell_t *cell = tor_malloc(size); + var_cell_t *cell = tor_malloc_zero(size); cell->payload_len = payload_len; cell->command = 0; cell->circ_id = 0; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index f4ba02b4ad..7f4690678e 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -62,6 +62,16 @@ static cached_dir_t *the_directory = NULL; /** For authoritative directories: the current (v1) network status. */ static cached_dir_t the_runningrouters; +/** Array of start and end of consensus methods used for supported + microdescriptor formats. */ +static const struct consensus_method_range_t { + int low; + int high; +} microdesc_consensus_methods[] = { + {MIN_METHOD_FOR_MICRODESC, MIN_METHOD_FOR_A_LINES - 1}, + {MIN_METHOD_FOR_A_LINES, MAX_SUPPORTED_CONSENSUS_METHOD}, + {-1, -1}}; + static void directory_remove_invalid(void); static cached_dir_t *dirserv_regenerate_directory(void); static char *format_versions_list(config_line_t *ln); @@ -717,7 +727,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.", ri->nickname, source, (int)ri->cache_info.signed_descriptor_len, MAX_DESCRIPTOR_UPLOAD_SIZE); - *msg = "Router descriptor was too large"; + *msg = "Router descriptor was too large."; control_event_or_authdir_new_descriptor("REJECTED", ri->cache_info.signed_descriptor_body, ri->cache_info.signed_descriptor_len, *msg); @@ -1007,7 +1017,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) IPv6 OR port since that'd kill all dual stack relays until a majority of the dir auths have IPv6 connectivity. */ answer = (now < node->last_reachable + REACHABLE_TIMEOUT && - (options->AuthDirHasIPv6Connectivity == 0 || + (options->AuthDirHasIPv6Connectivity != 1 || tor_addr_is_null(&router->ipv6_addr) || now < node->last_reachable6 + REACHABLE_TIMEOUT)); } @@ -2053,7 +2063,7 @@ version_from_platform(const char *platform) * non-NULL, add a "v" line for the platform. Return 0 on success, -1 on * failure. * - * The format argument has three possible values: + * The format argument has one of the following values: * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc @@ -2092,17 +2102,18 @@ routerstatus_format_entry(char *buf, size_t buf_len, log_warn(LD_BUG, "Not enough space in buffer."); return -1; } + cp = buf + strlen(buf); /* TODO: Maybe we want to pass in what we need to build the rest of * this here, instead of in the caller. Then we could use the * networkstatus_type_t values, with an additional control port value * added -MP */ - if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) - return 0; - cp = buf + strlen(buf); + /* V3 microdesc consensuses don't have "a" lines. */ + if (format == NS_V3_CONSENSUS_MICRODESC) + return 0; - /* Possible "a" line, not included in consensus for now. */ + /* Possible "a" line. At most one for now. */ if (!tor_addr_is_null(&rs->ipv6_addr)) { const char *addr_str = fmt_and_decorate_addr(&rs->ipv6_addr); r = tor_snprintf(cp, buf_len - (cp-buf), @@ -2116,6 +2127,9 @@ routerstatus_format_entry(char *buf, size_t buf_len, cp += strlen(cp); } + if (format == NS_V3_CONSENSUS) + return 0; + /* NOTE: Whenever this list expands, be sure to increase MAX_FLAG_LINE_LEN*/ r = tor_snprintf(cp, buf_len - (cp-buf), "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", @@ -2481,13 +2495,11 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); rs->or_port = ri->or_port; rs->dir_port = ri->dir_port; - if (options->AuthDirPublishIPv6 == 1 && + if (options->AuthDirHasIPv6Connectivity == 1 && !tor_addr_is_null(&ri->ipv6_addr) && - (options->AuthDirHasIPv6Connectivity == 0 || - node->last_reachable6 >= now - REACHABLE_TIMEOUT)) { - /* We're configured for publishing IPv6 OR ports. There's an IPv6 - OR port and it's reachable (or we know that we're not on IPv6) - so copy it to the routerstatus. */ + node->last_reachable6 >= now - REACHABLE_TIMEOUT) { + /* We're configured as having IPv6 connectivity. There's an IPv6 + OR port and it's reachable so copy it to the routerstatus. */ tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); rs->ipv6_orport = ri->ipv6_orport; } @@ -2753,6 +2765,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, microdescriptors = smartlist_new(); SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + const struct consensus_method_range_t *cmr = NULL; if (ri->cache_info.published_on >= cutoff) { routerstatus_t *rs; vote_routerstatus_t *vrs; @@ -2774,15 +2787,20 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, rs->is_flagged_running = 0; vrs->version = version_from_platform(ri->platform); - md = dirvote_create_microdescriptor(ri); - if (md) { - char buf[128]; - vote_microdesc_hash_t *h; - dirvote_format_microdesc_vote_line(buf, sizeof(buf), md); - h = tor_malloc(sizeof(vote_microdesc_hash_t)); - h->microdesc_hash_line = tor_strdup(buf); - h->next = NULL; - vrs->microdesc = h; + for (cmr = microdesc_consensus_methods; + cmr->low != -1 && cmr->high != -1; + cmr++) { + md = dirvote_create_microdescriptor(ri, cmr->low); + if (md) { + char buf[128]; + vote_microdesc_hash_t *h; + dirvote_format_microdesc_vote_line(buf, sizeof(buf), md, + cmr->low, cmr->high); + h = tor_malloc_zero(sizeof(vote_microdesc_hash_t)); + h->microdesc_hash_line = tor_strdup(buf); + h->next = vrs->microdesc; + vrs->microdesc = h; + } md->last_listed = now; smartlist_add(microdescriptors, md); } diff --git a/src/or/dirvote.c b/src/or/dirvote.c index b3de90b5c0..ea85636b3b 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -53,29 +53,6 @@ static int dirvote_compute_consensuses(void); static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high, const char *sep); -/** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 13 - -/** Lowest consensus method that contains a 'directory-footer' marker */ -#define MIN_METHOD_FOR_FOOTER 9 - -/** Lowest consensus method that contains bandwidth weights */ -#define MIN_METHOD_FOR_BW_WEIGHTS 9 - -/** Lowest consensus method that contains consensus params */ -#define MIN_METHOD_FOR_PARAMS 7 - -/** Lowest consensus method that generates microdescriptors */ -#define MIN_METHOD_FOR_MICRODESC 8 - -/** Lowest consensus method that ensures a majority of authorities voted - * for a param. */ -#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 - -/** Lowest consensus method where microdesc consensuses omit any entry - * with no microdesc. */ -#define MIN_METHOD_FOR_MANDATORY_MICRODESC 13 - /* ===== * Voting * =====*/ @@ -430,6 +407,21 @@ _compare_vote_rs(const void **_a, const void **_b) return compare_vote_rs(a,b); } +/** Helper for sorting OR ports. */ +static int +_compare_orports(const void **_a, const void **_b) +{ + const tor_addr_port_t *a = *_a, *b = *_b; + int r; + + if ((r = tor_addr_compare(&a->addr, &b->addr, CMP_EXACT))) + return r; + if ((r = (((int) b->port) - ((int) a->port)))) + return r; + + return 0; +} + /** Given a list of vote_routerstatus_t, all for the same router identity, * return whichever is most frequent, breaking ties in favor of more * recently published vote_routerstatus_t and in case of ties there, @@ -437,7 +429,8 @@ _compare_vote_rs(const void **_a, const void **_b) */ static vote_routerstatus_t * compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, - char *microdesc_digest256_out) + char *microdesc_digest256_out, + tor_addr_port_t *best_alt_orport_out) { vote_routerstatus_t *most = NULL, *cur = NULL; int most_n = 0, cur_n = 0; @@ -473,6 +466,39 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, tor_assert(most); + /* If we're producing "a" lines, vote on potential alternative (sets + * of) OR port(s) in the winning routerstatuses. + * + * XXX prop186 There's at most one alternative OR port (_the_ IPv6 + * port) for now. */ + if (consensus_method >= MIN_METHOD_FOR_A_LINES && best_alt_orport_out) { + smartlist_t *alt_orports = smartlist_new(); + const tor_addr_port_t *most_alt_orport = NULL; + + SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { + if (compare_vote_rs(most, rs) == 0 && + !tor_addr_is_null(&rs->status.ipv6_addr) + && rs->status.ipv6_orport) { + smartlist_add(alt_orports, tor_addr_port_new(&rs->status.ipv6_addr, + rs->status.ipv6_orport)); + } + } SMARTLIST_FOREACH_END(rs); + + smartlist_sort(alt_orports, _compare_orports); + most_alt_orport = smartlist_get_most_frequent(alt_orports, + _compare_orports); + if (most_alt_orport) { + memcpy(best_alt_orport_out, most_alt_orport, sizeof(tor_addr_port_t)); + log_debug(LD_DIR, "\"a\" line winner for %s is %s:%d", + most->status.nickname, + fmt_and_decorate_addr(&most_alt_orport->addr), + most_alt_orport->port); + } + + SMARTLIST_FOREACH(alt_orports, tor_addr_port_t *, ap, tor_free(ap)); + smartlist_free(alt_orports); + } + if (consensus_method >= MIN_METHOD_FOR_MICRODESC && microdesc_digest256_out) { smartlist_t *digests = smartlist_new(); @@ -1685,6 +1711,7 @@ networkstatus_compute_consensus(smartlist_t *votes, int n_listing = 0; int i; char microdesc_digest[DIGEST256_LEN]; + tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0}; /* Of the next-to-be-considered digest in each voter, which is first? */ SMARTLIST_FOREACH(votes, networkstatus_t *, v, { @@ -1754,7 +1781,7 @@ networkstatus_compute_consensus(smartlist_t *votes, * routerinfo and its contents are. */ memset(microdesc_digest, 0, sizeof(microdesc_digest)); rs = compute_routerstatus_consensus(matching_descs, consensus_method, - microdesc_digest); + microdesc_digest, &alt_orport); /* Copy bits of that into rs_out. */ memset(&rs_out, 0, sizeof(rs_out)); tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN)); @@ -1765,6 +1792,10 @@ networkstatus_compute_consensus(smartlist_t *votes, rs_out.published_on = rs->status.published_on; rs_out.dir_port = rs->status.dir_port; rs_out.or_port = rs->status.or_port; + if (consensus_method >= MIN_METHOD_FOR_A_LINES) { + tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr); + rs_out.ipv6_orport = alt_orport.port; + } rs_out.has_bandwidth = 0; rs_out.has_exitsummary = 0; @@ -3510,7 +3541,7 @@ dirvote_get_vote(const char *fp, int flags) * particular method. **/ microdesc_t * -dirvote_create_microdescriptor(const routerinfo_t *ri) +dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) { microdesc_t *result = NULL; char *key = NULL, *summary = NULL, *family = NULL; @@ -3526,6 +3557,12 @@ dirvote_create_microdescriptor(const routerinfo_t *ri) smartlist_add_asprintf(chunks, "onion-key\n%s", key); + if (consensus_method >= MIN_METHOD_FOR_A_LINES && + !tor_addr_is_null(&ri->ipv6_addr) && ri->ipv6_orport) + smartlist_add_asprintf(chunks, "a %s:%d\n", + fmt_and_decorate_addr(&ri->ipv6_addr), + ri->ipv6_orport); + if (family) smartlist_add_asprintf(chunks, "family %s\n", family); @@ -3559,33 +3596,36 @@ dirvote_create_microdescriptor(const routerinfo_t *ri) return result; } -/** Cached space-separated string to hold */ -static char *microdesc_consensus_methods = NULL; - /** Format the appropriate vote line to describe the microdescriptor <b>md</b> * in a consensus vote document. Write it into the <b>out_len</b>-byte buffer * in <b>out</b>. Return -1 on failure and the number of characters written * on success. */ ssize_t -dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md) +dirvote_format_microdesc_vote_line(char *out_buf, size_t out_buf_len, + const microdesc_t *md, + int consensus_method_low, + int consensus_method_high) { + ssize_t ret = -1; char d64[BASE64_DIGEST256_LEN+1]; - if (!microdesc_consensus_methods) { - microdesc_consensus_methods = - make_consensus_method_list(MIN_METHOD_FOR_MICRODESC, - MAX_SUPPORTED_CONSENSUS_METHOD, - ","); - tor_assert(microdesc_consensus_methods); - } + char *microdesc_consensus_methods = + make_consensus_method_list(consensus_method_low, + consensus_method_high, + ","); + tor_assert(microdesc_consensus_methods); + if (digest256_to_base64(d64, md->digest)<0) - return -1; + goto out; - if (tor_snprintf(out, out_len, "m %s sha256=%s\n", + if (tor_snprintf(out_buf, out_buf_len, "m %s sha256=%s\n", microdesc_consensus_methods, d64)<0) - return -1; + goto out; - return strlen(out); + ret = strlen(out_buf); + + out: + tor_free(microdesc_consensus_methods); + return ret; } /** If <b>vrs</b> has a hash made for the consensus method <b>method</b> with diff --git a/src/or/dirvote.h b/src/or/dirvote.h index e6f9700614..6211218641 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -19,6 +19,32 @@ /** Smallest allowable voting interval. */ #define MIN_VOTE_INTERVAL 300 +/** The highest consensus method that we currently support. */ +#define MAX_SUPPORTED_CONSENSUS_METHOD 14 + +/** Lowest consensus method that contains a 'directory-footer' marker */ +#define MIN_METHOD_FOR_FOOTER 9 + +/** Lowest consensus method that contains bandwidth weights */ +#define MIN_METHOD_FOR_BW_WEIGHTS 9 + +/** Lowest consensus method that contains consensus params */ +#define MIN_METHOD_FOR_PARAMS 7 + +/** Lowest consensus method that generates microdescriptors */ +#define MIN_METHOD_FOR_MICRODESC 8 + +/** Lowest consensus method that ensures a majority of authorities voted + * for a param. */ +#define MIN_METHOD_FOR_MAJORITY_PARAMS 12 + +/** Lowest consensus method where microdesc consensuses omit any entry + * with no microdesc. */ +#define MIN_METHOD_FOR_MANDATORY_MICRODESC 13 + +/** Lowest consensus method that contains "a" lines. */ +#define MIN_METHOD_FOR_A_LINES 14 + void dirvote_free_all(void); /* vote manipulation */ @@ -70,10 +96,12 @@ networkstatus_t * dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert); -microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri); +microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, + int consensus_method); ssize_t dirvote_format_microdesc_vote_line(char *out, size_t out_len, - const microdesc_t *md); - + const microdesc_t *md, + int consensus_method_low, + int consensus_method_high); int vote_routerstatus_find_microdesc_hash(char *digest256_out, const vote_routerstatus_t *vrs, int method, diff --git a/src/or/include.am b/src/or/include.am index 65ff684925..97072dce2f 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -64,7 +64,7 @@ src_or_libtor_a_SOURCES = \ src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or -src/or/tor_main.c: micro-revision.i +src/or/tor_main.o: micro-revision.i AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ @@ -81,7 +81,7 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-crypto.a @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ -noinst_HEADERS+= \ +ORHEADERS = \ src/or/buffers.h \ src/or/circuitbuild.h \ src/or/circuitlist.h \ @@ -122,8 +122,9 @@ noinst_HEADERS+= \ src/or/router.h \ src/or/routerlist.h \ src/or/routerparse.h \ - src/or/status.h \ - micro-revision.i + src/or/status.h + +noinst_HEADERS+= $(ORHEADERS) micro-revision.i src/or/config_codedigest.o: src/or/or_sha1.i @@ -143,12 +144,12 @@ micro-revision.i: FORCE mv micro-revision.tmp micro-revision.i; \ fi; true -src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) +src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES)) | \ + (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \ "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \ elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES)) | \ + (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \ "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \ else \ rm src/or/or_sha1.i; \ diff --git a/src/or/main.c b/src/or/main.c index 5ddf7ba541..39eccd6e65 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1547,11 +1547,15 @@ run_scheduled_events(time_t now) options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 - /* XXXXX this should take a list of ports, not just two! */ - tor_check_port_forwarding(options->PortForwardingHelper, - get_primary_dir_port(), - get_primary_or_port(), - now); + smartlist_t *ports_to_forward = get_list_of_ports_to_forward(); + if (ports_to_forward) { + tor_check_port_forwarding(options->PortForwardingHelper, + ports_to_forward, + now); + + SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp)); + smartlist_free(ports_to_forward); + } time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 26f159319f..b96491f5f9 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -196,7 +196,7 @@ nodelist_add_microdesc(microdesc_t *md) return node; } -/** Tell the nodelist that the current usable consensus to <b>ns</b>. +/** Tell the nodelist that the current usable consensus is <b>ns</b>. * This makes the nodelist change all of the routerstatus entries for * the nodes, drop nodes that no longer have enough info to get used, * and grab microdescriptors into nodes as appropriate. @@ -206,6 +206,7 @@ nodelist_set_consensus(networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v2(options) || authdir_mode_v3(options); + int client = !server_mode(options); init_nodelist(); if (ns->flavor == FLAV_MICRODESC) @@ -242,6 +243,11 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_bad_directory = rs->is_bad_directory; node->is_bad_exit = rs->is_bad_exit; node->is_hs_dir = rs->is_hs_dir; + node->ipv6_preferred = 0; + if (client && options->ClientPreferIPv6ORPort == 1 && + (tor_addr_is_null(&rs->ipv6_addr) == 0 || + (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) + node->ipv6_preferred = 1; } } SMARTLIST_FOREACH_END(rs); @@ -260,7 +266,8 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_valid = node->is_running = node->is_hs_dir = node->is_fast = node->is_stable = node->is_possible_guard = node->is_exit = - node->is_bad_exit = node->is_bad_directory = 0; + node->is_bad_exit = node->is_bad_directory = + node->ipv6_preferred = 0; } } } SMARTLIST_FOREACH_END(node); @@ -808,37 +815,50 @@ node_get_declared_family(const node_t *node) * <b>node</b>, else 0. * * We prefer the IPv6 address if the router has an IPv6 address and - * i) the node_t says that we do prefer IPv6 + * i) the node_t says that it prefers IPv6 * or - * ii) the router has no IPv4 address. */ + * ii) the router has no IPv4 address. */ int node_ipv6_preferred(const node_t *node) { + tor_addr_port_t ipv4_addr; node_assert_ok(node); - if (node->ri) - return (!tor_addr_is_null(&node->ri->ipv6_addr) - && (node->ipv6_preferred || node->ri->addr == 0)); - if (node->rs) - return (!tor_addr_is_null(&node->rs->ipv6_addr) - && (node->ipv6_preferred || node->rs->addr == 0)); + + if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) { + if (node->ri) + return !tor_addr_is_null(&node->ri->ipv6_addr); + if (node->md) + return !tor_addr_is_null(&node->md->ipv6_addr); + if (node->rs) + return !tor_addr_is_null(&node->rs->ipv6_addr); + } return 0; } /** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>node</b> into *<b>ap_out</b>. */ -void + * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and + * port was copied, else return non-zero.*/ +int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) { node_assert_ok(node); tor_assert(ap_out); if (node->ri) { + if (node->ri->addr == 0 || node->ri->or_port == 0) + return -1; tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr); ap_out->port = node->ri->or_port; - } else if (node->rs) { + return 0; + } + if (node->rs) { + if (node->rs->addr == 0 || node->rs->or_port == 0) + return -1; tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); ap_out->port = node->rs->or_port; + return 0; } + return -1; } /** Copy the preferred OR port (IP address and TCP port) for @@ -848,7 +868,13 @@ node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) { tor_assert(ap_out); - if (node_ipv6_preferred(node)) + /* Cheap implementation of config option ClientUseIPv6 -- simply + don't prefer IPv6 when ClientUseIPv6 is not set. (See #4455 for + more on this subject.) Note that this filter is too strict since + we're hindering not only clients! Erring on the safe side + shouldn't be a problem though. XXX move this check to where + outgoing connections are made? -LN */ + if (get_options()->ClientUseIPv6 == 1 && node_ipv6_preferred(node)) node_get_pref_ipv6_orport(node, ap_out); else node_get_prim_orport(node, ap_out); @@ -862,9 +888,17 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) node_assert_ok(node); tor_assert(ap_out); + /* We prefer the microdesc over a potential routerstatus here. They + are not being synchronised atm so there might be a chance that + they differ at some point, f.ex. when flipping + UseMicrodescriptors? -LN */ + if (node->ri) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); ap_out->port = node->ri->ipv6_orport; + } else if (node->md) { + tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr); + ap_out->port = node->md->ipv6_orport; } else if (node->rs) { tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); ap_out->port = node->rs->ipv6_orport; diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 5558827961..fb65fa5483 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -42,18 +42,18 @@ int node_get_purpose(const node_t *node); int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); smartlist_t *node_get_all_orports(const node_t *node); -void node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out); -void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out); -void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); -uint32_t node_get_prim_addr_ipv4h(const node_t *node); int node_allows_single_hop_exits(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); +uint32_t node_get_prim_addr_ipv4h(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); time_t node_get_published_on(const node_t *node); const smartlist_t *node_get_declared_family(const node_t *node); int node_ipv6_preferred(const node_t *node); +int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); smartlist_t *nodelist_get_list(void); diff --git a/src/or/or.h b/src/or/or.h index 00a359c533..3609d32b77 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1960,6 +1960,10 @@ typedef struct microdesc_t { /** As routerinfo_t.onion_pkey */ crypto_pk_t *onion_pkey; + /** As routerinfo_t.ipv6_add */ + tor_addr_t ipv6_addr; + /** As routerinfo_t.ipv6_orport */ + uint16_t ipv6_orport; /** As routerinfo_t.family */ smartlist_t *family; /** Exit policy summary */ @@ -3319,8 +3323,7 @@ typedef struct { int AuthDirMaxServersPerAuthAddr; /**< Do not permit more than this * number of servers per IP address shared * with an authority. */ - int AuthDirHasIPv6Connectivity; /**< Autoboolean: are we on IPv6? */ - int AuthDirPublishIPv6; /**< Boolean: should we list IPv6 OR ports? */ + int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */ /** If non-zero, always vote the Fast flag for any relay advertising * this amount of capacity or more. */ @@ -3487,6 +3490,13 @@ typedef struct { * over randomly chosen exits. */ int ClientRejectInternalAddresses; + /** If true, clients may connect over IPv6. XXX we don't really + enforce this -- clients _may_ set up outgoing IPv6 connections + even when this option is not set. */ + int ClientUseIPv6; + /** If true, prefer an IPv6 OR port over an IPv4 one. */ + int ClientPreferIPv6ORPort; + /** The length of time that we think a consensus should be fresh. */ int V3AuthVotingInterval; /** The length of time we think it will take to distribute votes. */ diff --git a/src/or/router.c b/src/or/router.c index 2cb0e26686..9a3ef9dccf 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -672,7 +672,7 @@ init_keys(void) * we don't really need new keys yet so the descriptor doesn't * change and the old one is still fresh. */ log_info(LD_GENERAL, "Couldn't add own descriptor to directory " - "after key init: %s. This is usually not a problem.", + "after key init: %s This is usually not a problem.", m?m:"<unknown error>"); } } diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 60a2eae75f..7bee08434f 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -67,6 +67,7 @@ typedef enum { K_OR_ADDRESS, K_P, K_R, + K_A, K_S, K_V, K_W, @@ -338,6 +339,7 @@ static token_rule_t extrainfo_token_table[] = { static token_rule_t rtrstatus_token_table[] = { T01("p", K_P, CONCAT_ARGS, NO_OBJ ), T1( "r", K_R, GE(7), NO_OBJ ), + T0N("a", K_A, GE(1), NO_OBJ ), T1( "s", K_S, ARGS, NO_OBJ ), T01("v", K_V, CONCAT_ARGS, NO_OBJ ), T01("w", K_W, ARGS, NO_OBJ ), @@ -522,6 +524,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = { /** List of tokens recognized in microdescriptors */ static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), + T0N("a", K_A, GE(1), NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ), @@ -1257,6 +1260,42 @@ dump_distinct_digest_count(int severity) #endif } +/** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's + * with at least one argument (use GE(1) in setup). If found, store + * address and port number to <b>addr_out</b> and + * <b>port_out</b>. Return number of OR ports found. */ +static int +find_single_ipv6_orport(const smartlist_t *list, + tor_addr_t *addr_out, + uint16_t *port_out) +{ + int ret = 0; + tor_assert(list != NULL); + tor_assert(addr_out != NULL); + tor_assert(port_out != NULL); + + SMARTLIST_FOREACH_BEGIN(list, directory_token_t *, t) { + tor_addr_t a; + maskbits_t bits; + uint16_t port_min, port_max; + tor_assert(t->n_args >= 1); + /* XXXX Prop186 the full spec allows much more than this. */ + if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min, + &port_max) == AF_INET6 && + bits == 128 && + port_min == port_max) { + /* Okay, this is one we can understand. Use it and ignore + any potential more addresses in list. */ + tor_addr_copy(addr_out, &a); + *port_out = port_min; + ret = 1; + break; + } + } SMARTLIST_FOREACH_END(t); + + return ret; +} + /** Helper function: reads a single router entry from *<b>s</b> ... * *<b>end</b>. Mallocs a new router and returns it if all goes well, else * returns NULL. If <b>cache_copy</b> is true, duplicate the contents of @@ -1513,21 +1552,8 @@ router_parse_entry_from_string(const char *s, const char *end, { smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS); if (or_addresses) { - SMARTLIST_FOREACH_BEGIN(or_addresses, directory_token_t *, t) { - tor_addr_t a; - maskbits_t bits; - uint16_t port_min, port_max; - /* XXXX Prop186 the full spec allows much more than this. */ - if (tor_addr_parse_mask_ports(t->args[0], &a, &bits, &port_min, - &port_max) == AF_INET6 && - bits == 128 && - port_min == port_max) { - /* Okay, this is one we can understand. */ - tor_addr_copy(&router->ipv6_addr, &a); - router->ipv6_orport = port_min; - break; - } - } SMARTLIST_FOREACH_END(t); + find_single_ipv6_orport(or_addresses, &router->ipv6_addr, + &router->ipv6_orport); smartlist_free(or_addresses); } } @@ -2060,6 +2086,14 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset], 10,0,65535,NULL,NULL); + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport); + smartlist_free(a_lines); + } + } + tok = find_opt_by_keyword(tokens, K_S); if (tok && vote) { int i; @@ -4421,6 +4455,14 @@ microdescs_parse_from_string(const char *s, const char *eos, md->onion_pkey = tok->key; tok->key = NULL; + { + smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); + if (a_lines) { + find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport); + smartlist_free(a_lines); + } + } + if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) { int i; md->family = smartlist_new(); diff --git a/src/or/transports.c b/src/or/transports.c index dd07a917ee..f2c604ce8b 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -585,15 +585,12 @@ pt_configure_remaining_proxies(void) mark_my_descriptor_dirty("configured managed proxies"); } -#ifdef _WIN32 - /** Attempt to continue configuring managed proxy <b>mp</b>. */ static void configure_proxy(managed_proxy_t *mp) { - int pos; - char stdout_buf[200]; - smartlist_t *lines = NULL; + smartlist_t *proxy_output = NULL; + enum stream_status stream_status = 0; /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { @@ -607,28 +604,18 @@ configure_proxy(managed_proxy_t *mp) tor_assert(mp->conf_state != PT_PROTO_INFANT); tor_assert(mp->process_handle); - pos = tor_read_all_handle(tor_process_get_stdout_pipe(mp->process_handle), - stdout_buf, sizeof(stdout_buf) - 1, NULL); - if (pos < 0) { - log_notice(LD_GENERAL, "Failed to read data from managed proxy '%s'.", - mp->argv[0]); - mp->conf_state = PT_PROTO_BROKEN; + proxy_output = + tor_get_lines_from_handle(tor_process_get_stdout_pipe(mp->process_handle), + &stream_status); + if (!proxy_output) { /* failed to get input from proxy */ + if (stream_status != IO_STREAM_EAGAIN) + mp->conf_state = PT_PROTO_BROKEN; + goto done; } - if (pos == 0) /* proxy has nothing interesting to say. */ - return; - - /* End with a null even if there isn't a \r\n at the end */ - /* TODO: What if this is a partial line? */ - stdout_buf[pos] = '\0'; - - /* Split up the buffer */ - lines = smartlist_new(); - tor_split_lines(lines, stdout_buf, pos); - /* Handle lines. */ - SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { + SMARTLIST_FOREACH_BEGIN(proxy_output, const char *, line) { handle_proxy_line(line, mp); if (proxy_configuration_finished(mp)) goto done; @@ -639,59 +626,12 @@ configure_proxy(managed_proxy_t *mp) if (proxy_configuration_finished(mp)) handle_finished_proxy(mp); - if (lines) - smartlist_free(lines); -} - -#else /* _WIN32 */ - -/** Attempt to continue configuring managed proxy <b>mp</b>. */ -static void -configure_proxy(managed_proxy_t *mp) -{ - enum stream_status r; - char stdout_buf[200]; - - /* if we haven't launched the proxy yet, do it now */ - if (mp->conf_state == PT_PROTO_INFANT) { - if (launch_managed_proxy(mp) < 0) { /* launch fail */ - mp->conf_state = PT_PROTO_FAILED_LAUNCH; - handle_finished_proxy(mp); - } - return; - } - - tor_assert(mp->conf_state != PT_PROTO_INFANT); - tor_assert(mp->process_handle); - - while (1) { - r = get_string_from_pipe(tor_process_get_stdout_pipe(mp->process_handle), - stdout_buf, sizeof(stdout_buf) - 1); - - if (r == IO_STREAM_OKAY) { /* got a line; handle it! */ - handle_proxy_line((const char *)stdout_buf, mp); - } else if (r == IO_STREAM_EAGAIN) { /* check back later */ - return; - } else if (r == IO_STREAM_CLOSED || r == IO_STREAM_TERM) { /* snap! */ - log_warn(LD_GENERAL, "Our communication channel with the managed proxy " - "'%s' closed. Most probably application stopped running.", - mp->argv[0]); - mp->conf_state = PT_PROTO_BROKEN; - } else { /* unknown stream status */ - log_warn(LD_BUG, "Unknown stream status '%d' while configuring managed " - "proxy '%s'.", (int)r, mp->argv[0]); - } - - /* if the proxy finished configuring, exit the loop. */ - if (proxy_configuration_finished(mp)) { - handle_finished_proxy(mp); - return; - } + if (proxy_output) { + SMARTLIST_FOREACH(proxy_output, char *, cp, tor_free(cp)); + smartlist_free(proxy_output); } } -#endif /* _WIN32 */ - /** Register server managed proxy <b>mp</b> transports to state */ static void register_server_proxy(const managed_proxy_t *mp) @@ -1384,6 +1324,33 @@ pt_prepare_proxy_list_for_config_read(void) tor_assert(unconfigured_proxies_n == 0); } +/** Return a smartlist containing the ports where our pluggable + * transports are listening. */ +smartlist_t * +get_transport_proxy_ports(void) +{ + smartlist_t *sl = NULL; + + if (!managed_proxy_list) + return NULL; + + /** XXX assume that external proxy ports have been forwarded + manually */ + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + if (!mp->is_server || mp->conf_state != PT_PROTO_COMPLETED) + continue; + + if (!sl) sl = smartlist_new(); + + tor_assert(mp->transports); + SMARTLIST_FOREACH(mp->transports, const transport_t *, t, + smartlist_add_asprintf(sl, "%u:%u", t->port, t->port)); + + } SMARTLIST_FOREACH_END(mp); + + return sl; +} + /** Return the pluggable transport string that we should display in * our extra-info descriptor. If we shouldn't display such a string, * or we have nothing to display, return NULL. The string is diff --git a/src/or/transports.h b/src/or/transports.h index 3fd97f8c2a..86a2530fcb 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -54,6 +54,8 @@ void pt_free_all(void); void pt_prepare_proxy_list_for_config_read(void); void sweep_proxy_list(void); +smartlist_t *get_transport_proxy_ports(void); + #ifdef PT_PRIVATE /** State of the managed proxy configuration protocol. */ enum pt_proto_state { diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 84705d0e02..af878a696f 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -797,6 +797,7 @@ test_dir_v3_networkstatus(void) networkstatus_t *vote=NULL, *v1=NULL, *v2=NULL, *v3=NULL, *con=NULL, *con_md=NULL; vote_routerstatus_t *vrs; + tor_addr_t addr_ipv6; routerstatus_t *rs; char *v1_text=NULL, *v2_text=NULL, *v3_text=NULL, *consensus_text=NULL, *cp; smartlist_t *votes = smartlist_new(); @@ -893,6 +894,9 @@ test_dir_v3_networkstatus(void) rs->addr = 0x99009901; rs->or_port = 443; rs->dir_port = 0; + tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); + tor_addr_copy(&rs->ipv6_addr, &addr_ipv6); + rs->ipv6_orport = 4711; rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1; smartlist_add(vote->routerstatus_list, vrs); @@ -987,6 +991,8 @@ test_dir_v3_networkstatus(void) test_eq(rs->addr, 0x99009901); test_eq(rs->or_port, 443); test_eq(rs->dir_port, 0); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); test_eq(vrs->flags, U64_LITERAL(254)); // all flags except "authority." { @@ -1169,6 +1175,8 @@ test_dir_v3_networkstatus(void) test_eq(rs->addr, 0x99009901); test_eq(rs->or_port, 443); test_eq(rs->dir_port, 0); + test_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); + test_eq(rs->ipv6_orport, 4711); test_assert(!rs->is_authority); test_assert(rs->is_exit); test_assert(rs->is_fast); diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c index 0e0b385f9b..ee6d5f3434 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c +++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c @@ -60,15 +60,15 @@ tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state) state->lease = NATPMP_DEFAULT_LEASE; if (tor_fw_options->verbose) - fprintf(stdout, "V: natpmp init...\n"); + fprintf(stderr, "V: natpmp init...\n"); r = initnatpmp(&(state->natpmp), 0, 0); if (r == 0) { state->init = 1; - fprintf(stdout, "tor-fw-helper: natpmp initialized...\n"); + fprintf(stderr, "V: natpmp initialized...\n"); return r; } else { - fprintf(stderr, "tor-fw-helper: natpmp failed to initialize...\n"); + fprintf(stderr, "V: natpmp failed to initialize...\n"); return r; } } @@ -80,10 +80,10 @@ tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state) natpmp_state_t *state = (natpmp_state_t *) backend_state; int r = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: natpmp cleanup...\n"); + fprintf(stderr, "V: natpmp cleanup...\n"); r = closenatpmp(&(state->natpmp)); if (tor_fw_options->verbose) - fprintf(stdout, "V: closing natpmp socket: %d\n", r); + fprintf(stderr, "V: closing natpmp socket: %d\n", r); return r; } @@ -101,7 +101,7 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout) FD_SET(fd, &fds); r = select(fd+1, &fds, NULL, NULL, timeout); if (r == -1) { - fprintf(stdout, "V: select failed in wait_until_fd_readable: %s\n", + fprintf(stderr, "V: select failed in wait_until_fd_readable: %s\n", strerror(errno)); return -1; } @@ -110,27 +110,25 @@ wait_until_fd_readable(tor_socket_t fd, struct timeval *timeout) return 0; } -/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b> - * using the <b>natpmp_t</b> stored in <b>backend_state</b>. */ int -tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, - void *backend_state) +tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state) { - natpmp_state_t *state = (natpmp_state_t *) backend_state; int r = 0; int x = 0; int sav_errno; + natpmp_state_t *state = (natpmp_state_t *) backend_state; struct timeval timeout; - if (tor_fw_options->verbose) - fprintf(stdout, "V: sending natpmp portmapping request...\n"); + if (is_verbose) + fprintf(stderr, "V: sending natpmp portmapping request...\n"); r = sendnewportmappingrequest(&(state->natpmp), state->protocol, - tor_fw_options->internal_port, - tor_fw_options->external_port, + internal_port, + external_port, state->lease); - if (tor_fw_options->verbose) - fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " + if (is_verbose) + fprintf(stderr, "tor-fw-helper: NAT-PMP sendnewportmappingrequest " "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED"); do { @@ -139,8 +137,8 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, if (x == -1) return -1; - if (tor_fw_options->verbose) - fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n"); + if (is_verbose) + fprintf(stderr, "V: attempting to readnatpmpreponseorretry...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = errno; @@ -163,16 +161,14 @@ tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, } if (r == NATPMP_SUCCESS) { - fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to" + fprintf(stderr, "tor-fw-helper: NAT-PMP mapped public port %hu to" " localport %hu liftime %u\n", (state->response).pnu.newportmapping.mappedpublicport, (state->response).pnu.newportmapping.privateport, (state->response).pnu.newportmapping.lifetime); } - tor_fw_options->nat_pmp_status = 1; - - return r; + return (r == NATPMP_SUCCESS) ? 0 : -1; } /** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device. @@ -189,7 +185,7 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, struct timeval timeout; r = sendpublicaddressrequest(&(state->natpmp)); - fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" + fprintf(stderr, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned" " %d (%s)\n", r, r==2?"SUCCESS":"FAILED"); do { @@ -200,12 +196,12 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, return -1; if (tor_fw_options->verbose) - fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n"); + fprintf(stderr, "V: NAT-PMP attempting to read reponse...\n"); r = readnatpmpresponseorretry(&(state->natpmp), &(state->response)); sav_errno = errno; if (tor_fw_options->verbose) - fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned" + fprintf(stderr, "V: NAT-PMP readnatpmpresponseorretry returned" " %d\n", r); if ( r < 0 && r != NATPMP_TRYAGAIN) { @@ -223,15 +219,15 @@ tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, return r; } - fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", + fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n", inet_ntoa((state->response).pnu.publicaddress.addr)); tor_fw_options->public_ip_status = 1; if (tor_fw_options->verbose) { - fprintf(stdout, "V: result = %u\n", r); - fprintf(stdout, "V: type = %u\n", (state->response).type); - fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode); - fprintf(stdout, "V: epoch = %u\n", (state->response).epoch); + fprintf(stderr, "V: result = %u\n", r); + fprintf(stderr, "V: type = %u\n", (state->response).type); + fprintf(stderr, "V: resultcode = %u\n", (state->response).resultcode); + fprintf(stderr, "V: epoch = %u\n", (state->response).epoch); } return r; diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h index 54f541bcf4..0f97236afb 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h +++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h @@ -36,8 +36,8 @@ int tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state); int tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state); -int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options, - void *backend_state); +int tor_natpmp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options, void *backend_state); diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c index 7c104f11cd..e5c33db0b4 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c +++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c @@ -91,7 +91,7 @@ tor_upnp_init(tor_fw_options_t *options, void *backend_state) assert(options); r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data), state->lanaddr, UPNP_LANADDR_SZ); - fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r, + fprintf(stderr, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r, r==UPNP_SUCCESS?"SUCCESS":"FAILED"); freeUPNPDevlist(devlist); @@ -141,7 +141,7 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state) goto err; if (externalIPAddress[0]) { - fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n", + fprintf(stderr, "tor-fw-helper: ExternalIPAddress = %s\n", externalIPAddress); tor_upnp_cleanup(options, state); options->public_ip_status = 1; return UPNP_ERR_SUCCESS; @@ -154,44 +154,40 @@ tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state) return UPNP_ERR_GETEXTERNALIP; } -/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b> - * and store the results in <b>backend_state</b>. */ int -tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state) +tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state) { - miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state; - int r; + int retval; char internal_port_str[6]; char external_port_str[6]; + miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state; if (!state->init) { - r = tor_upnp_init(options, state); - if (r != UPNP_ERR_SUCCESS) - return r; + fprintf(stderr, "E: %s but state is not initialized.\n", __func__); + return -1; } - if (options->verbose) - fprintf(stdout, "V: internal port: %d, external port: %d\n", - (int)options->internal_port, (int)options->external_port); + if (is_verbose) + fprintf(stderr, "V: UPnP: internal port: %u, external port: %u\n", + internal_port, external_port); tor_snprintf(internal_port_str, sizeof(internal_port_str), - "%d", (int)options->internal_port); + "%u", internal_port); tor_snprintf(external_port_str, sizeof(external_port_str), - "%d", (int)options->external_port); + "%u", external_port); - r = UPNP_AddPortMapping(state->urls.controlURL, - state->data.first.servicetype, - external_port_str, internal_port_str, + retval = UPNP_AddPortMapping(state->urls.controlURL, + state->data.first.servicetype, + external_port_str, internal_port_str, #ifdef MINIUPNPC15 - state->lanaddr, UPNP_DESC, "TCP", 0); + state->lanaddr, UPNP_DESC, "TCP", 0); #else - state->lanaddr, UPNP_DESC, "TCP", 0, 0); + state->lanaddr, UPNP_DESC, "TCP", 0, 0); #endif - if (r != UPNPCOMMAND_SUCCESS) - return UPNP_ERR_ADDPORTMAPPING; - options->upnp_status = 1; - return UPNP_ERR_SUCCESS; + return (retval == UPNP_ERR_SUCCESS) ? 0 : -1; } + #endif diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h index f037c75bab..3a061981d1 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h +++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h @@ -36,7 +36,8 @@ int tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state); int tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state); -int tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state); +int tor_upnp_add_tcp_mapping(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); #endif #endif diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c index 0510e65d11..4efe515cc4 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper.c +++ b/src/tools/tor-fw-helper/tor-fw-helper.c @@ -20,6 +20,9 @@ #include <getopt.h> #include <time.h> #include <string.h> +#include <assert.h> + +#include "container.h" #ifdef _WIN32 #include <winsock2.h> @@ -45,7 +48,7 @@ typedef struct backends_t { void *backend_state[MAX_BACKENDS]; } backends_t; -/** Initalize each backend helper with the user input stored in <b>options</b> +/** Initialize each backend helper with the user input stored in <b>options</b> * and put the results in the <b>backends</b> struct. */ static int init_backends(tor_fw_options_t *options, backends_t *backends) @@ -97,10 +100,7 @@ usage(void) " [-T|--Test]\n" " [-v|--verbose]\n" " [-g|--fetch-public-ip]\n" - " -i|--internal-or-port [TCP port]\n" - " [-e|--external-or-port [TCP port]]\n" - " [-d|--internal-dir-port [TCP port]\n" - " [-p|--external-dir-port [TCP port]]]\n"); + " [-p|--forward-port ([<external port>]:<internal port>])\n"); } /** Log commandline options to a hardcoded file <b>tor-fw-helper.log</b> in the @@ -125,7 +125,7 @@ log_commandline_options(int argc, char **argv) if (retval < 0) goto error; - retval = fprintf(stdout, "ARG: %d: %s\n", i, argv[i]); + retval = fprintf(stderr, "ARG: %d: %s\n", i, argv[i]); if (retval < 0) goto error; } @@ -152,82 +152,141 @@ tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options, int r = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_fetch_public_ip\n"); + fprintf(stderr, "V: tor_fw_fetch_public_ip\n"); for (i=0; i<backends->n_backends; ++i) { if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", + fprintf(stderr, "V: running backend_state now: %i\n", i); + fprintf(stderr, "V: size of backend state: %u\n", (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", + fprintf(stderr, "V: backend state name: %s\n", (char *)(backends->backend_ops)[i].name); } r = backends->backend_ops[i].fetch_public_ip(tor_fw_options, backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_fetch_public_ip backend %s " + fprintf(stderr, "tor-fw-helper: tor_fw_fetch_public_ip backend %s " " returned: %i\n", (char *)(backends->backend_ops)[i].name, r); } } -/** Iterate over each of the supported <b>backends</b> and attempt to add a - * port forward for the OR port stored in <b>tor_fw_options</b>. */ +/** Print a spec-conformant string to stdout describing the results of + * the TCP port forwarding operation from <b>external_port</b> to + * <b>internal_port</b>. */ static void -tor_fw_add_or_port(tor_fw_options_t *tor_fw_options, - backends_t *backends) +tor_fw_helper_report_port_fw_results(uint16_t internal_port, + uint16_t external_port, + int succeded, + const char *message) +{ + char *report_string = NULL; + + tor_asprintf(&report_string, "%s %s %u %u %s %s\n", + "tor-fw-helper", + "tcp-forward", + external_port, internal_port, + succeded ? "SUCCESS" : "FAIL", + message); + fprintf(stdout, "%s", report_string); + fflush(stdout); + tor_free(report_string); +} + +#define tor_fw_helper_report_port_fw_fail(i, e, m) \ + tor_fw_helper_report_port_fw_results((i), (e), 0, (m)) + +#define tor_fw_helper_report_port_fw_success(i, e, m) \ + tor_fw_helper_report_port_fw_results((i), (e), 1, (m)) + +/** Return a heap-allocated string containing the list of our + * backends. It can be used in log messages. Be sure to free it + * afterwards! */ +static char * +get_list_of_backends_string(backends_t *backends) { + char *backend_names = NULL; int i; - int r = 0; + smartlist_t *backend_names_sl = smartlist_new(); - if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_add_or_port\n"); + assert(backends->n_backends); - for (i=0; i<backends->n_backends; ++i) { - if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", - (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", - (const char *) backends->backend_ops[i].name); - } - r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options, - backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_add_or_port backend %s " - "returned: %i\n", (const char *) backends->backend_ops[i].name, r); - } + for (i=0; i<backends->n_backends; ++i) + smartlist_add(backend_names_sl, (char *) backends->backend_ops[i].name); + + backend_names = smartlist_join_strings(backend_names_sl, ", ", 0, NULL); + smartlist_free(backend_names_sl); + + return backend_names; } /** Iterate over each of the supported <b>backends</b> and attempt to add a - * port forward for the Dir port stored in <b>tor_fw_options</b>. */ + * port forward for the port stored in <b>tor_fw_options</b>. */ static void -tor_fw_add_dir_port(tor_fw_options_t *tor_fw_options, - backends_t *backends) +tor_fw_add_ports(tor_fw_options_t *tor_fw_options, + backends_t *backends) { int i; int r = 0; + int succeeded = 0; if (tor_fw_options->verbose) - fprintf(stdout, "V: tor_fw_add_dir_port\n"); + fprintf(stderr, "V: %s\n", __func__); - for (i=0; i<backends->n_backends; ++i) { - if (tor_fw_options->verbose) { - fprintf(stdout, "V: running backend_state now: %i\n", i); - fprintf(stdout, "V: size of backend state: %u\n", - (int)(backends->backend_ops)[i].state_len); - fprintf(stdout, "V: backend state name: %s\n", - (char *)(backends->backend_ops)[i].name); + /** Loop all ports that need to be forwarded, and try to use our + * backends for each port. If a backend succeeds, break the loop, + * report success and get to the next port. If all backends fail, + * report failure for that port. */ + SMARTLIST_FOREACH_BEGIN(tor_fw_options->ports_to_forward, + port_to_forward_t *, port_to_forward) { + + succeeded = 0; + + for (i=0; i<backends->n_backends; ++i) { + if (tor_fw_options->verbose) { + fprintf(stderr, "V: running backend_state now: %i\n", i); + fprintf(stderr, "V: size of backend state: %u\n", + (int)(backends->backend_ops)[i].state_len); + fprintf(stderr, "V: backend state name: %s\n", + (const char *) backends->backend_ops[i].name); + } + + r = + backends->backend_ops[i].add_tcp_mapping(port_to_forward->internal_port, + port_to_forward->external_port, + tor_fw_options->verbose, + backends->backend_state[i]); + if (r == 0) { /* backend success */ + tor_fw_helper_report_port_fw_success(port_to_forward->internal_port, + port_to_forward->external_port, + backends->backend_ops[i].name); + succeeded = 1; + break; + } + + fprintf(stderr, "tor-fw-helper: tor_fw_add_port backend %s " + "returned: %i\n", + (const char *) backends->backend_ops[i].name, r); } - r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options, - backends->backend_state[i]); - fprintf(stdout, "tor-fw-helper: tor_fw_add_dir_port backend %s " - "returned: %i\n", (const char *)backends->backend_ops[i].name, r); - } + + if (!succeeded) { /* all backends failed */ + char *list_of_backends_str = get_list_of_backends_string(backends); + char *fail_msg = NULL; + tor_asprintf(&fail_msg, "All port forwarding backends (%s) failed.", + list_of_backends_str); + tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port, + port_to_forward->external_port, + fail_msg); + tor_free(list_of_backends_str); + tor_free(fail_msg); + } + + } SMARTLIST_FOREACH_END(port_to_forward); } /** Called before we make any calls to network-related functions. * (Some operating systems require their network libraries to be * initialized.) (from common/compat.c) */ static int -network_init(void) +tor_fw_helper_network_init(void) { #ifdef _WIN32 /* This silly exercise is necessary before windows will allow @@ -247,6 +306,67 @@ network_init(void) return 0; } +/** Parse the '-p' argument of tor-fw-helper. Its format is + * [<external port>]:<internal port>, and <external port> is optional. + * Return NULL if <b>arg</b> was c0rrupted. */ +static port_to_forward_t * +parse_port(const char *arg) +{ + smartlist_t *sl = smartlist_new(); + port_to_forward_t *port_to_forward = NULL; + char *port_str = NULL; + int ok; + int port; + + smartlist_split_string(sl, arg, ":", 0, 0); + if (smartlist_len(sl) != 2) + goto err; + + port_to_forward = tor_malloc(sizeof(port_to_forward_t)); + if (!port_to_forward) + goto err; + + port_str = smartlist_get(sl, 0); /* macroify ? */ + port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); + if (!ok && strlen(port_str)) /* ":1555" is valid */ + goto err; + port_to_forward->external_port = port; + + port_str = smartlist_get(sl, 1); + port = (int)tor_parse_long(port_str, 10, 1, 65535, &ok, NULL); + if (!ok) + goto err; + port_to_forward->internal_port = port; + + goto done; + + err: + tor_free(port_to_forward); + + done: + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); + + return port_to_forward; +} + +/** Report a failure of epic proportions: We didn't manage to + * initialize any port forwarding backends. */ +static void +report_full_fail(const smartlist_t *ports_to_forward) +{ + if (!ports_to_forward) + return; + + SMARTLIST_FOREACH_BEGIN(ports_to_forward, + const port_to_forward_t *, port_to_forward) { + tor_fw_helper_report_port_fw_fail(port_to_forward->internal_port, + port_to_forward->external_port, + "All backends (NAT-PMP, UPnP) failed " + "to initialize!"); /* XXX hardcoded */ + } SMARTLIST_FOREACH_END(port_to_forward); +} + int main(int argc, char **argv) { @@ -259,22 +379,20 @@ main(int argc, char **argv) memset(&tor_fw_options, 0, sizeof(tor_fw_options)); memset(&backend_state, 0, sizeof(backend_state)); + // Parse CLI arguments. while (1) { int option_index = 0; static struct option long_options[] = { {"verbose", 0, 0, 'v'}, {"help", 0, 0, 'h'}, - {"internal-or-port", 1, 0, 'i'}, - {"external-or-port", 1, 0, 'e'}, - {"internal-dir-port", 1, 0, 'd'}, - {"external-dir-port", 1, 0, 'p'}, + {"port", 1, 0, 'p'}, {"fetch-public-ip", 0, 0, 'g'}, {"test-commandline", 0, 0, 'T'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "vhi:e:d:p:gT", + c = getopt_long(argc, argv, "vhp:gT", long_options, &option_index); if (c == -1) break; @@ -282,14 +400,31 @@ main(int argc, char **argv) switch (c) { case 'v': tor_fw_options.verbose = 1; break; case 'h': tor_fw_options.help = 1; usage(); exit(1); break; - case 'i': sscanf(optarg, "%hu", &tor_fw_options.private_or_port); - break; - case 'e': sscanf(optarg, "%hu", &tor_fw_options.public_or_port); - break; - case 'd': sscanf(optarg, "%hu", &tor_fw_options.private_dir_port); - break; - case 'p': sscanf(optarg, "%hu", &tor_fw_options.public_dir_port); + case 'p': { + port_to_forward_t *port_to_forward = parse_port(optarg); + if (!port_to_forward) { + fprintf(stderr, "E: Failed to parse '%s'.\n", optarg); + usage(); + exit(1); + } + + /* If no external port was given (it's optional), set it to be + * equal with the internal port. */ + if (!port_to_forward->external_port) { + assert(port_to_forward->internal_port); + if (tor_fw_options.verbose) + fprintf(stderr, "V: No external port was given. Setting to %u.\n", + port_to_forward->internal_port); + port_to_forward->external_port = port_to_forward->internal_port; + } + + if (!tor_fw_options.ports_to_forward) + tor_fw_options.ports_to_forward = smartlist_new(); + + smartlist_add(tor_fw_options.ports_to_forward, port_to_forward); + break; + } case 'g': tor_fw_options.fetch_public_ip = 1; break; case 'T': tor_fw_options.test_commandline = 1; break; case '?': break; @@ -297,98 +432,68 @@ main(int argc, char **argv) } } - if (tor_fw_options.verbose) { - fprintf(stderr, "V: tor-fw-helper version %s\n" - "V: We were called with the following arguments:\n" - "V: verbose = %d, help = %d, pub or port = %u, " - "priv or port = %u\n" - "V: pub dir port = %u, priv dir port = %u\n" - "V: fetch_public_ip = %u\n", - tor_fw_version, tor_fw_options.verbose, tor_fw_options.help, - tor_fw_options.private_or_port, tor_fw_options.public_or_port, - tor_fw_options.private_dir_port, tor_fw_options.public_dir_port, - tor_fw_options.fetch_public_ip); + { // Verbose output + + if (tor_fw_options.verbose) + fprintf(stderr, "V: tor-fw-helper version %s\n" + "V: We were called with the following arguments:\n" + "V: verbose = %d, help = %d, fetch_public_ip = %u\n", + tor_fw_version, tor_fw_options.verbose, tor_fw_options.help, + tor_fw_options.fetch_public_ip); + + if (tor_fw_options.verbose && tor_fw_options.ports_to_forward) { + fprintf(stderr, "V: TCP forwarding:\n"); + SMARTLIST_FOREACH(tor_fw_options.ports_to_forward, + const port_to_forward_t *, port_to_forward, + fprintf(stderr, "V: External: %u, Internal: %u\n", + port_to_forward->external_port, + port_to_forward->internal_port)); + } } if (tor_fw_options.test_commandline) { return log_commandline_options(argc, argv); } - /* At the very least, we require an ORPort; - Given a private ORPort, we can ask for a mapping that matches the port - externally. - */ - if (!tor_fw_options.private_or_port && !tor_fw_options.fetch_public_ip) { - fprintf(stderr, "E: We require an ORPort or fetch_public_ip" - " request!\n"); + // See if the user actually wants us to do something. + if (!tor_fw_options.fetch_public_ip && !tor_fw_options.ports_to_forward) { + fprintf(stderr, "E: We require a port to be forwarded or " + "fetch_public_ip request!\n"); usage(); exit(1); - } else { - /* When we only have one ORPort, internal/external are - set to be the same.*/ - if (!tor_fw_options.public_or_port && tor_fw_options.private_or_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We're setting public_or_port = " - "private_or_port.\n"); - tor_fw_options.public_or_port = tor_fw_options.private_or_port; - } - } - if (!tor_fw_options.private_dir_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We have no DirPort; no hole punching for " - "DirPorts\n"); - - } else { - /* When we only have one DirPort, internal/external are - set to be the same.*/ - if (!tor_fw_options.public_dir_port && tor_fw_options.private_dir_port) { - if (tor_fw_options.verbose) - fprintf(stdout, "V: We're setting public_or_port = " - "private_or_port.\n"); - - tor_fw_options.public_dir_port = tor_fw_options.private_dir_port; - } - } - - if (tor_fw_options.verbose) { - fprintf(stdout, "V: pub or port = %u, priv or port = %u\n" - "V: pub dir port = %u, priv dir port = %u\n", - tor_fw_options.private_or_port, tor_fw_options.public_or_port, - tor_fw_options.private_dir_port, - tor_fw_options.public_dir_port); } // Initialize networking - if (network_init()) + if (tor_fw_helper_network_init()) exit(1); // Initalize the various fw-helper backend helpers r = init_backends(&tor_fw_options, &backend_state); - if (r) - printf("tor-fw-helper: %i NAT traversal helper(s) loaded\n", r); - - if (tor_fw_options.fetch_public_ip) { - tor_fw_fetch_public_ip(&tor_fw_options, &backend_state); + if (!r) { // all backends failed: + // report our failure + report_full_fail(tor_fw_options.ports_to_forward); + fprintf(stderr, "tor-fw-helper: All backends failed.\n"); + exit(1); + } else { // some backends succeeded: + fprintf(stderr, "tor-fw-helper: %i NAT traversal helper(s) loaded\n", r); } - if (tor_fw_options.private_or_port) { - tor_fw_options.internal_port = tor_fw_options.private_or_port; - tor_fw_options.external_port = tor_fw_options.private_or_port; - tor_fw_add_or_port(&tor_fw_options, &backend_state); + // Forward TCP ports. + if (tor_fw_options.ports_to_forward) { + tor_fw_add_ports(&tor_fw_options, &backend_state); } - if (tor_fw_options.private_dir_port) { - tor_fw_options.internal_port = tor_fw_options.private_dir_port; - tor_fw_options.external_port = tor_fw_options.private_dir_port; - tor_fw_add_dir_port(&tor_fw_options, &backend_state); + // Fetch our public IP. + if (tor_fw_options.fetch_public_ip) { + tor_fw_fetch_public_ip(&tor_fw_options, &backend_state); } - r = (((tor_fw_options.nat_pmp_status | tor_fw_options.upnp_status) - |tor_fw_options.public_ip_status)); - if (r > 0) { - fprintf(stdout, "tor-fw-helper: SUCCESS\n"); - } else { - fprintf(stderr, "tor-fw-helper: FAILURE\n"); + // Cleanup and exit. + if (tor_fw_options.ports_to_forward) { + SMARTLIST_FOREACH(tor_fw_options.ports_to_forward, + port_to_forward_t *, port, + tor_free(port)); + smartlist_free(tor_fw_options.ports_to_forward); } exit(r); diff --git a/src/tools/tor-fw-helper/tor-fw-helper.h b/src/tools/tor-fw-helper/tor-fw-helper.h index 058afc4e09..08f94d0830 100644 --- a/src/tools/tor-fw-helper/tor-fw-helper.h +++ b/src/tools/tor-fw-helper/tor-fw-helper.h @@ -17,24 +17,26 @@ #include <time.h> /** The current version of tor-fw-helper. */ -#define tor_fw_version "0.1" +#define tor_fw_version "0.2" /** This is an arbitrary hard limit - We currently have two (NAT-PMP and UPnP). We're likely going to add the Intel UPnP library but nothing else comes to mind at the moment. */ #define MAX_BACKENDS 23 +/** Forward traffic received in port <b>external_port</b> in the + * external side of our NAT to <b>internal_port</b> in this host. */ +typedef struct { + uint16_t external_port; + uint16_t internal_port; +} port_to_forward_t; + /** This is where we store parsed commandline options. */ typedef struct { int verbose; int help; int test_commandline; - uint16_t private_dir_port; - uint16_t private_or_port; - uint16_t public_dir_port; - uint16_t public_or_port; - uint16_t internal_port; - uint16_t external_port; + struct smartlist_t *ports_to_forward; int fetch_public_ip; int nat_pmp_status; int upnp_status; @@ -50,8 +52,8 @@ typedef struct tor_fw_backend_t { int (*init)(tor_fw_options_t *options, void *backend_state); int (*cleanup)(tor_fw_options_t *options, void *backend_state); int (*fetch_public_ip)(tor_fw_options_t *options, void *backend_state); - int (*add_tcp_mapping)(tor_fw_options_t *options, void *backend_state); + int (*add_tcp_mapping)(uint16_t internal_port, uint16_t external_port, + int is_verbose, void *backend_state); } tor_fw_backend_t; - #endif diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 4c8c65518a..286854fdc2 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -232,7 +232,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.2.4.0-alpha-dev" +#define VERSION "0.2.4.1-alpha-dev" |