diff options
Diffstat (limited to 'src/or')
36 files changed, 2487 insertions, 874 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 67adf504df..57f5dd65e4 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -74,7 +74,7 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \ ../common/libor-event.a \ - @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ noinst_HEADERS = \ @@ -126,8 +126,9 @@ tor_main.o: micro-revision.i micro-revision.i: FORCE @rm -f micro-revision.tmp; \ - if test -d ../../.git && test -x "`which git 2>&1;true`"; then \ - HASH="`git rev-parse --short=16 HEAD`"; \ + if test -d "$(top_srcdir)/.git" && \ + test -x "`which git 2>&1;true`"; then \ + HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ echo \"$$HASH\" > micro-revision.tmp; \ fi; \ if test ! -f micro-revision.tmp ; then \ @@ -141,10 +142,10 @@ micro-revision.i: FORCE or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES) if test "@SHA1SUM@" != none; then \ - @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \ elif test "@OPENSSL@" != none; then \ - @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \ + (cd "$(srcdir)" && @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES)) | \ @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \ else \ rm or_sha1.i; \ diff --git a/src/or/buffers.c b/src/or/buffers.c index f4aac0f0e4..7c49423e16 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -401,9 +401,10 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate) if (buf->head->memlen >= capacity) { /* We don't need to grow the first chunk, but we might need to repack it.*/ - if (CHUNK_REMAINING_CAPACITY(buf->head) < capacity-buf->datalen) + size_t needed = capacity - buf->head->datalen; + if (CHUNK_REMAINING_CAPACITY(buf->head) < needed) chunk_repack(buf->head); - tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= capacity-buf->datalen); + tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= needed); } else { chunk_t *newhead; size_t newsize; diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b04bd10120..a9e5d22943 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2524,7 +2524,8 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload, append_cell_to_circuit_queue(TO_CIRCUIT(circ), circ->p_conn, &cell, CELL_DIRECTION_IN, 0); - log_debug(LD_CIRC,"Finished sending 'created' cell."); + log_debug(LD_CIRC,"Finished sending '%s' cell.", + circ->is_first_hop ? "created_fast" : "created"); if (!is_local_addr(&circ->p_conn->_base.addr) && !connection_or_nonopen_was_started_here(circ->p_conn)) { @@ -3012,7 +3013,7 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit) log_warn(LD_CIRC,"failed to choose an exit server"); return -1; } - exit = extend_info_from_node(node); + exit = extend_info_from_node(node, 0); tor_assert(exit); } state->chosen_exit = exit; @@ -3254,14 +3255,19 @@ 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) { - info = extend_info_from_node(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); tor_assert(info); } } else { const node_t *r = choose_good_middle_server(purpose, state, circ->cpath, cur_len); if (r) { - info = extend_info_from_node(r); + info = extend_info_from_node(r, 0); tor_assert(info); } } @@ -3320,28 +3326,36 @@ extend_info_alloc(const char *nickname, const char *digest, return info; } -/** Allocate and return a new extend_info_t that can be used to build a - * circuit to or through the router <b>r</b>. */ +/** Allocate and return a new extend_info_t that can be used to build + * a circuit to or through the router <b>r</b>. Use the primary + * address of the router unless <b>for_direct_connect</b> is true, in + * which case the preferred address is used instead. */ extend_info_t * -extend_info_from_router(const routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int for_direct_connect) { - tor_addr_t addr; + tor_addr_port_t ap; tor_assert(r); - tor_addr_from_ipv4h(&addr, r->addr); + + if (for_direct_connect) + router_get_pref_orport(r, &ap); + else + router_get_prim_orport(r, &ap); return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &addr, r->or_port); + r->onion_pkey, &ap.addr, ap.port); } -/** Allocate and return a new extend_info that can be used to build a ircuit - * to or through the node <b>node</b>. 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. +/** Allocate and return a new extend_info that can be used to build a + * ircuit 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. **/ extend_info_t * -extend_info_from_node(const node_t *node) +extend_info_from_node(const node_t *node, int for_direct_connect) { if (node->ri) { - return extend_info_from_router(node->ri); + return extend_info_from_router(node->ri, for_direct_connect); } else if (node->rs && node->md) { tor_addr_t addr; tor_addr_from_ipv4h(&addr, node->rs->addr); @@ -4841,10 +4855,11 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, static bridge_info_t * get_configured_bridge_by_routerinfo(const routerinfo_t *ri) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return get_configured_bridge_by_addr_port_digest(&addr, - ri->or_port, ri->cache_info.identity_digest); + tor_addr_port_t ap; + + router_get_pref_orport(ri, &ap); + return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, + ri->cache_info.identity_digest); } /** Return 1 if <b>ri</b> is one of our known bridges, else 0. */ @@ -4858,18 +4873,31 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) int node_is_a_configured_bridge(const node_t *node) { - tor_addr_t addr; - uint16_t orport; + int retval = 0; /* Negative. */ + smartlist_t *orports = NULL; + if (!node) - return 0; - if (node_get_addr(node, &addr) < 0) - return 0; - orport = node_get_orport(node); - if (orport == 0) - return 0; + goto out; + + orports = node_get_all_orports(node); + if (orports == NULL) + goto out; - return get_configured_bridge_by_addr_port_digest( - &addr, orport, node->identity) != NULL; + SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) { + if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port, + node->identity) != NULL) { + retval = 1; + goto out; + } + } SMARTLIST_FOREACH_END(orport); + + out: + if (orports != NULL) { + SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); + smartlist_free(orports); + orports = NULL; + } + return retval; } /** We made a connection to a router at <b>addr</b>:<b>port</b> @@ -5123,18 +5151,52 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) routerinfo_t *ri = node->ri; tor_addr_from_ipv4h(&addr, ri->addr); - if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) { + if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == ri->or_port) || + (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && + bridge->port == ri->ipv6_orport)) { /* they match, so no need to do anything */ } else { - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - tor_free(ri->address); - ri->address = tor_dup_ip(ri->addr); - ri->or_port = bridge->port; - log_info(LD_DIR, - "Adjusted bridge routerinfo for '%s' to match configured " - "address %s:%d.", - ri->nickname, ri->address, ri->or_port); + if (tor_addr_family(&bridge->addr) == AF_INET) { + ri->addr = tor_addr_to_ipv4h(&bridge->addr); + tor_free(ri->address); + ri->address = tor_dup_ip(ri->addr); + ri->or_port = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, ri->address, ri->or_port); + } else if (tor_addr_family(&bridge->addr) == AF_INET6) { + tor_addr_copy(&ri->ipv6_addr, &bridge->addr); + ri->ipv6_orport = bridge->port; + log_info(LD_DIR, + "Adjusted bridge routerinfo for '%s' to match configured " + "address %s:%d.", + ri->nickname, fmt_addr(&ri->ipv6_addr), ri->ipv6_orport); + } else { + log_err(LD_BUG, "Address family not supported: %d.", + tor_addr_family(&bridge->addr)); + return; + } + } + + /* Indicate that we prefer connecting to this bridge over the + protocol that the bridge address indicates. Last bridge + descriptor handled wins. */ + ri->ipv6_preferred = tor_addr_family(&bridge->addr) == AF_INET6; + + /* XXXipv6 we lack support for falling back to another address for + the same relay, warn the user */ + if (!tor_addr_is_null(&ri->ipv6_addr)) + { + tor_addr_port_t ap; + router_get_pref_orport(ri, &ap); + log_notice(LD_CONFIG, + "Bridge '%s' has both an IPv4 and an IPv6 address. " + "Will prefer using its %s address (%s:%d).", + ri->nickname, + ri->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addr(&ap.addr), ap.port); } } if (node->rs) { @@ -5179,8 +5241,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) rewrite_node_address_for_bridge(bridge, node); add_an_entry_guard(node, 1, 1); - log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname, - from_cache ? "cached" : "fresh"); + log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, + from_cache ? "cached" : "fresh", router_describe(ri)); /* set entry->made_contact so if it goes down we don't drop it from * our entry node list */ entry_guard_register_connect_status(ri->cache_info.identity_digest, diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 1052db6153..8df2748708 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -59,8 +59,10 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); extend_info_t *extend_info_alloc(const char *nickname, const char *digest, crypto_pk_env_t *onion_key, const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_router(const routerinfo_t *r); -extend_info_t *extend_info_from_node(const node_t *node); +extend_info_t *extend_info_from_router(const routerinfo_t *r, + int for_direct_connect); +extend_info_t *extend_info_from_node(const node_t *node, + int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); void extend_info_free(extend_info_t *info); const node_t *build_state_get_exit_node(cpath_build_state_t *state); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 23efe05348..ef4ac6faa3 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1004,7 +1004,6 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) void circuit_has_opened(origin_circuit_t *circ) { - int can_try_clearing_isolation = 0, tried_clearing_isolation = 0; control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0); /* Remember that this circuit has finished building. Now if we start @@ -1012,13 +1011,18 @@ circuit_has_opened(origin_circuit_t *circ) * to consider its build time. */ circ->has_opened = 1; - again: - switch (TO_CIRCUIT(circ)->purpose) { case CIRCUIT_PURPOSE_C_ESTABLISH_REND: rend_client_rendcirc_has_opened(circ); - can_try_clearing_isolation = 1; + /* Start building an intro circ if we don't have one yet. */ connection_ap_attach_pending(); + /* This isn't a call to circuit_try_attaching_streams because a + * circuit in _C_ESTABLISH_REND state isn't connected to its + * hidden service yet, thus we can't attach streams to it yet, + * thus circuit_try_attaching_streams would always clear the + * circuit's isolation state. circuit_try_attaching_streams is + * called later, when the rend circ enters _C_REND_JOINED + * state. */ break; case CIRCUIT_PURPOSE_C_INTRODUCING: rend_client_introcirc_has_opened(circ); @@ -1026,8 +1030,7 @@ circuit_has_opened(origin_circuit_t *circ) case CIRCUIT_PURPOSE_C_GENERAL: /* Tell any AP connections that have been waiting for a new * circuit that one is ready. */ - can_try_clearing_isolation = 1; - connection_ap_attach_pending(); + circuit_try_attaching_streams(circ); break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* at Bob, waiting for introductions */ @@ -1044,12 +1047,15 @@ circuit_has_opened(origin_circuit_t *circ) * This won't happen in normal operation, but might happen if the * controller did it. Just let it slide. */ } +} +/** If the stream-isolation state of <b>circ</b> can be cleared, clear + * it. Return non-zero iff <b>circ</b>'s isolation state was cleared. */ +static int +circuit_try_clearing_isolation_state(origin_circuit_t *circ) +{ if (/* The circuit may have become non-open if it was cannibalized.*/ circ->_base.state == CIRCUIT_STATE_OPEN && - /* Only if the purpose is clearable, and only if we haven't tried - * to clear isolation yet, do we try. */ - can_try_clearing_isolation && !tried_clearing_isolation && /* If !isolation_values_set, there is nothing to clear. */ circ->isolation_values_set && /* It's not legal to clear a circuit's isolation info if it's ever had @@ -1059,8 +1065,26 @@ circuit_has_opened(origin_circuit_t *circ) * we didn't manage to attach any streams to it, then we can * and should clear it and try again. */ circuit_clear_isolation(circ); - tried_clearing_isolation = 1; - goto again; + return 1; + } else { + return 0; + } +} + +/** Called when a circuit becomes ready for streams to be attached to + * it. */ +void +circuit_try_attaching_streams(origin_circuit_t *circ) +{ + /* Attach streams to this circuit if we can. */ + connection_ap_attach_pending(); + + /* The call to circuit_try_clearing_isolation_state here will do + * nothing and return 0 if we didn't attach any streams to circ + * above. */ + if (circuit_try_clearing_isolation_state(circ)) { + /* Maybe *now* we can attach some streams to this circuit. */ + connection_ap_attach_pending(); } } @@ -1439,7 +1463,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, int opt = conn->chosen_exit_optional; r = node_get_by_nickname(conn->chosen_exit_name, 1); if (r && node_has_descriptor(r)) { - extend_info = extend_info_from_node(r); + /* We might want to connect to an IPv6 bridge for loading + descriptors so we use the preferred address rather than + the primary. */ + extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0); } else { log_debug(LD_DIR, "considering %d, %s", want_onehop, conn->chosen_exit_name); @@ -1489,6 +1516,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, else new_circ_purpose = desired_circuit_purpose; + if (options->Tor2webMode && + (new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || + new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) { + want_onehop = 1; + } + { int flags = CIRCLAUNCH_NEED_CAPACITY; if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL; @@ -1684,7 +1717,7 @@ consider_recording_trackhost(const entry_connection_t *conn, addressmap_register(conn->socks_request->address, new_address, time(NULL) + options->TrackHostExitsExpire, - ADDRMAPSRC_TRACKEXIT); + ADDRMAPSRC_TRACKEXIT, 0, 0); } /** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 9867fd8205..bc11fe5d91 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -29,6 +29,7 @@ void reset_bandwidth_test(void); int circuit_enough_testing_circs(void); void circuit_has_opened(origin_circuit_t *circ); +void circuit_try_attaching_streams(origin_circuit_t *circ); void circuit_build_failed(origin_circuit_t *circ); /** Flag to set when a circuit should have only a single hop. */ diff --git a/src/or/config.c b/src/or/config.c index 06d7d5c022..be4113f41a 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -220,7 +220,7 @@ static config_var_t _option_vars[] = { V(ConstrainedSockSize, MEMUNIT, "8192"), V(ContactInfo, STRING, NULL), V(ControlListenAddress, LINELIST, NULL), - V(ControlPort, PORT, "0"), + V(ControlPort, LINELIST, NULL), V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), @@ -231,12 +231,13 @@ static config_var_t _option_vars[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), OBSOLETE("DebugLogFile"), - V(DirAllowPrivateAddresses, BOOL, NULL), + V(DisableNetwork, BOOL, "0"), + V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(DirListenAddress, LINELIST, NULL), OBSOLETE("DirFetchPeriod"), V(DirPolicy, LINELIST, NULL), - V(DirPort, PORT, "0"), + V(DirPort, LINELIST, NULL), V(DirPortFrontPage, FILENAME, NULL), OBSOLETE("DirPostPeriod"), OBSOLETE("DirRecordUsageByCountry"), @@ -246,7 +247,9 @@ static config_var_t _option_vars[] = { V(DirReqStatistics, BOOL, "1"), VAR("DirServer", LINELIST, DirServers, NULL), V(DisableAllSwap, BOOL, "0"), + V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), + V(DynamicDHGroups, BOOL, "1"), V(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@ -340,7 +343,7 @@ static config_var_t _option_vars[] = { V(NumCPUs, UINT, "0"), V(NumEntryGuards, UINT, "3"), V(ORListenAddress, LINELIST, NULL), - V(ORPort, PORT, "0"), + V(ORPort, LINELIST, NULL), V(OutboundBindAddress, STRING, NULL), OBSOLETE("PathlenCoinWeight"), V(PerConnBWBurst, MEMUNIT, "0"), @@ -394,6 +397,7 @@ static config_var_t _option_vars[] = { V(TestSocks, BOOL, "0"), OBSOLETE("TestVia"), V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"), + V(Tor2webMode, BOOL, "0"), V(TrackHostExits, CSV, NULL), V(TrackHostExitsExpire, INTERVAL, "30 minutes"), OBSOLETE("TrafficShaping"), @@ -405,6 +409,7 @@ static config_var_t _option_vars[] = { V(UseEntryGuards, BOOL, "1"), V(UseMicrodescriptors, AUTOBOOL, "auto"), V(User, STRING, NULL), + V(UserspaceIOCPBuffers, BOOL, "0"), VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"), VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), @@ -596,8 +601,11 @@ static int parse_dir_server_line(const char *line, dirinfo_type_t required_type, int validate_only); static void port_cfg_free(port_cfg_t *port); -static int parse_client_ports(const or_options_t *options, int validate_only, +static int parse_ports(const or_options_t *options, int validate_only, char **msg_out, int *n_ports_out); +static int check_server_ports(const smartlist_t *ports, + const or_options_t *options); + static int validate_data_directory(or_options_t *options); static int write_configuration_file(const char *fname, const or_options_t *options); @@ -610,9 +618,6 @@ static int or_state_validate(or_state_t *old_options, or_state_t *options, static int or_state_load(void); static int options_init_logs(or_options_t *options, int validate_only); -static int is_listening_on_low_port(int port_option, - const config_line_t *listen_options); - static uint64_t config_parse_memunit(const char *s, int *ok); static int config_parse_msec_interval(const char *s, int *ok); static int config_parse_interval(const char *s, int *ok); @@ -659,16 +664,20 @@ static const config_format_t state_format = { /** Command-line and config-file options. */ static or_options_t *global_options = NULL; +/** DOCDOC */ +static or_options_t *global_default_options = NULL; /** Name of most recently read torrc file. */ static char *torrc_fname = NULL; +/** DOCDOC */ +static char *torrc_defaults_fname; /** Persistent serialized state. */ static or_state_t *global_state = NULL; /** Configuration Options set by command line. */ static config_line_t *global_cmdline_options = NULL; /** Contents of most recently read DirPortFrontPage file. */ static char *global_dirfrontpagecontents = NULL; -/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */ -static smartlist_t *configured_client_ports = NULL; +/** List of port_cfg_t for all configured ports. */ +static smartlist_t *configured_ports = NULL; /** Return the contents of our frontpage string, or NULL if not configured. */ const char * @@ -804,6 +813,8 @@ config_free_all(void) { or_options_free(global_options); global_options = NULL; + or_options_free(global_default_options); + global_default_options = NULL; config_free(&state_format, global_state); global_state = NULL; @@ -811,14 +822,15 @@ config_free_all(void) config_free_lines(global_cmdline_options); global_cmdline_options = NULL; - if (configured_client_ports) { - SMARTLIST_FOREACH(configured_client_ports, + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, port_cfg_t *, p, tor_free(p)); - smartlist_free(configured_client_ports); - configured_client_ports = NULL; + smartlist_free(configured_ports); + configured_ports = NULL; } tor_free(torrc_fname); + tor_free(torrc_defaults_fname); tor_free(_version); tor_free(global_dirfrontpagecontents); } @@ -1064,7 +1076,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) #endif if (running_tor) { - int n_client_ports=0; + int n_ports=0; /* We need to set the connection limit before we can open the listeners. */ if (set_max_file_descriptors((unsigned)options->ConnLimit, &options->_ConnLimit) < 0) { @@ -1080,10 +1092,10 @@ options_act_reversible(const or_options_t *old_options, char **msg) libevent_initialized = 1; } - /* Adjust the client port configuration so we can launch listeners. */ - if (parse_client_ports(options, 0, msg, &n_client_ports)) { + /* Adjust the port configuration so we can launch listeners. */ + if (parse_ports(options, 0, msg, &n_ports)) { if (!*msg) - *msg = tor_strdup("Unexpected problem parsing client port config"); + *msg = tor_strdup("Unexpected problem parsing port config"); goto rollback; } @@ -1091,13 +1103,22 @@ options_act_reversible(const or_options_t *old_options, char **msg) consider_hibernation(time(NULL)); /* Launch the listeners. (We do this before we setuid, so we can bind to - * ports under 1024.) We don't want to rebind if we're hibernating. */ + * ports under 1024.) We don't want to rebind if we're hibernating. If + * networking is disabled, this will close all but the control listeners, + * but disable those. */ if (!we_are_hibernating()) { if (retry_all_listeners(replaced_listeners, new_listeners) < 0) { *msg = tor_strdup("Failed to bind one of the listener ports."); goto rollback; } } + if (options->DisableNetwork) { + /* Aggressively close non-controller stuff, NOW */ + log_notice(LD_NET, "DisableNetwork is set. Tor will not make or accept " + "non-control network connections. Shutting down all existing " + "connections."); + connection_mark_all_noncontrol_connections(); + } } #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) @@ -1266,6 +1287,24 @@ get_effective_bwburst(const or_options_t *options) return (uint32_t)bw; } +/** Return True if any changes from <b>old_options</b> to + * <b>new_options</b> needs us to refresh our TLS context. */ +static int +options_transition_requires_fresh_tls_context(const or_options_t *old_options, + const or_options_t *new_options) +{ + tor_assert(new_options); + + if (!old_options) + return 0; + + if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { + return 1; + } + + return 0; +} + /** Fetch the active option list, and take actions based on it. All of the * things we do should survive being done repeatedly. If present, * <b>old_options</b> contains the previous value of the options. @@ -1285,6 +1324,14 @@ options_act(const or_options_t *old_options) const int transition_affects_workers = old_options && options_transition_affects_workers(old_options, options); + /* disable ptrace and later, other basic debugging techniques */ + if (options->DisableDebuggerAttachment) { + tor_disable_debugger_attach(); + } else { + log_notice(LD_CONFIG,"Debugger attachment enabled " + "for unprivileged users."); + } + if (running_tor && !have_lockfile()) { if (try_locking(options, 1) < 0) return -1; @@ -1293,6 +1340,28 @@ options_act(const or_options_t *old_options) if (consider_adding_dir_authorities(options, old_options) < 0) return -1; +#ifdef NON_ANONYMOUS_MODE_ENABLED + log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a " + "non-anonymous mode. It will provide NO ANONYMITY."); +#endif + +#ifdef ENABLE_TOR2WEB_MODE + if (!options->Tor2webMode) { + log_err(LD_CONFIG, "This copy of Tor was compiled to run in " + "'tor2web mode'. It can only be run with the Tor2webMode torrc " + "option enabled."); + return -1; + } +#else + if (options->Tor2webMode) { + log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " + "'tor2web mode'. It cannot be run with the Tor2webMode torrc " + "option enabled. To enable Tor2webMode recompile with the " + "--enable-tor2webmode option."); + return -1; + } +#endif + if (options->Bridges) { mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { @@ -1361,6 +1430,29 @@ options_act(const or_options_t *old_options) finish_daemon(options->DataDirectory); } + /* If needed, generate a new TLS DH prime according to the current torrc. */ + if (server_mode(options)) { + if (!old_options) { + if (options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else { + crypto_set_tls_dh_prime(NULL); + } + } else { + if (options->DynamicDHGroups && !old_options->DynamicDHGroups) { + char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); + crypto_set_tls_dh_prime(fname); + tor_free(fname); + } else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) { + crypto_set_tls_dh_prime(NULL); + } + } + } else { /* clients don't need a dynamic DH prime. */ + crypto_set_tls_dh_prime(NULL); + } + /* We want to reinit keys as needed before we do much of anything else: keys are important, and other things can depend on them. */ if (transition_affects_workers || @@ -1370,6 +1462,13 @@ options_act(const or_options_t *old_options) log_warn(LD_BUG,"Error initializing keys; exiting"); return -1; } + } else if (old_options && + options_transition_requires_fresh_tls_context(old_options, + options)) { + if (router_initialize_tls_context() < 0) { + log_warn(LD_BUG,"Error initializing TLS context."); + return -1; + } } /* Write our PID to the PID file. If we do not have write permissions we @@ -1479,7 +1578,7 @@ options_act(const or_options_t *old_options) int was_relay = 0; if (options->BridgeRelay) { time_t int_start = time(NULL); - if (old_options->ORPort == options->ORPort) { + if (config_lines_eq(old_options->ORPort, options->ORPort)) { int_start += RELAY_BRIDGE_STATS_DELAY; was_relay = 1; } @@ -1718,7 +1817,11 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) int i = 1; while (i < argc) { + unsigned command = CONFIG_LINE_NORMAL; + int want_arg = 1; + if (!strcmp(argv[i],"-f") || + !strcmp(argv[i],"--defaults-torrc") || !strcmp(argv[i],"--hash-password")) { i += 2; /* command-line option with argument. ignore them. */ continue; @@ -1735,13 +1838,6 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) continue; } - if (i == argc-1) { - log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", - argv[i]); - config_free_lines(front); - return -1; - } - *new = tor_malloc_zero(sizeof(config_line_t)); s = argv[i]; @@ -1750,15 +1846,33 @@ config_get_commandlines(int argc, char **argv, config_line_t **result) s++; if (*s == '-') s++; + /* Figure out the command, if any. */ + if (*s == '+') { + s++; + command = CONFIG_LINE_APPEND; + } else if (*s == '/') { + s++; + command = CONFIG_LINE_CLEAR; + /* A 'clear' command has no argument. */ + want_arg = 0; + } + + if (want_arg && i == argc-1) { + log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.", + argv[i]); + config_free_lines(front); + return -1; + } (*new)->key = tor_strdup(expand_abbrev(&options_format, s, 1, 1)); - (*new)->value = tor_strdup(argv[i+1]); + (*new)->value = want_arg ? tor_strdup(argv[i+1]) : tor_strdup(""); + (*new)->command = command; (*new)->next = NULL; log(LOG_DEBUG, LD_CONFIG, "command line: parsed keyword '%s', value '%s'", (*new)->key, (*new)->value); new = &((*new)->next); - i += 2; + i += want_arg ? 2 : 1; } *result = front; return 0; @@ -1773,7 +1887,7 @@ config_line_append(config_line_t **lst, { config_line_t *newline; - newline = tor_malloc(sizeof(config_line_t)); + newline = tor_malloc_zero(sizeof(config_line_t)); newline->key = tor_strdup(key); newline->value = tor_strdup(val); newline->next = NULL; @@ -1786,9 +1900,12 @@ config_line_append(config_line_t **lst, /** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any - * misformatted lines. */ + * misformatted lines. + * + * If <b>extended</b> is set, then treat keys beginning with / and with + as + * indicating "clear" and "append" respectively. */ int -config_get_lines(const char *string, config_line_t **result) +config_get_lines(const char *string, config_line_t **result, int extended) { config_line_t *list = NULL, **next; char *k, *v; @@ -1804,13 +1921,30 @@ config_get_lines(const char *string, config_line_t **result) return -1; } if (k && v) { + unsigned command = CONFIG_LINE_NORMAL; + if (extended) { + if (k[0] == '+') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + command = CONFIG_LINE_APPEND; + } else if (k[0] == '/') { + char *k_new = tor_strdup(k+1); + tor_free(k); + k = k_new; + tor_free(v); + v = tor_strdup(""); + command = CONFIG_LINE_CLEAR; + } + } /* This list can get long, so we keep a pointer to the end of it * rather than using config_line_append over and over and getting * n^2 performance. */ - *next = tor_malloc(sizeof(config_line_t)); + *next = tor_malloc_zero(sizeof(config_line_t)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; + (*next)->command = command; next = &((*next)->next); } else { tor_free(k); @@ -2038,7 +2172,19 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, case CONFIG_TYPE_LINELIST: case CONFIG_TYPE_LINELIST_S: - config_line_append((config_line_t**)lvalue, c->key, c->value); + { + config_line_t *lastval = *(config_line_t**)lvalue; + if (lastval && lastval->fragile) { + if (c->command != CONFIG_LINE_APPEND) { + config_free_lines(lastval); + *(config_line_t**)lvalue = NULL; + } else { + lastval->fragile = 0; + } + } + + config_line_append((config_line_t**)lvalue, c->key, c->value); + } break; case CONFIG_TYPE_OBSOLETE: log_warn(LD_CONFIG, "Skipping obsolete configuration option '%s'", c->key); @@ -2054,6 +2200,28 @@ config_assign_value(const config_format_t *fmt, or_options_t *options, return 0; } +/** Mark every linelist in <b>options<b> "fragile", so that fresh assignments + * to it will replace old ones. */ +static void +config_mark_lists_fragile(const config_format_t *fmt, or_options_t *options) +{ + int i; + tor_assert(fmt); + tor_assert(options); + + for (i = 0; fmt->vars[i].name; ++i) { + const config_var_t *var = &fmt->vars[i]; + config_line_t *list; + if (var->type != CONFIG_TYPE_LINELIST && + var->type != CONFIG_TYPE_LINELIST_V) + continue; + + list = *(config_line_t **)STRUCT_VAR_P(options, var->var_offset); + if (list) + list->fragile = 1; + } +} + /** If <b>c</b> is a syntactically valid configuration line, update * <b>options</b> with its value and return 0. Otherwise return -1 for bad * key, -2 for bad value. @@ -2096,8 +2264,9 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, if (!strlen(c->value)) { /* reset or clear it, then return */ if (!clear_first) { - if (var->type == CONFIG_TYPE_LINELIST || - var->type == CONFIG_TYPE_LINELIST_S) { + if ((var->type == CONFIG_TYPE_LINELIST || + var->type == CONFIG_TYPE_LINELIST_S) && + c->command != CONFIG_LINE_CLEAR) { /* We got an empty linelist from the torrc or command line. As a special case, call this an error. Warn and ignore. */ log_warn(LD_CONFIG, @@ -2107,6 +2276,8 @@ config_assign_line(const config_format_t *fmt, or_options_t *options, } } return 0; + } else if (c->command == CONFIG_LINE_CLEAR && !clear_first) { + option_reset(fmt, options, var, use_defaults); } if (options_seen && (var->type != CONFIG_TYPE_LINELIST && @@ -2201,7 +2372,7 @@ config_lines_dup(const config_line_t *inp) config_line_t *result = NULL; config_line_t **next_out = &result; while (inp) { - *next_out = tor_malloc(sizeof(config_line_t)); + *next_out = tor_malloc_zero(sizeof(config_line_t)); (*next_out)->key = tor_strdup(inp->key); (*next_out)->value = tor_strdup(inp->value); inp = inp->next; @@ -2438,6 +2609,12 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list, list = list->next; } bitarray_free(options_seen); + + /** Now we're done assigning a group of options to the configuration. + * Subsequent group assignments should _replace_ linelists, not extend + * them. */ + config_mark_lists_fragile(fmt, options); + return 0; } @@ -2880,37 +3057,6 @@ options_init(or_options_t *options) config_init(&options_format, options); } -/* Check if the port number given in <b>port_option</b> in combination with - * the specified port in <b>listen_options</b> will result in Tor actually - * opening a low port (meaning a port lower than 1024). Return 1 if - * it is, or 0 if it isn't or the concept of a low port isn't applicable for - * the platform we're on. */ -static int -is_listening_on_low_port(int port_option, - const config_line_t *listen_options) -{ -#ifdef MS_WINDOWS - (void) port_option; - (void) listen_options; - return 0; /* No port is too low for windows. */ -#else - const config_line_t *l; - uint16_t p; - if (port_option == 0) - return 0; /* We're not listening */ - if (listen_options == NULL) - return (port_option < 1024); - - for (l = listen_options; l; l = l->next) { - addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p); - if (p<1024) { - return 1; - } - } - return 0; -#endif -} - /** Set all vars in the configuration object <b>options</b> to their default * values. */ static void @@ -2933,24 +3079,30 @@ config_init(const config_format_t *fmt, void *options) * Else, if comment_defaults, write default values as comments. */ static char * -config_dump(const config_format_t *fmt, const void *options, int minimal, +config_dump(const config_format_t *fmt, const void *default_options, + const void *options, int minimal, int comment_defaults) { smartlist_t *elements; - or_options_t *defaults; + const or_options_t *defaults = default_options; + void *defaults_tmp = NULL; config_line_t *line, *assigned; char *result; int i; char *msg = NULL; - defaults = config_alloc(fmt); - config_init(fmt, defaults); + if (defaults == NULL) { + defaults = defaults_tmp = config_alloc(fmt); + config_init(fmt, defaults_tmp); + } /* XXX use a 1 here so we don't add a new log line while dumping */ - if (fmt->validate_fn(NULL,defaults, 1, &msg) < 0) { - log_err(LD_BUG, "Failed to validate default config."); - tor_free(msg); - tor_assert(0); + if (default_options == NULL) { + if (fmt->validate_fn(NULL, defaults_tmp, 1, &msg) < 0) { + log_err(LD_BUG, "Failed to validate default config."); + tor_free(msg); + tor_assert(0); + } } elements = smartlist_create(); @@ -2992,7 +3144,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, result = smartlist_join_strings(elements, "", 0, NULL); SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); smartlist_free(elements); - config_free(fmt, defaults); + if (defaults_tmp) + config_free(fmt, defaults_tmp); return result; } @@ -3003,7 +3156,8 @@ config_dump(const config_format_t *fmt, const void *options, int minimal, char * options_dump(const or_options_t *options, int minimal) { - return config_dump(&options_format, options, minimal, 0); + return config_dump(&options_format, global_default_options, + options, minimal, 0); } /** Return 0 if every element of sl is a string holding a decimal @@ -3128,7 +3282,7 @@ options_validate(or_options_t *old_options, or_options_t *options, int i; config_line_t *cl; const char *uname = get_uname(); - int n_client_ports=0; + int n_ports=0; #define REJECT(arg) \ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END #define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END @@ -3146,13 +3300,7 @@ options_validate(or_options_t *old_options, or_options_t *options, "for details.", uname); } - if (options->ORPort == 0 && options->ORListenAddress != NULL) - REJECT("ORPort must be defined if ORListenAddress is defined."); - - if (options->DirPort == 0 && options->DirListenAddress != NULL) - REJECT("DirPort must be defined if DirListenAddress is defined."); - - if (parse_client_ports(options, 1, msg, &n_client_ports) < 0) + if (parse_ports(options, 1, msg, &n_ports) < 0) return -1; if (validate_data_directory(options)<0) @@ -3199,7 +3347,9 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Can't use a relative path to torrc when RunAsDaemon is set."); #endif - if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines) + /* XXXX require that the only port not be DirPort? */ + /* XXXX require that at least one port be listened-upon. */ + if (n_ports == 0 && !options->RendConfigLines) log(LOG_WARN, LD_CONFIG, "SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all " "undefined, and there aren't any hidden services configured. " @@ -3215,19 +3365,6 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } - if (options->AccountingMax && - (is_listening_on_low_port(options->ORPort, options->ORListenAddress) || - is_listening_on_low_port(options->DirPort, options->DirListenAddress))) - { - log(LOG_WARN, LD_CONFIG, - "You have set AccountingMax to use hibernation. You have also " - "chosen a low DirPort or OrPort. This combination can make Tor stop " - "working when it tries to re-attach the port after a period of " - "hibernation. Please choose a different port or turn off " - "hibernation unless you know this combination will work on your " - "platform."); - } - if (options->ExcludeExitNodes || options->ExcludeNodes) { options->_ExcludeExitNodesUnion = routerset_new(); routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeExitNodes); @@ -3429,6 +3566,10 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->UseBridges && options->EntryNodes) REJECT("You cannot set both UseBridges and EntryNodes."); + if (options->EntryNodes && !options->UseEntryGuards) + log_warn(LD_CONFIG, "EntryNodes is set, but UseEntryGuards is disabled. " + "EntryNodes will be ignored."); + options->_AllowInvalid = 0; if (options->AllowInvalidNodes) { SMARTLIST_FOREACH(options->AllowInvalidNodes, const char *, cp, { @@ -3481,7 +3622,8 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->BridgeRelay && options->DirPort) { log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling " "DirPort"); - options->DirPort = 0; + config_free_lines(options->DirPort); + options->DirPort = NULL; } if (options->MinUptimeHidServDirectoryV2 < 0) { @@ -3502,6 +3644,24 @@ options_validate(or_options_t *old_options, or_options_t *options, options->RendPostPeriod = MAX_DIR_PERIOD; } + if (options->Tor2webMode && options->LearnCircuitBuildTimeout) { + /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in + * two ways: + * + * - LearnCircuitBuildTimeout results in a low CBT, which + * Tor2webMode's use of one-hop rendezvous circuits lowers + * much further, producing *far* too many timeouts. + * + * - The adaptive CBT code does not update its timeout estimate + * using build times for single-hop circuits. + * + * If we fix both of these issues someday, we should test + * Tor2webMode with LearnCircuitBuildTimeout on again. */ + log_notice(LD_CONFIG,"Tor2webMode is enabled; turning " + "LearnCircuitBuildTimeout off."); + options->LearnCircuitBuildTimeout = 0; + } + if (options->MaxCircuitDirtiness < MIN_MAX_CIRCUIT_DIRTINESS) { log_warn(LD_CONFIG, "MaxCircuitDirtiness option is too short; " "raising to %d seconds.", MIN_MAX_CIRCUIT_DIRTINESS); @@ -3704,39 +3864,6 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->ControlListenAddress) { - int all_are_local = 1; - config_line_t *ln; - for (ln = options->ControlListenAddress; ln; ln = ln->next) { - if (strcmpstart(ln->value, "127.")) - all_are_local = 0; - } - if (!all_are_local) { - if (!options->HashedControlPassword && - !options->HashedControlSessionPassword && - !options->CookieAuthentication) { - log_warn(LD_CONFIG, - "You have a ControlListenAddress set to accept " - "unauthenticated connections from a non-local address. " - "This means that programs not running on your computer " - "can reconfigure your Tor, without even having to guess a " - "password. That's so bad that I'm closing your ControlPort " - "for you. If you need to control your Tor remotely, try " - "enabling authentication and using a tool like stunnel or " - "ssh to encrypt remote access."); - options->ControlPort = 0; - } else { - log_warn(LD_CONFIG, "You have a ControlListenAddress set to accept " - "connections from a non-local address. This means that " - "programs not running on your computer can reconfigure your " - "Tor. That's pretty bad, since the controller " - "protocol isn't encrypted! Maybe you should just listen on " - "127.0.0.1 and use a tool like stunnel or ssh to encrypt " - "remote connections to your control port."); - } - } - } - if (options->ControlPort && !options->HashedControlPassword && !options->HashedControlSessionPassword && !options->CookieAuthentication) { @@ -3959,8 +4086,9 @@ options_validate(or_options_t *old_options, or_options_t *options, } }); - if (options->BridgeRelay == 1 && options->ORPort == 0) - REJECT("BridgeRelay is 1, ORPort is 0. This is an invalid combination."); + if (options->BridgeRelay == 1 && ! options->ORPort) + REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid " + "combination."); return 0; #undef REJECT @@ -4051,7 +4179,7 @@ options_transition_affects_workers(const or_options_t *old_options, { if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) || old_options->NumCPUs != new_options->NumCPUs || - old_options->ORPort != new_options->ORPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || old_options->ServerDNSSearchDomains != new_options->ServerDNSSearchDomains || old_options->_SafeLogging != new_options->_SafeLogging || @@ -4081,9 +4209,10 @@ options_transition_affects_descriptor(const or_options_t *old_options, !config_lines_eq(old_options->ExitPolicy,new_options->ExitPolicy) || old_options->ExitPolicyRejectPrivate != new_options->ExitPolicyRejectPrivate || - old_options->ORPort != new_options->ORPort || - old_options->DirPort != new_options->DirPort || + !config_lines_eq(old_options->ORPort, new_options->ORPort) || + !config_lines_eq(old_options->DirPort, new_options->DirPort) || old_options->ClientOnly != new_options->ClientOnly || + old_options->DisableNetwork != new_options->DisableNetwork || old_options->_PublishServerDescriptor != new_options->_PublishServerDescriptor || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || @@ -4157,17 +4286,25 @@ get_windows_conf_root(void) } #endif -/** Return the default location for our torrc file. */ +/** Return the default location for our torrc file. + * DOCDOC defaults_file */ static const char * -get_default_conf_file(void) +get_default_conf_file(int defaults_file) { #ifdef MS_WINDOWS - static char path[MAX_PATH+1]; - strlcpy(path, get_windows_conf_root(), MAX_PATH); - strlcat(path,"\\torrc",MAX_PATH); - return path; + if (defaults_file) { + static char defaults_path[MAX_PATH+1]; + tor_snprintf(defaults_path, MAX_PATH, "%s\\torrc-defaults", + get_windows_conf_root()); + return defaults_path; + } else { + static char path[MAX_PATH+1]; + tor_snprintf(path, MAX_PATH, "%s\\torrc", + get_windows_conf_root()); + return path; + } #else - return (CONFDIR "/torrc"); + return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc"; #endif } @@ -4200,37 +4337,54 @@ check_nickname_list(const char *lst, const char *name, char **msg) return r; } -/** Learn config file name from command line arguments, or use the default */ +/** Learn config file name from command line arguments, or use the default, + * DOCDOC defaults_file */ static char * find_torrc_filename(int argc, char **argv, + int defaults_file, int *using_default_torrc, int *ignore_missing_torrc) { char *fname=NULL; int i; + const char *fname_opt = defaults_file ? "--defaults-torrc" : "-f"; + const char *ignore_opt = defaults_file ? NULL : "--ignore-missing-torrc"; + + if (defaults_file) + *ignore_missing_torrc = 1; for (i = 1; i < argc; ++i) { - if (i < argc-1 && !strcmp(argv[i],"-f")) { + if (i < argc-1 && !strcmp(argv[i],fname_opt)) { if (fname) { - log(LOG_WARN, LD_CONFIG, "Duplicate -f options on command line."); + log(LOG_WARN, LD_CONFIG, "Duplicate %s options on command line.", + fname_opt); tor_free(fname); } fname = expand_filename(argv[i+1]); + + { + char *absfname; + absfname = make_path_absolute(fname); + tor_free(fname); + fname = absfname; + } + *using_default_torrc = 0; ++i; - } else if (!strcmp(argv[i],"--ignore-missing-torrc")) { + } else if (ignore_opt && !strcmp(argv[i],ignore_opt)) { *ignore_missing_torrc = 1; } } if (*using_default_torrc) { /* didn't find one, try CONFDIR */ - const char *dflt = get_default_conf_file(); + const char *dflt = get_default_conf_file(defaults_file); if (dflt && file_status(dflt) == FN_FILE) { fname = tor_strdup(dflt); } else { #ifndef MS_WINDOWS - char *fn; - fn = expand_filename("~/.torrc"); + char *fn = NULL; + if (!defaults_file) + fn = expand_filename("~/.torrc"); if (fn && file_status(fn) == FN_FILE) { fname = fn; } else { @@ -4245,31 +4399,34 @@ find_torrc_filename(int argc, char **argv, return fname; } -/** Load torrc from disk, setting torrc_fname if successful */ +/** Load torrc from disk, setting torrc_fname if successful. + * DOCDOC defaults_file */ static char * -load_torrc_from_disk(int argc, char **argv) +load_torrc_from_disk(int argc, char **argv, int defaults_file) { char *fname=NULL; char *cf = NULL; int using_default_torrc = 1; int ignore_missing_torrc = 0; + char **fname_var = defaults_file ? &torrc_defaults_fname : &torrc_fname; - fname = find_torrc_filename(argc, argv, + fname = find_torrc_filename(argc, argv, defaults_file, &using_default_torrc, &ignore_missing_torrc); tor_assert(fname); log(LOG_DEBUG, LD_CONFIG, "Opening config file \"%s\"", fname); - tor_free(torrc_fname); - torrc_fname = fname; + tor_free(*fname_var); + *fname_var = fname; /* Open config file */ if (file_status(fname) != FN_FILE || !(cf = read_file_to_str(fname,0,NULL))) { - if (using_default_torrc == 1 || ignore_missing_torrc ) { - log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " - "using reasonable defaults.", fname); + if (using_default_torrc == 1 || ignore_missing_torrc) { + if (!defaults_file) + log(LOG_NOTICE, LD_CONFIG, "Configuration file \"%s\" not present, " + "using reasonable defaults.", fname); tor_free(fname); /* sets fname to NULL */ - torrc_fname = NULL; + *fname_var = NULL; cf = tor_strdup(""); } else { log(LOG_WARN, LD_CONFIG, @@ -4283,7 +4440,7 @@ load_torrc_from_disk(int argc, char **argv) return cf; err: tor_free(fname); - torrc_fname = NULL; + *fname_var = NULL; return NULL; } @@ -4294,8 +4451,9 @@ load_torrc_from_disk(int argc, char **argv) int options_init_from_torrc(int argc, char **argv) { - char *cf=NULL; - int i, retval, command; + char *cf=NULL, *cf_defaults=NULL; + int i, command; + int retval = -1; static char **backup_argv; static int backup_argc; char *command_arg = NULL; @@ -4354,24 +4512,24 @@ options_init_from_torrc(int argc, char **argv) if (command == CMD_HASH_PASSWORD) { cf = tor_strdup(""); } else { - cf = load_torrc_from_disk(argc, argv); + cf_defaults = load_torrc_from_disk(argc, argv, 1); + cf = load_torrc_from_disk(argc, argv, 0); if (!cf) goto err; } - retval = options_init_from_string(cf, command, command_arg, &errmsg); - tor_free(cf); - if (retval < 0) - goto err; - - return 0; + retval = options_init_from_string(cf_defaults, cf, command, command_arg, + &errmsg); err: + + tor_free(cf); + tor_free(cf_defaults); if (errmsg) { log(LOG_WARN,LD_CONFIG,"%s", errmsg); tor_free(errmsg); } - return -1; + return retval < 0 ? -1 : 0; } /** Load the options from the configuration in <b>cf</b>, validate @@ -4384,13 +4542,13 @@ options_init_from_torrc(int argc, char **argv) * * -4 for error while setting the new options */ setopt_err_t -options_init_from_string(const char *cf, +options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg) { - or_options_t *oldoptions, *newoptions; + or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL; config_line_t *cl; - int retval; + int retval, i; setopt_err_t err = SETOPT_ERR_MISC; tor_assert(msg); @@ -4403,17 +4561,24 @@ options_init_from_string(const char *cf, newoptions->command = command; newoptions->command_arg = command_arg; - /* get config lines, assign them */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } /* Go through command-line variables too */ @@ -4448,6 +4613,8 @@ options_init_from_string(const char *cf, /* Clear newoptions and re-initialize them with new defaults. */ config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); + newdefaultoptions = NULL; newoptions = tor_malloc_zero(sizeof(or_options_t)); newoptions->_magic = OR_OPTIONS_MAGIC; options_init(newoptions); @@ -4455,22 +4622,24 @@ options_init_from_string(const char *cf, newoptions->command_arg = command_arg; /* Assign all options a second time. */ - retval = config_get_lines(cf, &cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); - config_free_lines(cl); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; - } - retval = config_assign(&options_format, newoptions, - global_cmdline_options, 0, 0, msg); - if (retval < 0) { - err = SETOPT_ERR_PARSE; - goto err; + for (i = 0; i < 2; ++i) { + const char *body = i==0 ? cf_defaults : cf; + if (!body) + continue; + /* get config lines, assign them */ + retval = config_get_lines(body, &cl, 1); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + retval = config_assign(&options_format, newoptions, cl, 0, 0, msg); + config_free_lines(cl); + if (retval < 0) { + err = SETOPT_ERR_PARSE; + goto err; + } + if (i==0) + newdefaultoptions = options_dup(&options_format, newoptions); } } @@ -4489,11 +4658,14 @@ options_init_from_string(const char *cf, err = SETOPT_ERR_SETTING; goto err; /* frees and replaces old options */ } + config_free(&options_format, global_default_options); + global_default_options = newdefaultoptions; return SETOPT_OK; err: config_free(&options_format, newoptions); + config_free(&options_format, newdefaultoptions); if (*msg) { char *old_msg = *msg; tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg); @@ -4505,12 +4677,14 @@ options_init_from_string(const char *cf, /** Return the location for our configuration file. */ const char * -get_torrc_fname(void) +get_torrc_fname(int defaults_fname) { - if (torrc_fname) - return torrc_fname; + const char *fname = defaults_fname ? torrc_defaults_fname : torrc_fname; + + if (fname) + return fname; else - return get_default_conf_file(); + return get_default_conf_file(defaults_fname); } /** Adjust the address map based on the MapAddress elements in the @@ -4526,24 +4700,60 @@ config_register_addressmaps(const or_options_t *options) addressmap_clear_configured(); elts = smartlist_create(); for (opt = options->AddressMap; opt; opt = opt->next) { + int from_wildcard = 0, to_wildcard = 0; smartlist_split_string(elts, opt->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); - if (smartlist_len(elts) >= 2) { - from = smartlist_get(elts,0); - to = smartlist_get(elts,1); - if (address_is_invalid_destination(to, 1)) { - log_warn(LD_CONFIG, - "Skipping invalid argument '%s' to MapAddress", to); - } else { - addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC); - if (smartlist_len(elts)>2) { - log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); - } - } - } else { + if (smartlist_len(elts) < 2) { log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.", opt->value); + goto cleanup; } + + from = smartlist_get(elts,0); + to = smartlist_get(elts,1); + + if (to[0] == '.' || from[0] == '.') { + log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a" + "'.'. Ignoring.",opt->value); + goto cleanup; + } + + if (!strcmp(to, "*") || !strcmp(from, "*")) { + log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from " + "or to *. Ignoring.",opt->value); + goto cleanup; + } + /* Detect asterisks in expressions of type: '*.example.com' */ + if (!strncmp(from,"*.",2)) { + from += 2; + from_wildcard = 1; + } + if (!strncmp(to,"*.",2)) { + to += 2; + to_wildcard = 1; + } + + if (to_wildcard && !from_wildcard) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress: " + "can only use wildcard (i.e. '*.') if 'from' address " + "uses wildcard also", opt->value); + goto cleanup; + } + + if (address_is_invalid_destination(to, 1)) { + log_warn(LD_CONFIG, + "Skipping invalid argument '%s' to MapAddress", opt->value); + goto cleanup; + } + + addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC, + from_wildcard, to_wildcard); + + if (smartlist_len(elts) > 2) + log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress."); + + cleanup: SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); smartlist_clear(elts); } @@ -5115,7 +5325,7 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type, * clause once Tor 0.1.2.17 is obsolete. */ log_warn(LD_CONFIG, "Dangerous dirserver line. To correct, erase your " "torrc file (%s), or reinstall Tor and use the default torrc.", - get_torrc_fname()); + get_torrc_fname(0)); goto err; } if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) { @@ -5179,12 +5389,53 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) } SMARTLIST_FOREACH_END(port); } -#define CL_PORT_NO_OPTIONS (1u<<0) +/** DOCDOC */ +static void +warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) +{ + int warned = 0; + SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { + if (port->type != CONN_TYPE_CONTROL_LISTENER) + continue; + if (port->is_unix_addr) + continue; + if (!tor_addr_is_loopback(&port->addr)) { + if (forbid) { + if (!warned) + log_warn(LD_CONFIG, + "You have a ControlPort set to accept " + "unauthenticated connections from a non-local address. " + "This means that programs not running on your computer " + "can reconfigure your Tor, without even having to guess a " + "password. That's so bad that I'm closing your ControlPort " + "for you. If you need to control your Tor remotely, try " + "enabling authentication and using a tool like stunnel or " + "ssh to encrypt remote access."); + warned = 1; + port_cfg_free(port); + SMARTLIST_DEL_CURRENT(ports, port); + } else { + log_warn(LD_CONFIG, "You have a ControlPort set to accept " + "connections from a non-local address. This means that " + "programs not running on your computer can reconfigure your " + "Tor. That's pretty bad, since the controller " + "protocol isn't encrypted! Maybe you should just listen on " + "127.0.0.1 and use a tool like stunnel or ssh to encrypt " + "remote connections to your control port."); + return; /* No point in checking the rest */ + } + } + } SMARTLIST_FOREACH_END(port); +} + +#define CL_PORT_NO_OPTIONS (1u<<0) #define CL_PORT_WARN_NONLOCAL (1u<<1) #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) +#define CL_PORT_SERVER_OPTIONS (1u<<3) +#define CL_PORT_FORBID_NONLOCAL (1u<<4) /** - * Parse port configuration for a single client port type. + * Parse port configuration for a single port type. * * Read entries of the "FooPort" type from the list <b>ports</b>, and * entries of the "FooListenAddress" type from the list @@ -5204,17 +5455,22 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) * isolation options in the FooPort entries. * * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the - * ports are not on a local address. + * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, + * this is a contrl port with no password set: don't even allow it. * * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn * if FooListenAddress is set but FooPort is 0. * + * If CL_PORT_SERVER_OPTIONS is set in <b>flags</b>, do not allow stream + * isolation options in the FooPort entries; instead allow the + * server-port option set. + * * On success, if <b>out</b> is given, add a new port_cfg_t entry to * <b>out</b> for every port that the client should listen on. Return 0 * on success, -1 on failure. */ static int -parse_client_port_config(smartlist_t *out, +parse_port_config(smartlist_t *out, const config_line_t *ports, const config_line_t *listenaddrs, const char *portname, @@ -5225,8 +5481,11 @@ parse_client_port_config(smartlist_t *out, { smartlist_t *elts; int retval = -1; - const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS); + const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); + const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS; + const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; + const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; const unsigned allow_spurious_listenaddr = flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; @@ -5262,6 +5521,17 @@ parse_client_port_config(smartlist_t *out, return -1; } + if (use_server_options && out) { + /* Add a no_listen port. */ + port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); + cfg->type = listener_type; + cfg->port = mainport; + tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ + cfg->no_listen = 1; + cfg->ipv4_only = 1; + smartlist_add(out, cfg); + } + for (; listenaddrs; listenaddrs = listenaddrs->next) { tor_addr_t addr; uint16_t port = 0; @@ -5277,12 +5547,17 @@ parse_client_port_config(smartlist_t *out, tor_addr_copy(&cfg->addr, &addr); cfg->session_group = SESSION_GROUP_UNSET; cfg->isolation_flags = ISO_DEFAULT; + cfg->no_advertise = 1; smartlist_add(out, cfg); } } - if (warn_nonlocal && out) - warn_nonlocal_client_ports(out, portname); + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } return 0; } /* end if (listenaddrs) */ @@ -5314,6 +5589,8 @@ parse_client_port_config(smartlist_t *out, char *addrport; uint16_t ptmp=0; int ok; + int no_listen = 0, no_advertise = 0, all_addrs = 0, + ipv4_only = 0, ipv6_only = 0; smartlist_split_string(elts, ports->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); @@ -5322,7 +5599,7 @@ parse_client_port_config(smartlist_t *out, goto err; } - if (!allow_client_options && smartlist_len(elts) > 1) { + if (allow_no_options && smartlist_len(elts) > 1) { log_warn(LD_CONFIG, "Too many options on %sPort line", portname); goto err; } @@ -5361,56 +5638,107 @@ parse_client_port_config(smartlist_t *out, } /* Now parse the rest of the options, if any. */ - SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { - int no = 0, isoflag = 0; - const char *elt_orig = elt; - if (elt_sl_idx == 0) - continue; /* Skip addr:port */ - if (!strcasecmpstart(elt, "SessionGroup=")) { - int group = (int)tor_parse_long(elt+strlen("SessionGroup="), - 10, 0, INT_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, "Invalid %sPort option '%s'", + if (use_server_options) { + /* This is a server port; parse advertising options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + if (elt_sl_idx == 0) + continue; /* Skip addr:port */ + + if (!strcasecmp(elt, "NoAdvertise")) { + no_advertise = 1; + } else if (!strcasecmp(elt, "NoListen")) { + no_listen = 1; +#if 0 + /* not implemented yet. */ + } else if (!strcasecmp(elt, "AllAddrs")) { + + all_addrs = 1; +#endif + } else if (!strcasecmp(elt, "IPv4Only")) { + ipv4_only = 1; + } else if (!strcasecmp(elt, "IPv6Only")) { + ipv6_only = 1; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", portname, escaped(elt)); - goto err; } - if (sessiongroup >= 0) { - log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", - portname); - goto err; - } - sessiongroup = group; - continue; - } + } SMARTLIST_FOREACH_END(elt); - if (!strcasecmpstart(elt, "No")) { - no = 1; - elt += 2; + if (no_advertise && no_listen) { + log_warn(LD_CONFIG, "Tried to set both NoListen and NoAdvertise " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; } - if (!strcasecmpend(elt, "s")) - elt[strlen(elt)-1] = '\0'; /* kill plurals. */ - - if (!strcasecmp(elt, "IsolateDestPort")) { - isoflag = ISO_DESTPORT; - } else if (!strcasecmp(elt, "IsolateDestAddr")) { - isoflag = ISO_DESTADDR; - } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { - isoflag = ISO_SOCKSAUTH; - } else if (!strcasecmp(elt, "IsolateClientProtocol")) { - isoflag = ISO_CLIENTPROTO; - } else if (!strcasecmp(elt, "IsolateClientAddr")) { - isoflag = ISO_CLIENTADDR; - } else { - log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", - portname, escaped(elt_orig)); + if (ipv4_only && ipv6_only) { + log_warn(LD_CONFIG, "Tried to set both IPv4Only and IPv6Only " + "on %sPort line '%s'", + portname, escaped(ports->value)); + goto err; } - - if (no) { - isolation &= ~isoflag; - } else { - isolation |= isoflag; + if (ipv4_only && tor_addr_family(&addr) == AF_INET6) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; } - } SMARTLIST_FOREACH_END(elt); + if (ipv6_only && tor_addr_family(&addr) == AF_INET) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + portname); + goto err; + } + } else { + /* This is a client port; parse isolation options */ + SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { + int no = 0, isoflag = 0; + const char *elt_orig = elt; + if (elt_sl_idx == 0) + continue; /* Skip addr:port */ + if (!strcasecmpstart(elt, "SessionGroup=")) { + int group = (int)tor_parse_long(elt+strlen("SessionGroup="), + 10, 0, INT_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, "Invalid %sPort option '%s'", + portname, escaped(elt)); + goto err; + } + if (sessiongroup >= 0) { + log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort", + portname); + goto err; + } + sessiongroup = group; + continue; + } + + if (!strcasecmpstart(elt, "No")) { + no = 1; + elt += 2; + } + if (!strcasecmpend(elt, "s")) + elt[strlen(elt)-1] = '\0'; /* kill plurals. */ + + if (!strcasecmp(elt, "IsolateDestPort")) { + isoflag = ISO_DESTPORT; + } else if (!strcasecmp(elt, "IsolateDestAddr")) { + isoflag = ISO_DESTADDR; + } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) { + isoflag = ISO_SOCKSAUTH; + } else if (!strcasecmp(elt, "IsolateClientProtocol")) { + isoflag = ISO_CLIENTPROTO; + } else if (!strcasecmp(elt, "IsolateClientAddr")) { + isoflag = ISO_CLIENTADDR; + } else { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt_orig)); + } + + if (no) { + isolation &= ~isoflag; + } else { + isolation |= isoflag; + } + } SMARTLIST_FOREACH_END(elt); + } if (out && port) { port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); @@ -5419,14 +5747,24 @@ parse_client_port_config(smartlist_t *out, tor_addr_copy(&cfg->addr, &addr); cfg->session_group = sessiongroup; cfg->isolation_flags = isolation; + cfg->no_listen = no_listen; + cfg->no_listen = no_advertise; + cfg->all_addrs = all_addrs; + cfg->ipv4_only = ipv4_only; + cfg->ipv6_only = ipv6_only; + smartlist_add(out, cfg); } SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); } - if (warn_nonlocal && out) - warn_nonlocal_client_ports(out, portname); + if (warn_nonlocal && out) { + if (is_control) + warn_nonlocal_controller_ports(out, forbid_nonlocal); + else + warn_nonlocal_client_ports(out, portname); + } retval = 0; err: @@ -5435,6 +5773,27 @@ parse_client_port_config(smartlist_t *out, return retval; } +/** DOCDOC */ +static int +parse_socket_config(smartlist_t *out, const config_line_t *cfg, + int listener_type) +{ + + if (!out) + return 0; + + for ( ; cfg; cfg = cfg->next) { + size_t len = strlen(cfg->value); + port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); + port->is_unix_addr = 1; + memcpy(port->unix_addr, cfg->value, len+1); + port->type = listener_type; + smartlist_add(out, port); + } + + return 0; +} + /** Parse all client port types (Socks, DNS, Trans, NATD) from * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of * ports that are listed and return 0. On failure, set *<b>msg</b> to a @@ -5444,8 +5803,8 @@ parse_client_port_config(smartlist_t *out, * new list of ports parsed from <b>options</b>. **/ static int -parse_client_ports(const or_options_t *options, int validate_only, - char **msg, int *n_ports_out) +parse_ports(const or_options_t *options, int validate_only, + char **msg, int *n_ports_out) { smartlist_t *ports; int retval = -1; @@ -5454,7 +5813,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *n_ports_out = 0; - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->SocksPort, options->SocksListenAddress, "Socks", CONN_TYPE_AP_LISTENER, "127.0.0.1", 9050, @@ -5462,7 +5821,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->DNSPort, options->DNSListenAddress, "DNS", CONN_TYPE_AP_DNS_LISTENER, "127.0.0.1", 0, @@ -5470,7 +5829,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->TransPort, options->TransListenAddress, "Trans", CONN_TYPE_AP_TRANS_LISTENER, "127.0.0.1", 0, @@ -5478,7 +5837,7 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration"); goto err; } - if (parse_client_port_config(ports, + if (parse_port_config(ports, options->NATDPort, options->NATDListenAddress, "NATD", CONN_TYPE_AP_NATD_LISTENER, "127.0.0.1", 0, @@ -5486,16 +5845,63 @@ parse_client_ports(const or_options_t *options, int validate_only, *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration"); goto err; } + { + unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL; + const int any_passwords = (options->HashedControlPassword || + options->HashedControlSessionPassword || + options->CookieAuthentication); + if (! any_passwords) + control_port_flags |= CL_PORT_FORBID_NONLOCAL; + + if (parse_port_config(ports, + options->ControlPort, options->ControlListenAddress, + "Control", CONN_TYPE_CONTROL_LISTENER, + "127.0.0.1", 0, + control_port_flags) < 0) { + *msg = tor_strdup("Invalid ControlPort/ControlListenAddress " + "configuration"); + goto err; + } + if (parse_socket_config(ports, + options->ControlSocket, + CONN_TYPE_CONTROL_LISTENER) < 0) { + *msg = tor_strdup("Invalid ControlSocket configuration"); + goto err; + } + } + if (! options->ClientOnly) { + if (parse_port_config(ports, + options->ORPort, options->ORListenAddress, + "OR", CONN_TYPE_OR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration"); + goto err; + } + if (parse_port_config(ports, + options->DirPort, options->DirListenAddress, + "Dir", CONN_TYPE_DIR_LISTENER, + "0.0.0.0", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid DirPort/DirListenAddress configuration"); + goto err; + } + } + + if (check_server_ports(ports, options) < 0) { + *msg = tor_strdup("Misconfigured server ports"); + goto err; + } *n_ports_out = smartlist_len(ports); if (!validate_only) { - if (configured_client_ports) { - SMARTLIST_FOREACH(configured_client_ports, + if (configured_ports) { + SMARTLIST_FOREACH(configured_ports, port_cfg_t *, p, port_cfg_free(p)); - smartlist_free(configured_client_ports); + smartlist_free(configured_ports); } - configured_client_ports = ports; + configured_ports = ports; ports = NULL; /* prevent free below. */ } @@ -5508,14 +5914,107 @@ parse_client_ports(const or_options_t *options, int validate_only, return retval; } +/** DOCDOC */ +static int +check_server_ports(const smartlist_t *ports, + const or_options_t *options) +{ + int n_orport_advertised = 0; + int n_orport_advertised_ipv4 = 0; + int n_orport_listeners = 0; + int n_dirport_advertised = 0; + int n_dirport_listeners = 0; + int n_low_port = 0; + int r = 0; + + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { + if (port->type == CONN_TYPE_DIR_LISTENER) { + if (! port->no_advertise) + ++n_dirport_advertised; + if (! port->no_listen) + ++n_dirport_listeners; + } else if (port->type == CONN_TYPE_OR_LISTENER) { + if (! port->no_advertise) { + ++n_orport_advertised; + if (tor_addr_family(&port->addr) == AF_INET || + (tor_addr_family(&port->addr) == AF_UNSPEC && !port->ipv6_only)) + ++n_orport_advertised_ipv4; + } + if (! port->no_listen) + ++n_orport_listeners; + } else { + continue; + } +#ifndef MS_WINDOWS + if (!port->no_advertise && port->port < 1024) + ++n_low_port; +#endif + } SMARTLIST_FOREACH_END(port); + + if (n_orport_advertised && !n_orport_listeners) { + log_warn(LD_CONFIG, "We are advertising an ORPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised && !n_dirport_listeners) { + log_warn(LD_CONFIG, "We are advertising a DirPort, but not actually " + "listening on one."); + r = -1; + } + if (n_dirport_advertised > 1) { + log_warn(LD_CONFIG, "Can't advertise more than one DirPort."); + r = -1; + } + if (n_orport_advertised && !n_orport_advertised_ipv4 && + !options->BridgeRelay) { + log_warn(LD_CONFIG, "Configured non-bridge only to listen on an IPv6 " + "address."); + r = -1; + } + + if (n_low_port && options->AccountingMax) { + log(LOG_WARN, LD_CONFIG, + "You have set AccountingMax to use hibernation. You have also " + "chosen a low DirPort or OrPort. This combination can make Tor stop " + "working when it tries to re-attach the port after a period of " + "hibernation. Please choose a different port or turn off " + "hibernation unless you know this combination will work on your " + "platform."); + } + + return r; +} + /** Return a list of port_cfg_t for client ports parsed from the * options. */ const smartlist_t * -get_configured_client_ports(void) +get_configured_ports(void) { - if (!configured_client_ports) - configured_client_ports = smartlist_create(); - return configured_client_ports; + if (!configured_ports) + configured_ports = smartlist_create(); + return configured_ports; +} + +/** Return the first advertised port of type <b>listener_type</b> in + <b>address_family</b>. */ +int +get_first_advertised_port_by_type_af(int listener_type, int address_family) +{ + if (!configured_ports) + return 0; + SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + if (cfg->type == listener_type && + !cfg->no_advertise && + (tor_addr_family(&cfg->addr) == address_family || + tor_addr_family(&cfg->addr) == AF_UNSPEC)) { + if (tor_addr_family(&cfg->addr) != AF_UNSPEC || + (address_family == AF_INET && !cfg->ipv6_only) || + (address_family == AF_INET6 && !cfg->ipv4_only)) { + return cfg->port; + } + } + } SMARTLIST_FOREACH_END(cfg); + return 0; } /** Adjust the value of options->DataDirectory, or fill it in if it's @@ -5674,7 +6173,7 @@ options_save_current(void) * If we try falling back to datadirectory or something, we have a better * chance of saving the configuration, but a better chance of doing * something the user never expected. */ - return write_configuration_file(get_torrc_fname(), get_options()); + return write_configuration_file(get_torrc_fname(0), get_options()); } /** Mapping from a unit name to a multiplier for converting that unit into a @@ -5893,7 +6392,7 @@ init_libevent(const or_options_t *options) suppress_libevent_log_msg(NULL); tor_check_libevent_version(tor_libevent_get_method(), - get_options()->ORPort != 0, + get_options()->ORPort != NULL, &badness); if (badness) { const char *v = tor_libevent_get_version_str(); @@ -6140,7 +6639,7 @@ or_state_load(void) if (contents) { config_line_t *lines=NULL; int assign_retval; - if (config_get_lines(contents, &lines)<0) + if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); @@ -6244,7 +6743,7 @@ or_state_save(time_t now) tor_free(global_state->TorVersion); tor_asprintf(&global_state->TorVersion, "Tor %s", get_version()); - state = config_dump(&state_format, global_state, 1, 0); + state = config_dump(&state_format, NULL, global_state, 1, 0); format_local_iso_time(tbuf, now); tor_asprintf(&contents, "# Tor state file last generated on %s local time\n" @@ -6341,21 +6840,29 @@ get_transport_bindaddr(const char *line, const char *transport) return NULL; } -/** Return a static string containing the address:port a proxy - * transport should bind on. */ -const char * +/** Return a string containing the address:port that a proxy transport + * should bind on. The string is stored on the heap and must be freed + * by the caller of this function. */ +char * get_bindaddr_for_transport(const char *transport) { - static const char default_addrport[] = "127.0.0.1:0"; - const char *bindaddr = NULL; + char *default_addrport = NULL; + const char *stored_bindaddr = NULL; config_line_t *line = get_transport_in_state_by_name(transport); - if (!line) - return default_addrport; - - bindaddr = get_transport_bindaddr(line->value, transport); - - return bindaddr ? bindaddr : default_addrport; + if (!line) /* Found no references in state for this transport. */ + goto no_bindaddr_found; + + stored_bindaddr = get_transport_bindaddr(line->value, transport); + if (stored_bindaddr) /* found stored bindaddr in state file. */ + return tor_strdup(stored_bindaddr); + + no_bindaddr_found: + /** If we didn't find references for this pluggable transport in the + state file, we should instruct the pluggable transport proxy to + listen on INADDR_ANY on a random ephemeral port. */ + tor_asprintf(&default_addrport, "%s:%s", fmt_addr32(INADDR_ANY), "0"); + return default_addrport; } /** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to diff --git a/src/or/config.h b/src/or/config.h index 76f6841d70..88258c133e 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -23,24 +23,24 @@ const char *escaped_safe_str_client(const char *address); const char *escaped_safe_str(const char *address); const char *get_version(void); -int config_get_lines(const char *string, config_line_t **result); +int config_get_lines(const char *string, config_line_t **result, int extended); void config_free_lines(config_line_t *front); setopt_err_t options_trial_assign(config_line_t *list, int use_defaults, int clear_first, char **msg); int resolve_my_address(int warn_severity, const or_options_t *options, uint32_t *addr, char **hostname_out); -int is_local_addr(const tor_addr_t *addr) ATTR_PURE; +int is_local_addr(const tor_addr_t *addr); void options_init(or_options_t *options); char *options_dump(const or_options_t *options, int minimal); int options_init_from_torrc(int argc, char **argv); -setopt_err_t options_init_from_string(const char *cf, +setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, int command, const char *command_arg, char **msg); int option_is_recognized(const char *key); const char *option_get_canonical_name(const char *key); config_line_t *option_get_assignment(const or_options_t *options, const char *key); int options_save_current(void); -const char *get_torrc_fname(void); +const char *get_torrc_fname(int defaults_fname); char *options_get_datadir_fname2_suffix(const or_options_t *options, const char *sub1, const char *sub2, const char *suffix); @@ -64,14 +64,20 @@ or_state_t *get_or_state(void); int did_last_state_file_write_fail(void); int or_state_save(time_t now); -const smartlist_t *get_configured_client_ports(void); +const smartlist_t *get_configured_ports(void); +int get_first_advertised_port_by_type_af(int listener_type, + int address_family); +#define get_primary_or_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) +#define get_primary_dir_port() \ + (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET)) int options_need_geoip_info(const or_options_t *options, const char **reason_out); void save_transport_to_state(const char *transport_name, const tor_addr_t *addr, uint16_t port); -const char *get_bindaddr_for_transport(const char *transport); +char *get_bindaddr_for_transport(const char *transport); int getinfo_helper_config(control_connection_t *conn, const char *question, char **answer, diff --git a/src/or/connection.c b/src/or/connection.c index a52bf48078..a6d5d8f1e4 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -880,7 +880,8 @@ connection_create_listener(const struct sockaddr *listensockaddr, return NULL; } - if (listensockaddr->sa_family == AF_INET) { + if (listensockaddr->sa_family == AF_INET || + listensockaddr->sa_family == AF_INET6) { int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); if (is_tcp) start_reading = 1; @@ -890,7 +891,7 @@ connection_create_listener(const struct sockaddr *listensockaddr, log_notice(LD_NET, "Opening %s on %s:%d", conn_type_to_string(type), fmt_addr(&addr), usePort); - s = tor_open_socket(PF_INET, + s = tor_open_socket(tor_addr_family(&addr), is_tcp ? SOCK_STREAM : SOCK_DGRAM, is_tcp ? IPPROTO_TCP: IPPROTO_UDP); if (!SOCKET_OK(s)) { @@ -1050,7 +1051,12 @@ connection_create_listener(const struct sockaddr *listensockaddr, } /** Do basic sanity checking on a newly received socket. Return 0 - * if it looks ok, else return -1. */ + * if it looks ok, else return -1. + * + * Notably, some TCP stacks can erroneously have accept() return successfully + * with socklen 0, when the client sends an RST before the accept call (as + * nmap does). We want to detect that, and not go on with the connection. + */ static int check_sockaddr(struct sockaddr *sa, int len, int level) { @@ -1116,14 +1122,14 @@ connection_handle_listener_read(connection_t *conn, int new_type) tor_socket_t news; /* the new socket */ connection_t *newconn; /* information about the remote peer when connecting to other routers */ - char addrbuf[256]; - struct sockaddr *remote = (struct sockaddr*)addrbuf; + struct sockaddr_storage addrbuf; + struct sockaddr *remote = (struct sockaddr*)&addrbuf; /* length of the remote address. Must be whatever accept() needs. */ socklen_t remotelen = (socklen_t)sizeof(addrbuf); const or_options_t *options = get_options(); tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in)); - memset(addrbuf, 0, sizeof(addrbuf)); + memset(&addrbuf, 0, sizeof(addrbuf)); news = tor_accept_socket(conn->s,remote,&remotelen); if (!SOCKET_OK(news)) { /* accept() error */ @@ -1160,21 +1166,9 @@ connection_handle_listener_read(connection_t *conn, int new_type) uint16_t port; if (check_sockaddr(remote, remotelen, LOG_INFO)<0) { log_info(LD_NET, - "accept() returned a strange address; trying getsockname()."); - remotelen=sizeof(addrbuf); - memset(addrbuf, 0, sizeof(addrbuf)); - if (getsockname(news, remote, &remotelen)<0) { - int e = tor_socket_errno(news); - log_warn(LD_NET, "getsockname() for new connection failed: %s", - tor_socket_strerror(e)); - } else { - if (check_sockaddr((struct sockaddr*)addrbuf, remotelen, - LOG_WARN) < 0) { - log_warn(LD_NET,"Something's wrong with this conn. Closing it."); - tor_close_socket(news); - return 0; - } - } + "accept() returned a strange address; closing connection."); + tor_close_socket(news); + return 0; } if (check_sockaddr_family_match(remote->sa_family, conn) < 0) { @@ -1302,7 +1296,7 @@ connection_connect(connection_t *conn, const char *address, { tor_socket_t s; int inprogress = 0; - char addrbuf[256]; + struct sockaddr_storage addrbuf; struct sockaddr *dest_addr; int dest_addr_len; const or_options_t *options = get_options(); @@ -1318,6 +1312,24 @@ connection_connect(connection_t *conn, const char *address, else protocol_family = PF_INET; + if (get_options()->DisableNetwork) { + /* We should never even try to connect anyplace if DisableNetwork is set. + * Warn if we do, and refuse to make the connection. */ + static ratelim_t disablenet_violated = RATELIM_INIT(30*60); + char *m; +#ifdef MS_WINDOWS + *socket_error = WSAENETUNREACH; +#else + *socket_error = ENETUNREACH; +#endif + if ((m = rate_limit_log(&disablenet_violated, approx_time()))) { + log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m); + tor_free(m); + } + tor_fragile_assert(); + return -1; + } + s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP); if (s < 0) { *socket_error = tor_socket_errno(-1); @@ -1352,8 +1364,8 @@ connection_connect(connection_t *conn, const char *address, if (options->ConstrainedSockets) set_constrained_socket_buffers(s, (int)options->ConstrainedSockSize); - memset(addrbuf,0,sizeof(addrbuf)); - dest_addr = (struct sockaddr*) addrbuf; + memset(&addrbuf,0,sizeof(addrbuf)); + dest_addr = (struct sockaddr*) &addrbuf; dest_addr_len = tor_addr_to_sockaddr(addr, port, dest_addr, sizeof(addrbuf)); tor_assert(dest_addr_len > 0); @@ -1796,6 +1808,9 @@ retry_listener_ports(smartlist_t *old_conns, (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr)) continue; + if (wanted->no_listen) + continue; /* We don't want to open a listener for this one */ + if (wanted->is_unix_addr) { if (conn->socket_family == AF_UNIX && !strcmp(wanted->unix_addr, conn->address)) { @@ -1834,6 +1849,8 @@ retry_listener_ports(smartlist_t *old_conns, connection_t *conn; int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port; tor_assert(real_port <= UINT16_MAX); + if (port->no_listen) + continue; if (port->is_unix_addr) { listensockaddr = (struct sockaddr *) @@ -1870,82 +1887,6 @@ retry_listener_ports(smartlist_t *old_conns, return r; } -/** - * Launch any configured listener connections of type <b>type</b>. (A - * listener is configured if <b>port_option</b> is non-zero. If any - * ListenAddress configuration options are given in <b>cfg</b>, create a - * connection binding to each one. Otherwise, create a single - * connection binding to the address <b>default_addr</b>.) - * - * We assume that we're starting with a list of existing listener connection_t - * pointers in <b>old_conns</b>: we do not launch listeners that are already - * in that list. Instead, we just remove them from the list. - * - * All new connections we launch are added to <b>new_conns</b>. - */ -static int -retry_listeners(smartlist_t *old_conns, - int type, const config_line_t *cfg, - int port_option, const char *default_addr, - smartlist_t *new_conns, - int is_sockaddr_un) -{ - smartlist_t *ports = smartlist_create(); - tor_addr_t dflt_addr; - int retval = 0; - - if (default_addr) { - tor_addr_parse(&dflt_addr, default_addr); - } else { - tor_addr_make_unspec(&dflt_addr); - } - - if (port_option) { - if (!cfg) { - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t)); - tor_addr_copy(&port->addr, &dflt_addr); - port->port = port_option; - port->type = type; - smartlist_add(ports, port); - } else { - const config_line_t *c; - for (c = cfg; c; c = c->next) { - port_cfg_t *port; - tor_addr_t addr; - uint16_t portval = 0; - if (is_sockaddr_un) { - size_t len = strlen(c->value); - port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); - port->is_unix_addr = 1; - memcpy(port->unix_addr, c->value, len+1); - } else { - if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) { - log_warn(LD_CONFIG, "Can't parse/resolve %s %s", - c->key, c->value); - retval = -1; - continue; - } - port = tor_malloc_zero(sizeof(port_cfg_t)); - tor_addr_copy(&port->addr, &addr); - } - port->type = type; - port->port = portval ? portval : port_option; - smartlist_add(ports, port); - } - } - } - - if (retval == -1) - goto cleanup; - - retval = retry_listener_ports(old_conns, ports, new_conns); - - cleanup: - SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p)); - smartlist_free(ports); - return retval; -} - /** Launch listeners for each port you should have open. Only launch * listeners who are not already open, and only close listeners we no longer * want. @@ -1968,35 +1909,10 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_add(listeners, conn); } SMARTLIST_FOREACH_END(conn); - if (! options->ClientOnly) { - if (retry_listeners(listeners, - CONN_TYPE_OR_LISTENER, options->ORListenAddress, - options->ORPort, "0.0.0.0", - new_conns, 0) < 0) - retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_DIR_LISTENER, options->DirListenAddress, - options->DirPort, "0.0.0.0", - new_conns, 0) < 0) - retval = -1; - } - if (retry_listener_ports(listeners, - get_configured_client_ports(), + get_configured_ports(), new_conns) < 0) retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_CONTROL_LISTENER, - options->ControlListenAddress, - options->ControlPort, "127.0.0.1", - new_conns, 0) < 0) - retval = -1; - if (retry_listeners(listeners, - CONN_TYPE_CONTROL_LISTENER, - options->ControlSocket, - options->ControlSocket ? 1 : 0, NULL, - new_conns, 1) < 0) - retval = -1; /* Any members that were still in 'listeners' don't correspond to * any configured port. Kill 'em. */ @@ -2013,6 +1929,7 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_free(listeners); + /* XXXprop186 should take all advertised ports into account */ if (old_or_port != router_get_advertised_or_port(options) || old_dir_port != router_get_advertised_dir_port(options, 0)) { /* Our chosen ORPort or DirPort is not what it used to be: the @@ -2025,6 +1942,43 @@ retry_all_listeners(smartlist_t *replaced_conns, return retval; } +/** Mark every listener of type other than CONTROL_LISTENER to be closed. */ +void +connection_mark_all_noncontrol_listeners(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + if (conn->type == CONN_TYPE_CONTROL_LISTENER) + continue; + if (connection_is_listener(conn)) + connection_mark_for_close(conn); + } SMARTLIST_FOREACH_END(conn); +} + +/** Mark every external conection not used for controllers for close. */ +void +connection_mark_all_noncontrol_connections(void) +{ + SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { + if (conn->marked_for_close) + continue; + switch (conn->type) { + case CONN_TYPE_CPUWORKER: + case CONN_TYPE_CONTROL_LISTENER: + case CONN_TYPE_CONTROL: + break; + case CONN_TYPE_AP: + connection_mark_unattached_ap(TO_ENTRY_CONN(conn), + END_STREAM_REASON_HIBERNATING); + break; + default: + connection_mark_for_close(conn); + break; + } + } SMARTLIST_FOREACH_END(conn); +} + /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 * otherwise. * Right now this just checks if it's an internal IP address or an @@ -2999,6 +2953,9 @@ connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg) { connection_t *conn = arg; (void) bufev; + if (conn->marked_for_close) + return; + if (event & BEV_EVENT_CONNECTED) { tor_assert(connection_state_is_connecting(conn)); if (connection_finished_connecting(conn)<0) @@ -3176,8 +3133,7 @@ connection_handle_write_impl(connection_t *conn, int force) /* Sometimes, "writable" means "connected". */ if (connection_state_is_connecting(conn)) { if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, (void*)&e, &len) < 0) { - log_warn(LD_BUG, - "getsockopt() syscall failed?! Please report to tor-ops."); + log_warn(LD_BUG, "getsockopt() syscall failed"); if (CONN_IS_EDGE(conn)) connection_edge_end_errno(TO_EDGE_CONN(conn)); connection_mark_for_close(conn); diff --git a/src/or/connection.h b/src/or/connection.h index 9f11489727..c4b8bf8abe 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type, int retry_all_listeners(smartlist_t *replaced_conns, smartlist_t *new_conns); +void connection_mark_all_noncontrol_listeners(void); +void connection_mark_all_noncontrol_connections(void); + ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); void connection_bucket_init(void); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index efaad79b6a..aba9ba2727 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -803,11 +803,18 @@ connection_ap_detach_retriable(entry_connection_t *conn, * the configuration file, "1" for mappings set from the control * interface, and other values for DNS and TrackHostExit mappings that can * expire.) + * + * A mapping may be 'wildcarded'. If "src_wildcard" is true, then + * any address that ends with a . followed by the key for this entry will + * get remapped by it. If "dst_wildcard" is also true, then only the + * matching suffix of such addresses will get replaced by new_address. */ typedef struct { char *new_address; time_t expires; addressmap_entry_source_t source:3; + unsigned src_wildcard:1; + unsigned dst_wildcard:1; short num_resolve_failures; } addressmap_entry_t; @@ -1054,6 +1061,37 @@ addressmap_free_all(void) virtaddress_reversemap = NULL; } +/** Try to find a match for AddressMap expressions that use + * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or + * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c). + * Return the matching entry in AddressMap or NULL if no match is found. + * For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d' + * to 'a' before we return the matching AddressMap entry. + * + * This function does not handle the case where a pattern of the form "*.c.d" + * matches the address c.d -- that's done by the main addressmap_rewrite + * function. + */ +static addressmap_entry_t * +addressmap_match_superdomains(char *address) +{ + addressmap_entry_t *val; + char *cp; + + cp = address; + while ((cp = strchr(cp, '.'))) { + /* cp now points to a suffix of address that begins with a . */ + val = strmap_get_lc(addressmap, cp+1); + if (val && val->src_wildcard) { + if (val->dst_wildcard) + *cp = '\0'; + return val; + } + ++cp; + } + return NULL; +} + /** Look at address, and rewrite it until it doesn't want any * more rewrites; but don't get into an infinite loop. * Don't write more than maxlen chars into address. Return true if the @@ -1066,25 +1104,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out) { addressmap_entry_t *ent; int rewrites; - char *cp; time_t expires = TIME_MAX; for (rewrites = 0; rewrites < 16; rewrites++) { + int exact_match = 0; + char *addr_orig = tor_strdup(escaped_safe_str_client(address)); + ent = strmap_get(addressmap, address); if (!ent || !ent->new_address) { + ent = addressmap_match_superdomains(address); + } else { + if (ent->src_wildcard && !ent->dst_wildcard && + !strcasecmp(address, ent->new_address)) { + /* This is a rule like *.example.com example.com, and we just got + * "example.com" */ + tor_free(addr_orig); + if (expires_out) + *expires_out = expires; + return rewrites > 0; + } + + exact_match = 1; + } + + if (!ent || !ent->new_address) { + tor_free(addr_orig); if (expires_out) *expires_out = expires; return (rewrites > 0); /* done, no rewrite needed */ } - cp = tor_strdup(escaped_safe_str_client(ent->new_address)); + if (ent->dst_wildcard && !exact_match) { + strlcat(address, ".", maxlen); + strlcat(address, ent->new_address, maxlen); + } else { + strlcpy(address, ent->new_address, maxlen); + } + log_info(LD_APP, "Addressmap: rewriting %s to %s", - escaped_safe_str_client(address), cp); + addr_orig, escaped_safe_str_client(address)); if (ent->expires > 1 && ent->expires < expires) expires = ent->expires; - tor_free(cp); - strlcpy(address, ent->new_address, maxlen); + tor_free(addr_orig); } log_warn(LD_CONFIG, "Loop detected: we've rewritten %s 16 times! Using it as-is.", @@ -1148,17 +1210,34 @@ addressmap_have_mapping(const char *address, int update_expiry) * <b>new_address</b> should be a newly dup'ed string, which we'll use or * free as appropriate. We will leave address alone. * - * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove - * any mappings that exist from <b>address</b>. - */ + * If <b>wildcard_addr</b> is true, then the mapping will match any address + * equal to <b>address</b>, or any address ending with a period followed by + * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are + * both true, the mapping will rewrite addresses that end with + * ".<b>address</b>" into ones that end with ".<b>new_address</b>." + * + * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to + * <b>address</b> and <b>wildcard_addr</b> is equal to + * <b>wildcard_new_addr</b>, remove any mappings that exist from + * <b>address</b>. + * + * + * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is + * not set. */ void addressmap_register(const char *address, char *new_address, time_t expires, - addressmap_entry_source_t source) + addressmap_entry_source_t source, + const int wildcard_addr, + const int wildcard_new_addr) { addressmap_entry_t *ent; + if (wildcard_new_addr) + tor_assert(wildcard_addr); + ent = strmap_get(addressmap, address); - if (!new_address || !strcasecmp(address,new_address)) { + if (!new_address || (!strcasecmp(address,new_address) && + wildcard_addr == wildcard_new_addr)) { /* Remove the mapping, if any. */ tor_free(new_address); if (ent) { @@ -1193,6 +1272,8 @@ addressmap_register(const char *address, char *new_address, time_t expires, ent->expires = expires==2 ? 1 : expires; ent->num_resolve_failures = 0; ent->source = source; + ent->src_wildcard = wildcard_addr ? 1 : 0; + ent->dst_wildcard = wildcard_new_addr ? 1 : 0; log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'", safe_str_client(address), @@ -1277,7 +1358,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name, "%s", name); } addressmap_register(extendedaddress, tor_strdup(extendedval), - time(NULL) + ttl, ADDRMAPSRC_DNS); + time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0); } /** Record the fact that <b>address</b> resolved to <b>val</b>. @@ -1529,7 +1610,7 @@ addressmap_register_virtual_address(int type, char *new_address) log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address); if (vent_needs_to_be_added) strmap_set(virtaddress_reversemap, new_address, vent); - addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP); + addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0); #if 0 { @@ -1892,6 +1973,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, return -1; } + if (options->Tor2webMode) { + log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s " + "because tor2web mode is enabled.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + if (socks->command == SOCKS_COMMAND_RESOLVE) { uint32_t answer; struct in_addr in; @@ -2453,7 +2542,9 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) begin_type = ap_conn->use_begindir ? RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN; if (begin_type == RELAY_COMMAND_BEGIN) { +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(circ->build_state->onehop_tunnel == 0); +#endif } if (connection_edge_send_command(edge_conn, begin_type, @@ -2593,7 +2684,7 @@ connection_ap_make_link(connection_t *partner, want_onehop ? "direct" : "anonymized", safe_str_client(address), port); - conn = entry_connection_new(CONN_TYPE_AP, AF_INET); + conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr)); base_conn = ENTRY_TO_CONN(conn); base_conn->linked = 1; /* so that we can add it safely below. */ @@ -3199,7 +3290,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn) exitconn->_base.state = EXIT_CONN_STATE_OPEN; - dirconn = dir_connection_new(AF_INET); + dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr)); tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr); dirconn->_base.port = 0; diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 830667e601..47c9c45b1a 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -78,7 +78,8 @@ int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out); int addressmap_have_mapping(const char *address, int update_timeout); void addressmap_register(const char *address, char *new_address, - time_t expires, addressmap_entry_source_t source); + time_t expires, addressmap_entry_source_t source, + int address_wildcard, int new_address_wildcard); int parse_virtual_addr_network(const char *val, int validate_only, char **msg); int client_dns_incr_failures(const char *address); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cce99e4d65..7609138e68 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -626,7 +626,9 @@ connection_or_update_token_buckets(smartlist_t *conns, /** If we don't necessarily know the router we're connecting to, but we * have an addr/port/id_digest, then fill in as much as we can. Start - * by checking to see if this describes a router we know. */ + * by checking to see if this describes a router we know. + * <b>started_here</b> is 1 if we are the initiator of <b>conn</b> and + * 0 if it's an incoming connection. */ void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, @@ -641,10 +643,11 @@ connection_or_init_conn_from_address(or_connection_t *conn, tor_addr_copy(&conn->_base.addr, addr); tor_addr_copy(&conn->real_addr, addr); if (r) { - tor_addr_t node_addr; - node_get_addr(r, &node_addr); - /* XXXX proposal 118 will make this more complex. */ - if (tor_addr_eq(&conn->_base.addr, &node_addr)) + tor_addr_port_t node_ap; + node_get_pref_orport(r, &node_ap); + /* XXXX proposal 186 is making this more complex. For now, a conn + is canonical when it uses the _preferred_ address. */ + if (tor_addr_eq(&conn->_base.addr, &node_ap.addr)) conn->is_canonical = 1; if (!started_here) { /* Override the addr/port, so our log messages will make sense. @@ -657,12 +660,12 @@ connection_or_init_conn_from_address(or_connection_t *conn, * right IP address and port 56244, that wouldn't be as helpful. now we * log the "right" port too, so we know if it's moria1 or moria2. */ - tor_addr_copy(&conn->_base.addr, &node_addr); - conn->_base.port = node_get_orport(r); + tor_addr_copy(&conn->_base.addr, &node_ap.addr); + conn->_base.port = node_ap.port; } conn->nickname = tor_strdup(node_get_nickname(r)); tor_free(conn->_base.address); - conn->_base.address = tor_dup_addr(&node_addr); + conn->_base.address = tor_dup_addr(&node_ap.addr); } else { const char *n; /* If we're an authoritative directory server, we may know a @@ -1029,7 +1032,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port, return NULL; } - conn = or_connection_new(AF_INET); + conn = or_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); @@ -1096,12 +1099,13 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving) conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING; tor_assert(!conn->tls); conn->tls = tor_tls_new(conn->_base.s, receiving); - tor_tls_set_logged_address(conn->tls, // XXX client and relay? - escaped_safe_str(conn->_base.address)); if (!conn->tls) { log_warn(LD_BUG,"tor_tls_new failed. Closing."); return -1; } + tor_tls_set_logged_address(conn->tls, // XXX client and relay? + escaped_safe_str(conn->_base.address)); + #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(TO_CONN(conn))) { const int filtering = get_options()->_UseFilteringSSLBufferevents; @@ -1485,7 +1489,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn, control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, END_OR_CONN_REASON_OR_IDENTITY); if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY); + control_event_bootstrap_problem( + "Unexpected identity in router certificate", + END_OR_CONN_REASON_OR_IDENTITY); as_expected = 0; } if (authdir_mode_tests_reachability(options)) { diff --git a/src/or/control.c b/src/or/control.c index 42eaed2765..c9d3765ad3 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -737,7 +737,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body, SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); smartlist_free(entries); - if (config_get_lines(config, &lines) < 0) { + if (config_get_lines(config, &lines, 0) < 0) { log_warn(LD_CONTROL,"Controller gave us config lines we can't parse."); connection_write_str_to_buf("551 Couldn't parse configuration\r\n", conn); @@ -883,7 +883,7 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len, const char *msg = NULL; (void) len; - retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring); + retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring); if (retval != SETOPT_OK) log_warn(LD_CONTROL, @@ -1331,7 +1331,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len, smartlist_add(reply, ans); } } else { - addressmap_register(from, tor_strdup(to), 1, ADDRMAPSRC_CONTROLLER); + addressmap_register(from, tor_strdup(to), 1, + ADDRMAPSRC_CONTROLLER, 0, 0); tor_snprintf(ans, anslen, "250-%s", line); smartlist_add(reply, ans); } @@ -1378,7 +1379,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, if (!strcmp(question, "version")) { *answer = tor_strdup(get_version()); } else if (!strcmp(question, "config-file")) { - *answer = tor_strdup(get_torrc_fname()); + *answer = tor_strdup(get_torrc_fname(0)); + } else if (!strcmp(question, "config-defaults-file")) { + *answer = tor_strdup(get_torrc_fname(1)); } else if (!strcmp(question, "config-text")) { *answer = options_dump(get_options(), 1); } else if (!strcmp(question, "info/names")) { @@ -2106,6 +2109,7 @@ typedef struct getinfo_item_t { static const getinfo_item_t getinfo_items[] = { ITEM("version", misc, "The current version of Tor."), ITEM("config-file", misc, "Current location of the \"torrc\" file."), + ITEM("config-defaults-file", misc, "Current location of the defaults file."), ITEM("config-text", misc, "Return the string that would be written by a saveconf command."), ITEM("accounting/bytes", accounting, @@ -2481,7 +2485,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, /* now circ refers to something that is ready to be extended */ SMARTLIST_FOREACH(nodes, const node_t *, node, { - extend_info_t *info = extend_info_from_node(node); + extend_info_t *info = extend_info_from_node(node, 0); tor_assert(info); /* True, since node_has_descriptor(node) == true */ circuit_append_new_exit(circ, info); extend_info_free(info); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index 914003790a..e4460c5981 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -347,6 +347,7 @@ spawn_cpuworker(void) /* set up conn so it's got all the data we need to remember */ conn->s = fd; conn->address = tor_strdup("localhost"); + tor_addr_make_unspec(&conn->addr); if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for cpuworker failed. Giving up."); diff --git a/src/or/directory.c b/src/or/directory.c index 776b7a25f9..12636bac33 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -410,7 +410,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, } else { /* Otherwise it might be a consensus we don't parse, but which we * do cache. Look at the cached copy, perhaps. */ - cached_dir_t *cd = dirserv_get_consensus(resource ? resource : "ns"); + cached_dir_t *cd = dirserv_get_consensus(resource); if (cd) if_modified_since = cd->published + 180; } @@ -907,8 +907,12 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose)); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(is_sensitive_dir_purpose(dir_purpose) && !anonymized_connection)); +#else + (void)is_sensitive_dir_purpose; +#endif /* ensure that we don't make direct connections when a SOCKS server is * configured. */ @@ -919,7 +923,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr, return; } - conn = dir_connection_new(AF_INET); + conn = dir_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ tor_addr_copy(&conn->_base.addr, &addr); @@ -1619,9 +1623,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (!reason) reason = tor_strdup("[no reason given]"); log_debug(LD_DIR, - "Received response from directory server '%s:%d': %d %s", + "Received response from directory server '%s:%d': %d %s " + "(purpose: %d)", conn->_base.address, conn->_base.port, status_code, - escaped(reason)); + escaped(reason), + conn->_base.purpose); /* now check if it's got any hints for us about our IP address. */ if (conn->dirconn_direct) { @@ -3729,7 +3735,7 @@ download_status_reset(download_status_t *dls) const int *schedule; size_t schedule_len; - find_dl_schedule_and_len(dls, get_options()->DirPort, + find_dl_schedule_and_len(dls, get_options()->DirPort != NULL, &schedule, &schedule_len); dls->n_download_failures = 0; diff --git a/src/or/directory.h b/src/or/directory.h index 8c63bb5dfd..5050f700d2 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -80,7 +80,7 @@ time_t download_status_increment_failure(download_status_t *dls, * the optional status code <b>sc</b>. */ #define download_status_failed(dls, sc) \ download_status_increment_failure((dls), (sc), NULL, \ - get_options()->DirPort, time(NULL)) + get_options()->DirPort!=NULL, time(NULL)) void download_status_reset(download_status_t *dls); static int download_status_is_ready(download_status_t *dls, time_t now, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index be62459b16..f4bbca8500 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -232,7 +232,7 @@ dirserv_load_fingerprint_file(void) } tor_free(fname); - result = config_get_lines(cf, &front); + result = config_get_lines(cf, &front, 0); tor_free(cf); if (result < 0) { log_warn(LD_CONFIG, "Error reading from fingerprint file"); @@ -1212,7 +1212,7 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); - if (options->DirPort == 0 && !refuseunknown) + if (options->DirPort == NULL && !refuseunknown) return 0; if (!server_mode(options) || !advertised_server_mode()) return 0; @@ -1248,7 +1248,7 @@ directory_fetches_dir_info_later(const or_options_t *options) int directory_caches_v2_dir_info(const or_options_t *options) { - return options->DirPort != 0; + return options->DirPort != NULL; } /** Return 1 if we want to keep descriptors, networkstatuses, etc around @@ -1273,7 +1273,7 @@ directory_caches_dir_info(const or_options_t *options) int directory_permits_begindir_requests(const or_options_t *options) { - return options->BridgeRelay != 0 || options->DirPort != 0; + return options->BridgeRelay != 0 || options->DirPort != NULL; } /** Return 1 if we want to allow controllers to ask us directory @@ -1282,7 +1282,7 @@ directory_permits_begindir_requests(const or_options_t *options) int directory_permits_controller_requests(const or_options_t *options) { - return options->DirPort != 0; + return options->DirPort != NULL; } /** Return 1 if we have no need to fetch new descriptors. This generally diff --git a/src/or/dirvote.c b/src/or/dirvote.c index bf34c62af3..01e2358c44 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -50,7 +50,7 @@ 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 11 +#define MAX_SUPPORTED_CONSENSUS_METHOD 12 /** Lowest consensus method that contains a 'directory-footer' marker */ #define MIN_METHOD_FOR_FOOTER 9 @@ -64,6 +64,10 @@ static char *make_consensus_method_list(int low, int high, const char *sep); /** 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 + /* ===== * Voting * =====*/ @@ -608,11 +612,16 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning) return result; } +/** Minimum number of directory authorities voting for a parameter to + * include it in the consensus, if consensus method 12 or later is to be + * used. See proposal 178 for details. */ +#define MIN_VOTES_FOR_PARAM 3 + /** Helper: given a list of valid networkstatus_t, return a new string * containing the contents of the consensus network parameter set. */ /* private */ char * -dirvote_compute_params(smartlist_t *votes) +dirvote_compute_params(smartlist_t *votes, int method, int total_authorities) { int i; int32_t *vals; @@ -669,11 +678,17 @@ dirvote_compute_params(smartlist_t *votes) next_param = smartlist_get(param_list, param_sl_idx+1); if (!next_param || strncmp(next_param, param, cur_param_len)) { /* We've reached the end of a series. */ - int32_t median = median_int32(vals, i); - char *out_string = tor_malloc(64+cur_param_len); - memcpy(out_string, param, cur_param_len); - tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); - smartlist_add(output, out_string); + /* Make sure enough authorities voted on this param, unless the + * the consensus method we use is too old for that. */ + if (method < MIN_METHOD_FOR_MAJORITY_PARAMS || + i > total_authorities/2 || + i >= MIN_VOTES_FOR_PARAM) { + int32_t median = median_int32(vals, i); + char *out_string = tor_malloc(64+cur_param_len); + memcpy(out_string, param, cur_param_len); + tor_snprintf(out_string+cur_param_len,64, "%ld", (long)median); + smartlist_add(output, out_string); + } i = 0; if (next_param) { @@ -1496,7 +1511,8 @@ networkstatus_compute_consensus(smartlist_t *votes, } if (consensus_method >= MIN_METHOD_FOR_PARAMS) { - params = dirvote_compute_params(votes); + params = dirvote_compute_params(votes, consensus_method, + total_authorities); if (params) { smartlist_add(chunks, tor_strdup("params ")); smartlist_add(chunks, params); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index d19635173f..1f4dc362b5 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -84,7 +84,8 @@ document_signature_t *voter_get_sig_by_algorithm( #ifdef DIRVOTE_PRIVATE char *format_networkstatus_vote(crypto_pk_env_t *private_key, networkstatus_t *v3_ns); -char *dirvote_compute_params(smartlist_t *votes); +char *dirvote_compute_params(smartlist_t *votes, int method, + int total_authorities); #endif #endif diff --git a/src/or/dns.c b/src/or/dns.c index 8ed9536903..beb110acb2 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -1395,6 +1395,10 @@ launch_resolve(edge_connection_t *exitconn) int r; int options = get_options()->ServerDNSSearchDomains ? 0 : DNS_QUERY_NO_SEARCH; + + if (get_options()->DisableNetwork) + return -1; + /* What? Nameservers not configured? Sounds like a bug. */ if (!nameservers_configured) { log_warn(LD_EXIT, "(Harmless.) Nameservers not configured, but resolve " @@ -1601,6 +1605,9 @@ launch_test_addresses(int fd, short event, void *args) (void)event; (void)args; + if (options->DisableNetwork) + return; + log_info(LD_EXIT, "Launching checks to see whether our nameservers like to " "hijack *everything*."); /* This situation is worse than the failure-hijacking situation. When this diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 6fd2b4f197..ce64581d1c 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void) static void hibernate_begin(hibernate_state_t new_state, time_t now) { - connection_t *conn; const or_options_t *options = get_options(); if (new_state == HIBERNATE_STATE_EXITING && @@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now) } /* close listeners. leave control listener(s). */ - while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) || - (conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) { - log_info(LD_NET,"Closing listener type %d", conn->type); - connection_mark_for_close(conn); - } + connection_mark_all_noncontrol_listeners(); /* XXX kill intro point circs */ /* XXX upload rendezvous service descriptors with no intro points */ diff --git a/src/or/main.c b/src/or/main.c index 7008d388a1..1ba33957a6 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -196,6 +196,26 @@ free_old_inbuf(connection_t *conn) } #endif +#if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS) +/** Remove the kernel-space send and receive buffers for <b>s</b>. For use + * with IOCP only. */ +static int +set_buffer_lengths_to_zero(tor_socket_t s) +{ + int zero = 0; + int r = 0; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_SNDBUF"); + r = -1; + } + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) { + log_warn(LD_NET, "Unable to clear SO_RCVBUF"); + r = -1; + } + return r; +} +#endif + /** Add <b>conn</b> to the array of connections that we can poll on. The * connection's socket must be set; the connection starts out * non-reading and non-writing. @@ -216,6 +236,14 @@ connection_add_impl(connection_t *conn, int is_connecting) #ifdef USE_BUFFEREVENTS if (connection_type_uses_bufferevent(conn)) { if (SOCKET_OK(conn->s) && !conn->linked) { + +#ifdef MS_WINDOWS + if (tor_libevent_using_iocp_bufferevents() && + get_options()->UserspaceIOCPBuffers) { + set_buffer_lengths_to_zero(conn->s); + } +#endif + conn->bufev = bufferevent_socket_new( tor_libevent_get_base(), conn->s, @@ -906,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache) update_extrainfo_downloads(now); } - if (server_mode(options) && !we_are_hibernating() && !from_cache && + if (server_mode(options) && !net_is_disabled() && !from_cache && (can_complete_circuit || !any_predicted_circuits(now))) consider_testing_reachability(1, 1); } @@ -1133,11 +1161,11 @@ run_scheduled_events(time_t now) if (router_rebuild_descriptor(1)<0) { log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); } - if (advertised_server_mode()) + if (advertised_server_mode() & !options->DisableNetwork) router_upload_dir_desc_to_dirservers(0); } - if (time_to_try_getting_descriptors < now) { + if (!options->DisableNetwork && time_to_try_getting_descriptors < now) { update_all_descriptor_downloads(now); update_extrainfo_downloads(now); if (router_have_minimum_dir_info()) @@ -1161,10 +1189,7 @@ run_scheduled_events(time_t now) last_rotated_x509_certificate = now; if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) { log_info(LD_GENERAL,"Rotating tls context."); - if (tor_tls_context_init(public_server_mode(options), - get_tlsclient_identity_key(), - is_server ? get_server_identity_key() : NULL, - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_warn(LD_BUG, "Error reinitializing TLS context"); /* XXX is it a bug here, that we just keep going? -RD */ } @@ -1191,7 +1216,7 @@ run_scheduled_events(time_t now) if (time_to_launch_reachability_tests < now && (authdir_mode_tests_reachability(options)) && - !we_are_hibernating()) { + !net_is_disabled()) { time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); @@ -1327,7 +1352,7 @@ run_scheduled_events(time_t now) /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (time_to_check_descriptor < now) { + if (time_to_check_descriptor < now && !options->DisableNetwork) { static int dirport_reachability_count = 0; time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; check_descriptor_bandwidth_changed(now); @@ -1402,7 +1427,7 @@ run_scheduled_events(time_t now) connection_expire_held_open(); /** 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!we_are_hibernating() && time_to_check_listeners < now) { + if (!net_is_disabled() && time_to_check_listeners < now) { retry_all_listeners(NULL, NULL); time_to_check_listeners = now+60; } @@ -1413,7 +1438,7 @@ run_scheduled_events(time_t now) * and we make a new circ if there are no clean circuits. */ have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !we_are_hibernating()) + if (have_dir_info && !net_is_disabled()) circuit_build_needed_circs(now); /* every 10 seconds, but not at the same second as other such events */ @@ -1444,7 +1469,7 @@ run_scheduled_events(time_t now) circuit_close_all_marked(); /** 7. And upload service descriptors if necessary. */ - if (can_complete_circuit && !we_are_hibernating()) { + if (can_complete_circuit && !net_is_disabled()) { rend_consider_services_upload(now); rend_consider_descriptor_republication(); } @@ -1461,7 +1486,8 @@ run_scheduled_events(time_t now) /** 9. and if we're a server, check whether our DNS is telling stories to * us. */ - if (public_server_mode(options) && time_to_check_for_correct_dns < now) { + if (!net_is_disabled() && + public_server_mode(options) && time_to_check_for_correct_dns < now) { if (!time_to_check_for_correct_dns) { time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); } else { @@ -1480,19 +1506,21 @@ run_scheduled_events(time_t now) } /** 11. check the port forwarding app */ - if (time_to_check_port_forwarding < now && + if (!net_is_disabled() && + time_to_check_port_forwarding < 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, - options->DirPort, - options->ORPort, + get_primary_dir_port(), + get_primary_or_port(), now); time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } /** 11b. check pending unconfigured managed proxies */ - if (pt_proxies_configuration_pending()) + if (!net_is_disabled() && pt_proxies_configuration_pending()) pt_configure_remaining_proxies(); /** 11c. validate pluggable transports configuration if we need to */ @@ -1564,7 +1592,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) control_event_stream_bandwidth_used(); if (server_mode(options) && - !we_are_hibernating() && + !net_is_disabled() && seconds_elapsed > 0 && can_complete_circuit && stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != @@ -1765,7 +1793,8 @@ do_hup(void) /* retry appropriate downloads */ router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - update_networkstatus_downloads(time(NULL)); + if (!options->DisableNetwork) + update_networkstatus_downloads(time(NULL)); /* We'll retry routerstatus downloads in about 10 seconds; no need to * force a retry there. */ @@ -2239,15 +2268,14 @@ tor_init(int argc, char *argv[]) { const char *version = get_version(); - log_notice(LD_GENERAL, "Tor v%s%s running on %s.", version, #ifdef USE_BUFFEREVENTS - " (with bufferevents)", + log_notice(LD_GENERAL, "Tor v%s (with bufferevents) running on %s.", + version, get_uname()); #else - "", + log_notice(LD_GENERAL, "Tor v%s running on %s.", version, get_uname()); #endif - get_uname()); - log_notice(LD_GENERAL, "WARNING: Tor can't help you if you use it wrong. " + log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! " "Learn how to be safe at " "https://www.torproject.org/download/download#warning"); @@ -2256,6 +2284,11 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } +#ifdef NON_ANONYMOUS_MODE_ENABLED + log(LOG_WARN, LD_GENERAL, "This copy of Tor was compiled to run in a " + "non-anonymous mode. It will provide NO ANONYMITY."); +#endif + if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); return -1; diff --git a/src/or/nodelist.c b/src/or/nodelist.c index b93b919c13..eafc9b8b74 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -646,24 +646,70 @@ node_exit_policy_rejects_all(const node_t *node) return 1; } -/** Copy the address for <b>node</b> into *<b>addr_out</b>. */ -int -node_get_addr(const node_t *node, tor_addr_t *addr_out) +/** Return list of tor_addr_port_t with all OR ports (in the sense IP + * addr + TCP port) for <b>node</b>. Caller must free all elements + * using tor_free() and free the list using smartlist_free(). + * + * XXX this is potentially a memory fragmentation hog -- if on + * critical path consider the option of having the caller allocate the + * memory + */ +smartlist_t * +node_get_all_orports(const node_t *node) +{ + smartlist_t *sl = smartlist_create(); + + if (node->ri != NULL) { + if (node->ri->addr != 0) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&ap->addr, node->ri->addr); + ap->port = node->ri->or_port; + smartlist_add(sl, ap); + } + if (!tor_addr_is_null(&node->ri->ipv6_addr)) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_copy(&ap->addr, &node->ri->ipv6_addr); + ap->port = node->ri->or_port; + smartlist_add(sl, ap); + } + } else if (node->rs != NULL) { + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&ap->addr, node->rs->addr); + ap->port = node->rs->or_port; + smartlist_add(sl, ap); + } + + return sl; +} + +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - tor_addr_from_ipv4h(addr_out, node->ri->addr); - return 0; - } else if (node->rs) { - tor_addr_from_ipv4h(addr_out, node->rs->addr); - return 0; + router_get_prim_orport(node->ri, ap_out); } - return -1; + else if (node->rs) { + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; + } +} + +/** Wrapper around node_get_prim_orport for backward + compatibility. */ +void +node_get_addr(const node_t *node, tor_addr_t *addr_out) +{ + tor_addr_port_t ap; + node_get_prim_orport(node, &ap); + tor_addr_copy(addr_out, &ap.addr); } /** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't * seem to have one. */ uint32_t -node_get_addr_ipv4h(const node_t *node) +node_get_prim_addr_ipv4h(const node_t *node) { if (node->ri) { return node->ri->addr; @@ -673,9 +719,38 @@ node_get_addr_ipv4h(const node_t *node) return 0; } -/** Copy a string representation of the IP address for <b>node</b> into the - * <b>len</b>-byte buffer at <b>buf</b>. - */ +/** Copy the preferred OR port (IP address and TCP port) for + * <b>node</b> into <b>ap_out</b>. */ +void +node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + if (node->ri) { + router_get_pref_orport(node->ri, ap_out); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; + } +} + +/** Copy the preferred IPv6 OR port (address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) +{ + if (node->ri) { + router_get_pref_ipv6_orport(node->ri, ap_out); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_make_unspec(&ap_out->addr); + ap_out->port = 0; + } +} + +/** Copy a string representation of an IP address for <b>node</b> into + * the <b>len</b>-byte buffer at <b>buf</b>. */ void node_get_address_string(const node_t *node, char *buf, size_t len) { @@ -701,18 +776,6 @@ node_get_declared_uptime(const node_t *node) return -1; } -/** Return <b>node</b>'s declared or_port */ -uint16_t -node_get_orport(const node_t *node) -{ - if (node->ri) - return node->ri->or_port; - else if (node->rs) - return node->rs->or_port; - else - return 0; -} - /** Return <b>node</b>'s platform string, or NULL if we don't know it. */ const char * node_get_platform(const node_t *node) diff --git a/src/or/nodelist.h b/src/or/nodelist.h index bd2e63953c..1b7549dade 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -37,10 +37,13 @@ int node_get_purpose(const node_t *node); (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE) int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); -int node_get_addr(const node_t *node, tor_addr_t *addr_out); -uint32_t node_get_addr_ipv4h(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 *addr_port_out); +void node_get_pref_orport(const node_t *node, tor_addr_port_t *addr_port_out); +void node_get_pref_ipv6_orport(const node_t *node, + tor_addr_port_t *addr_port_out); +uint32_t node_get_prim_addr_ipv4h(const node_t *node); int node_allows_single_hop_exits(const node_t *node); -uint16_t node_get_orport(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); @@ -50,6 +53,10 @@ const smartlist_t *node_get_declared_family(const node_t *node); smartlist_t *nodelist_get_list(void); +/* Temporary during transition to multiple addresses. */ +void node_get_addr(const node_t *node, tor_addr_t *addr_out); +#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) + /* XXXX These need to move out of routerlist.c */ void nodelist_refresh_countries(void); void node_set_country(node_t *node); diff --git a/src/or/or.h b/src/or/or.h index 67ba62bdd6..63ff5c4b31 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -130,6 +130,10 @@ #define cell_t tor_cell_t #endif +#ifdef ENABLE_TOR2WEB_MODE +#define NON_ANONYMOUS_MODE_ENABLED 1 +#endif + /** Length of longest allowable configured nickname. */ #define MAX_NICKNAME_LEN 19 /** Length of a router identity encoded as a hexadecimal digest, plus @@ -789,10 +793,10 @@ typedef struct rend_data_t { char rend_cookie[REND_COOKIE_LEN]; } rend_data_t; -/** Time interval for tracking possible replays of INTRODUCE2 cells. - * Incoming cells with timestamps half of this interval in the past or - * future are dropped immediately. */ -#define REND_REPLAY_TIME_INTERVAL (60 * 60) +/** Time interval for tracking replays of DH public keys received in + * INTRODUCE2 cells. Used only to avoid launching multiple + * simultaneous attempts to connect to the same rendezvous point. */ +#define REND_REPLAY_TIME_INTERVAL (5 * 60) /** Used to indicate which way a cell is going on a circuit. */ typedef enum { @@ -1720,6 +1724,13 @@ typedef struct { uint16_t or_port; /**< Port for TLS connections. */ uint16_t dir_port; /**< Port for HTTP directory connections. */ + /* DOCDOC */ + /* XXXXX187 Actually these should probably be part of a list of addresses, + * not just a special case. Use abstractions to access these; don't do it + * directly. */ + tor_addr_t ipv6_addr; + uint16_t ipv6_orport; + crypto_pk_env_t *onion_pkey; /**< Public RSA key for onions. */ crypto_pk_env_t *identity_pkey; /**< Public RSA key for signing. */ @@ -1751,6 +1762,8 @@ typedef struct { /** True if, after we have added this router, we should re-launch * tests for it. */ unsigned int needs_retest_if_added:1; + /** True if ipv6_addr:ipv6_orport is preferred. */ + unsigned int ipv6_preferred:1; /** Tor can use this router for general positions in circuits; we got it * from a directory server as usual, or we're an authority and a server @@ -2831,16 +2844,37 @@ typedef struct port_cfg_t { int session_group; /**< A session group, or -1 if this port is not in a * session group. */ + /* Server port types (or, dir) only: */ + unsigned int no_advertise : 1; + unsigned int no_listen : 1; + unsigned int all_addrs : 1; + unsigned int ipv4_only : 1; + unsigned int ipv6_only : 1; + /* Unix sockets only: */ /** Path for an AF_UNIX address */ char unix_addr[FLEXIBLE_ARRAY_MEMBER]; } port_cfg_t; +/** Ordinary configuration line. */ +#define CONFIG_LINE_NORMAL 0 +/** Appends to previous configuration for the same option, even if we + * would ordinary replace it. */ +#define CONFIG_LINE_APPEND 1 +/* Removes all previous configuration for an option. */ +#define CONFIG_LINE_CLEAR 2 + /** A linked list of lines in a config file. */ typedef struct config_line_t { char *key; char *value; struct config_line_t *next; + /** What special treatment (if any) does this line require? */ + unsigned int command:2; + /** If true, subsequent assignments to this linelist should replace + * it, not extend it. Set only on the first item in a linelist in an + * or_options_t. */ + unsigned int fragile:1; } config_line_t; typedef struct routerset_t routerset_t; @@ -2873,6 +2907,8 @@ typedef struct { char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ + int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/ + routerset_t *ExitNodes; /**< Structure containing nicknames, digests, * country codes and IP address patterns of ORs to * consider as exits. */ @@ -2930,17 +2966,18 @@ typedef struct { int DirAllowPrivateAddresses; char *User; /**< Name of user to run Tor as. */ char *Group; /**< Name of group to run Tor as. */ - int ORPort; /**< Port to listen on for OR connections. */ + config_line_t *ORPort; /**< Ports to listen on for OR connections. */ config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */ /** Ports to listen on for transparent pf/netfilter connections. */ config_line_t *TransPort; config_line_t *NATDPort; /**< Ports to listen on for transparent natd * connections. */ - int ControlPort; /**< Port to listen on for control connections. */ + config_line_t *ControlPort; /**< Port to listen on for control + * connections. */ config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on * for control connections. */ int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ - int DirPort; /**< Port to listen on for directory connections. */ + config_line_t *DirPort; /**< Port to listen on for directory connections. */ config_line_t *DNSPort; /**< Port to listen on for DNS requests. */ int AssumeReachable; /**< Whether to publish our descriptor regardless. */ int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ @@ -3008,6 +3045,11 @@ typedef struct { int AllDirActionsPrivate; /**< Should every directory action be sent * through a Tor circuit? */ + /** Run in 'tor2web mode'? (I.e. only make client connections to hidden + * services, and use a single hop for all hidden-service-related + * circuits.) */ + int Tor2webMode; + int ConnLimit; /**< Demanded minimum number of simultaneous connections. */ int _ConnLimit; /**< Maximum allowed number of simultaneous connections. */ int RunAsDaemon; /**< If true, run in the background. (Unix only) */ @@ -3248,6 +3290,8 @@ typedef struct { disclaimer. This allows a server administrator to show that they're running Tor and anyone visiting their server will know this without any specialized knowledge. */ + int DisableDebuggerAttachment; /**< Currently Linux only specific attempt to + disable ptrace; needs BSD testing. */ /** Boolean: if set, we start even if our resolv.conf file is missing * or broken. */ int ServerDNSAllowBrokenConfig; @@ -3432,6 +3476,15 @@ typedef struct { * never use it. If -1, we do what the consensus says. */ int OptimisticData; + /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF + * to 0 to try to save kernel memory and avoid the dread "Out of buffers" + * issue. */ + int UserspaceIOCPBuffers; + + /** If 1, we accept and launch no external network connections, except on + * control ports. */ + int DisableNetwork; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -4017,6 +4070,26 @@ typedef struct rend_encoded_v2_service_descriptor_t { * introduction point. See also rend_intro_point_t.unreachable_count. */ #define MAX_INTRO_POINT_REACHABILITY_FAILURES 5 +/** The maximum number of distinct INTRODUCE2 cells which a hidden + * service's introduction point will receive before it begins to + * expire. + * + * XXX023 Is this number at all sane? */ +#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384 + +/** The minimum number of seconds that an introduction point will last + * before expiring due to old age. (If it receives + * INTRO_POINT_LIFETIME_INTRODUCTIONS INTRODUCE2 cells, it may expire + * sooner.) + * + * XXX023 Should this be configurable? */ +#define INTRO_POINT_LIFETIME_MIN_SECONDS 18*60*60 +/** The maximum number of seconds that an introduction point will last + * before expiring due to old age. + * + * XXX023 Should this be configurable? */ +#define INTRO_POINT_LIFETIME_MAX_SECONDS 24*60*60 + /** Introduction point information. Used both in rend_service_t (on * the service side) and in rend_service_descriptor_t (on both the * client and service side). */ @@ -4036,6 +4109,42 @@ typedef struct rend_intro_point_t { * circuit to this intro point for some reason other than our * circuit-build timeout. See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */ unsigned int unreachable_count : 3; + + /** (Service side only) Flag indicating that this intro point was + * included in the last HS descriptor we generated. */ + unsigned int listed_in_last_desc : 1; + + /** (Service side only) Flag indicating that + * rend_service_note_removing_intro_point has been called for this + * intro point. */ + unsigned int rend_service_note_removing_intro_point_called : 1; + + /** (Service side only) A digestmap recording the INTRODUCE2 cells + * this intro point's circuit has received. Each key is the digest + * of the RSA-encrypted part of a received INTRODUCE2 cell; each + * value is a pointer to the time_t at which the cell was received. + * This digestmap is used to prevent replay attacks. */ + digestmap_t *accepted_intro_rsa_parts; + + /** (Service side only) The time at which this intro point was first + * published, or -1 if this intro point has not yet been + * published. */ + time_t time_published; + + /** (Service side only) The time at which this intro point should + * (start to) expire, or -1 if we haven't decided when this intro + * point should expire. */ + time_t time_to_expire; + + /** (Service side only) The time at which we decided that this intro + * point should start expiring, or -1 if this intro point is not yet + * expiring. + * + * This field also serves as a flag to indicate that we have decided + * to expire this intro point, in case intro_point_should_expire_now + * flaps (perhaps due to a clock jump; perhaps due to other + * weirdness, or even a (present or future) bug). */ + time_t time_expiring; } rend_intro_point_t; /** Information used to connect to a hidden service. Used on both the diff --git a/src/or/relay.c b/src/or/relay.c index ac3114bda5..6cf4b73a5f 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -924,6 +924,7 @@ connection_edge_process_relay_cell_not_open( } circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ)); /* don't send a socks reply to transparent conns */ + tor_assert(entry_conn->socks_request != NULL); if (!entry_conn->socks_request->has_finished) connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 6a45207e29..8aae8c5cb5 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -139,8 +139,10 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(rendcirc->rend_data); tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address, rendcirc->rend_data->onion_address)); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(introcirc->build_state->onehop_tunnel)); tor_assert(!(rendcirc->build_state->onehop_tunnel)); +#endif if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, &entry) < 1) { @@ -331,7 +333,9 @@ rend_client_introduction_acked(origin_circuit_t *circ, } tor_assert(circ->build_state->chosen_exit); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(circ->build_state->onehop_tunnel)); +#endif tor_assert(circ->rend_data); if (request_len == 0) { @@ -343,7 +347,9 @@ rend_client_introduction_acked(origin_circuit_t *circ, rendcirc = circuit_get_by_rend_query_and_purpose( circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY); if (rendcirc) { /* remember the ack */ +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(rendcirc->build_state->onehop_tunnel)); +#endif rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED; /* Set timestamp_dirty, because circuit_expire_building expects * it to specify when a circuit entered the @@ -529,6 +535,7 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; time_t now = time(NULL); char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; + int tor2web_mode = get_options()->Tor2webMode; tor_assert(desc_id); tor_assert(rend_query); /* Determine responsible dirs. Even if we can't get all we want, @@ -587,7 +594,8 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) directory_initiate_command_routerstatus_rend(hs_dir, DIR_PURPOSE_FETCH_RENDDESC_V2, ROUTER_PURPOSE_GENERAL, - 1, desc_id_base32, NULL, 0, 0, + !tor2web_mode, desc_id_base32, + NULL, 0, 0, rend_query); log_info(LD_REND, "Sending fetch request for v2 descriptor for " "service '%s' with descriptor ID '%s', auth type %d, " @@ -884,10 +892,9 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, onion_append_to_cpath(&circ->cpath, hop); circ->build_state->pending_final_cpath = NULL; /* prevent double-free */ - /* XXXX023 This is a pretty brute-force approach. It'd be better to - * attach only the connections that are waiting on this circuit, rather - * than trying to attach them all. See comments bug 743. */ - connection_ap_attach_pending(); + + circuit_try_attaching_streams(circ); + memset(keys, 0, sizeof(keys)); return 0; err: @@ -1059,7 +1066,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, smartlist_del(usable_nodes, i); goto again; } - new_extend_info = extend_info_from_node(node); + new_extend_info = extend_info_from_node(node, 0); if (!new_extend_info) { log_info(LD_REND, "We don't have a descriptor for the intro-point relay " "'%s'; trying another.", diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 94bb002210..d09e6566c2 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -440,6 +440,11 @@ rend_intro_point_free(rend_intro_point_t *intro) extend_info_free(intro->extend_info); crypto_free_pk_env(intro->intro_key); + + if (intro->accepted_intro_rsa_parts != NULL) { + digestmap_free(intro->accepted_intro_rsa_parts, _tor_free); + } + tor_free(intro); } diff --git a/src/or/rendservice.c b/src/or/rendservice.c index e0c1a8c87a..76caeffd0f 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -26,6 +26,11 @@ static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest); +static rend_intro_point_t *find_intro_point(origin_circuit_t *circ); + +static int intro_point_accepted_intro_count(rend_intro_point_t *intro); +static int intro_point_should_expire_now(rend_intro_point_t *intro, + time_t now); /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. @@ -36,8 +41,10 @@ typedef struct rend_service_port_config_t { tor_addr_t real_addr; } rend_service_port_config_t; -/** Try to maintain this many intro points per service if possible. */ -#define NUM_INTRO_POINTS 3 +/** Try to maintain this many intro points per service by default. */ +#define NUM_INTRO_POINTS_DEFAULT 3 +/** Maintain no more than this many intro points per hidden service. */ +#define NUM_INTRO_POINTS_MAX 10 /** If we can't build our intro circuits, don't retry for this long. */ #define INTRO_CIRC_RETRY_PERIOD (60*5) @@ -51,6 +58,10 @@ typedef struct rend_service_port_config_t { * rendezvous point before giving up? */ #define MAX_REND_TIMEOUT 30 +/** How many seconds should we wait for new HS descriptors to reach + * our clients before we close an expiring intro point? */ +#define INTRO_POINT_EXPIRATION_GRACE_PERIOD 5*60 + /** Represents a single hidden service running at this OP. */ typedef struct rend_service_t { /* Fields specified in config file */ @@ -72,17 +83,24 @@ typedef struct rend_service_t { * introduction points. */ int n_intro_circuits_launched; /**< Count of intro circuits we have * established in this period. */ + unsigned int n_intro_points_wanted; /**< Number of intro points this + * service wants to have open. */ rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ time_t desc_is_dirty; /**< Time at which changes to the hidden service * descriptor content occurred, or 0 if it's * up-to-date. */ time_t next_upload_time; /**< Scheduled next hidden service descriptor * upload time. */ - /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t of when - * they were received; used to prevent replays. */ - digestmap_t *accepted_intros; - /** Time at which we last removed expired values from accepted_intros. */ - time_t last_cleaned_accepted_intros; + /** Map from digests of Diffie-Hellman values INTRODUCE2 to time_t + * of when they were received. Clients may send INTRODUCE1 cells + * for the same rendezvous point through two or more different + * introduction points; when they do, this digestmap keeps us from + * launching multiple simultaneous attempts to connect to the same + * rend point. */ + digestmap_t *accepted_intro_dh_parts; + /** Time at which we last removed expired values from + * accepted_intro_dh_parts. */ + time_t last_cleaned_accepted_intro_dh_parts; } rend_service_t; /** A list of rend_service_t's for services run on this OP. @@ -98,6 +116,17 @@ num_rend_services(void) return smartlist_len(rend_service_list); } +/** Return a string identifying <b>service</b>, suitable for use in a + * log message. The result does not need to be freed, but may be + * overwritten by the next call to this function. */ +static const char * +rend_service_describe_for_log(rend_service_t *service) +{ + /* XXX024 Use this function throughout rendservice.c. */ + /* XXX024 Return a more useful description? */ + return safe_str_client(service->service_id); +} + /** Helper: free storage held by a single service authorized client entry. */ static void rend_authorized_client_free(rend_authorized_client_t *client) @@ -142,7 +171,7 @@ rend_service_free(rend_service_t *service) rend_authorized_client_free(c);); smartlist_free(service->clients); } - digestmap_free(service->accepted_intros, _tor_free); + digestmap_free(service->accepted_intro_dh_parts, _tor_free); tor_free(service); } @@ -319,6 +348,7 @@ rend_config_services(const or_options_t *options, int validate_only) service->directory = tor_strdup(line->value); service->ports = smartlist_create(); service->intro_period_started = time(NULL); + service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; continue; } if (!service) { @@ -542,16 +572,38 @@ rend_service_update_descriptor(rend_service_t *service) for (i = 0; i < smartlist_len(service->intro_nodes); ++i) { rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i); rend_intro_point_t *intro_desc; + + /* This intro point won't be listed in the descriptor... */ + intro_svc->listed_in_last_desc = 0; + + if (intro_svc->time_expiring != -1) { + /* This intro point is expiring. Don't list it. */ + continue; + } + circ = find_intro_circuit(intro_svc, service->pk_digest); - if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) + if (!circ || circ->_base.purpose != CIRCUIT_PURPOSE_S_INTRO) { + /* This intro point's circuit isn't finished yet. Don't list it. */ continue; + } - /* We have an entirely established intro circuit. */ + /* ...unless this intro point is listed in the descriptor. */ + intro_svc->listed_in_last_desc = 1; + + /* We have an entirely established intro circuit. Publish it in + * our descriptor. */ intro_desc = tor_malloc_zero(sizeof(rend_intro_point_t)); intro_desc->extend_info = extend_info_dup(intro_svc->extend_info); if (intro_svc->intro_key) intro_desc->intro_key = crypto_pk_dup_key(intro_svc->intro_key); smartlist_add(d->intro_nodes, intro_desc); + + if (intro_svc->time_published == -1) { + /* We are publishing this intro point in a descriptor for the + * first time -- note the current time in the service's copy of + * the intro point. */ + intro_svc->time_published = time(NULL); + } } } @@ -857,15 +909,16 @@ rend_check_authorization(rend_service_t *service, /** Remove elements from <b>service</b>'s replay cache that are old enough to * be noticed by timestamp checking. */ static void -clean_accepted_intros(rend_service_t *service, time_t now) +clean_accepted_intro_dh_parts(rend_service_t *service, time_t now) { const time_t cutoff = now - REND_REPLAY_TIME_INTERVAL; - service->last_cleaned_accepted_intros = now; - if (!service->accepted_intros) + service->last_cleaned_accepted_intro_dh_parts = now; + if (!service->accepted_intro_dh_parts) return; - DIGESTMAP_FOREACH_MODIFY(service->accepted_intros, digest, time_t *, t) { + DIGESTMAP_FOREACH_MODIFY(service->accepted_intro_dh_parts, digest, + time_t *, t) { if (*t < cutoff) { tor_free(t); MAP_DEL_CURRENT(digest); @@ -873,6 +926,104 @@ clean_accepted_intros(rend_service_t *service, time_t now) } DIGESTMAP_FOREACH_END; } +/** Called when <b>intro</b> will soon be removed from + * <b>service</b>'s list of intro points. */ +static void +rend_service_note_removing_intro_point(rend_service_t *service, + rend_intro_point_t *intro) +{ + time_t now = time(NULL); + + /* Don't process an intro point twice here. */ + if (intro->rend_service_note_removing_intro_point_called) { + return; + } else { + intro->rend_service_note_removing_intro_point_called = 1; + } + + /* Update service->n_intro_points_wanted based on how long intro + * lasted and how many introductions it handled. */ + if (intro->time_published == -1) { + /* This intro point was never used. Don't change + * n_intro_points_wanted. */ + } else { + /* We want to increase the number of introduction points service + * operates if intro was heavily used, or decrease the number of + * intro points if intro was lightly used. + * + * We consider an intro point's target 'usage' to be + * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in + * INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's + * fraction of target usage, we divide the fraction of + * _LIFETIME_INTRODUCTIONS introductions that it has handled by + * the fraction of _LIFETIME_MIN_SECONDS for which it existed. + * + * Then we take the reciprocal of that fraction of desired usage, + * then multiply by a fudge factor of 1.5, to decide how many new + * introduction points should ideally replace intro (which is now + * closed or soon to be closed). In theory, assuming that + * introduction load is distributed equally across all intro + * points and ignoring the fact that different intro points are + * established and closed at different times, that number of intro + * points should bring all of our intro points exactly to our + * target usage. + * + * Then we clamp that number to a number of intro points we might + * be willing to replace this intro point with and turn it into an + * integer. then we clamp it again to the number of new intro + * points we could establish now, then we adjust + * service->n_intro_points_wanted and let rend_services_introduce + * create the new intro points we want (if any). + */ + double fractional_n_intro_points_wanted_to_replace_this_one = + ((((double)now - intro->time_published) / + INTRO_POINT_LIFETIME_MIN_SECONDS) * + ((intro_point_accepted_intro_count(intro)) / + INTRO_POINT_LIFETIME_INTRODUCTIONS)) * 1.5; + unsigned int n_intro_points_wanted_to_replace_this_one; + unsigned int n_intro_points_wanted_now; + unsigned int n_intro_points_really_wanted_now; + int n_intro_points_really_replacing_this_one; + + if (fractional_n_intro_points_wanted_to_replace_this_one > + NUM_INTRO_POINTS_MAX) { + n_intro_points_wanted_to_replace_this_one = NUM_INTRO_POINTS_MAX; + } else if (fractional_n_intro_points_wanted_to_replace_this_one < 0) { + n_intro_points_wanted_to_replace_this_one = 0; + } else { + n_intro_points_wanted_to_replace_this_one = (unsigned) + fractional_n_intro_points_wanted_to_replace_this_one; + } + + n_intro_points_wanted_now = + service->n_intro_points_wanted + + n_intro_points_wanted_to_replace_this_one - 1; + + if (n_intro_points_wanted_now < NUM_INTRO_POINTS_DEFAULT) { + /* XXXX This should be NUM_INTRO_POINTS_MIN instead. Perhaps + * another use of NUM_INTRO_POINTS_DEFAULT should be, too. */ + n_intro_points_really_wanted_now = NUM_INTRO_POINTS_DEFAULT; + } else if (n_intro_points_wanted_now > NUM_INTRO_POINTS_MAX) { + n_intro_points_really_wanted_now = NUM_INTRO_POINTS_MAX; + } else { + n_intro_points_really_wanted_now = n_intro_points_wanted_now; + } + + n_intro_points_really_replacing_this_one = + n_intro_points_really_wanted_now - service->n_intro_points_wanted + 1; + + log_info(LD_REND, "Replacing closing intro point for service %s " + "with %d new intro points (wanted %g replacements); " + "service will now try to have %u intro points", + rend_service_describe_for_log(service), + n_intro_points_really_replacing_this_one, + fractional_n_intro_points_wanted_to_replace_this_one, + n_intro_points_really_wanted_now); + + service->n_intro_points_wanted = n_intro_points_really_wanted_now; + } +} + /****** * Handle cells ******/ @@ -890,6 +1041,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, char buf[RELAY_PAYLOAD_SIZE]; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ rend_service_t *service; + rend_intro_point_t *intro_point; int r, i, v3_shift = 0; size_t len, keylen; crypto_dh_env_t *dh = NULL; @@ -910,7 +1062,9 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, time_t *access_time; const or_options_t *options = get_options(); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(circuit->build_state->onehop_tunnel)); +#endif tor_assert(circuit->rend_data); base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, @@ -937,7 +1091,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, service = rend_service_get_by_pk_digest( circuit->rend_data->rend_pk_digest); if (!service) { - log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.", + log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro " + "circ for an unrecognized service %s.", escaped(serviceid)); return -1; } @@ -962,17 +1117,26 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, return -1; } - if (!service->accepted_intros) - service->accepted_intros = digestmap_new(); + intro_point = find_intro_point(circuit); + if (intro_point == NULL) { + log_warn(LD_BUG, "Internal error: Got an INTRODUCE2 cell on an intro circ " + "(for service %s) with no corresponding rend_intro_point_t.", + escaped(serviceid)); + return -1; + } + + if (!service->accepted_intro_dh_parts) + service->accepted_intro_dh_parts = digestmap_new(); + + if (!intro_point->accepted_intro_rsa_parts) + intro_point->accepted_intro_rsa_parts = digestmap_new(); { char pkpart_digest[DIGEST_LEN]; - /* Check for replay of PK-encrypted portion. It is slightly naughty to - use the same digestmap to check for this and for g^x replays, but - collisions are tremendously unlikely. - */ + /* Check for replay of PK-encrypted portion. */ crypto_digest(pkpart_digest, (char*)request+DIGEST_LEN, keylen); - access_time = digestmap_get(service->accepted_intros, pkpart_digest); + access_time = digestmap_get(intro_point->accepted_intro_rsa_parts, + pkpart_digest); if (access_time != NULL) { log_warn(LD_REND, "Possible replay detected! We received an " "INTRODUCE2 cell with same PK-encrypted part %d seconds ago. " @@ -981,7 +1145,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, } access_time = tor_malloc(sizeof(time_t)); *access_time = now; - digestmap_set(service->accepted_intros, pkpart_digest, access_time); + digestmap_set(intro_point->accepted_intro_rsa_parts, + pkpart_digest, access_time); } /* Next N bytes is encrypted with service key */ @@ -997,7 +1162,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, len = r; if (*buf == 3) { /* Version 3 INTRODUCE2 cell. */ - time_t ts = 0; v3_shift = 1; auth_type = buf[1]; switch (auth_type) { @@ -1019,17 +1183,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, log_info(LD_REND, "Unknown authorization type '%d'", auth_type); } - /* Check timestamp. */ - ts = ntohl(get_uint32(buf+1+v3_shift)); + /* Skip the timestamp field. We no longer use it. */ v3_shift += 4; - if ((now - ts) < -1 * REND_REPLAY_TIME_INTERVAL / 2 || - (now - ts) > REND_REPLAY_TIME_INTERVAL / 2) { - /* This is far more likely to mean that a client's clock is - * skewed than that a replay attack is in progress. */ - log_info(LD_REND, "INTRODUCE2 cell is too %s. Discarding.", - (now - ts) < 0 ? "old" : "new"); - return -1; - } } if (*buf == 2 || *buf == 3) { /* Version 2 INTRODUCE2 cell. */ @@ -1099,7 +1254,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, goto err; } - extend_info = extend_info_from_node(node); + extend_info = extend_info_from_node(node, 0); } if (len != REND_COOKIE_LEN+DH_KEY_LEN) { @@ -1128,7 +1283,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, /* Check whether there is a past request with the same Diffie-Hellman, * part 1. */ - access_time = digestmap_get(service->accepted_intros, diffie_hellman_hash); + access_time = digestmap_get(service->accepted_intro_dh_parts, + diffie_hellman_hash); if (access_time != NULL) { /* A Tor client will send a new INTRODUCE1 cell with the same rend * cookie and DH public key as its previous one if its intro circ @@ -1150,9 +1306,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, * one hour. */ access_time = tor_malloc(sizeof(time_t)); *access_time = now; - digestmap_set(service->accepted_intros, diffie_hellman_hash, access_time); - if (service->last_cleaned_accepted_intros + REND_REPLAY_TIME_INTERVAL < now) - clean_accepted_intros(service, now); + digestmap_set(service->accepted_intro_dh_parts, + diffie_hellman_hash, access_time); + if (service->last_cleaned_accepted_intro_dh_parts + REND_REPLAY_TIME_INTERVAL + < now) + clean_accepted_intro_dh_parts(service, now); /* If the service performs client authorization, check included auth data. */ if (service->clients) { @@ -1394,7 +1552,9 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) crypto_pk_env_t *intro_key; tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(circuit->build_state->onehop_tunnel)); +#endif tor_assert(circuit->cpath); tor_assert(circuit->rend_data); @@ -1412,7 +1572,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) /* If we already have enough introduction circuits for this service, * redefine this one as a general circuit or close it, depending. */ - if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) { + if (count_established_intro_points(serviceid) > + (int)service->n_intro_points_wanted) { /* XXX023 remove cast */ const or_options_t *options = get_options(); if (options->ExcludeNodes) { /* XXXX in some future version, we can test whether the transition is @@ -1550,7 +1711,9 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); tor_assert(circuit->cpath); tor_assert(circuit->build_state); +#ifndef NON_ANONYMOUS_MODE_ENABLED tor_assert(!(circuit->build_state->onehop_tunnel)); +#endif tor_assert(circuit->rend_data); hop = circuit->build_state->pending_final_cpath; tor_assert(hop); @@ -1652,6 +1815,35 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest) return NULL; } +/** Return a pointer to the rend_intro_point_t corresponding to the + * service-side introduction circuit <b>circ</b>. */ +static rend_intro_point_t * +find_intro_point(origin_circuit_t *circ) +{ + const char *serviceid; + rend_service_t *service = NULL; + + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO); + tor_assert(circ->rend_data); + serviceid = circ->rend_data->onion_address; + + SMARTLIST_FOREACH(rend_service_list, rend_service_t *, s, + if (tor_memeq(s->service_id, serviceid, REND_SERVICE_ID_LEN_BASE32)) { + service = s; + break; + }); + + if (service == NULL) return NULL; + + SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro_point, + if (crypto_pk_cmp_keys(intro_point->intro_key, circ->intro_key) == 0) { + return intro_point; + }); + + return NULL; +} + /** Determine the responsible hidden service directories for the * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them; * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging @@ -1855,6 +2047,64 @@ upload_service_descriptor(rend_service_t *service) service->desc_is_dirty = 0; } +/** Return the number of INTRODUCE2 cells an intro point has + * received. */ +static int +intro_point_accepted_intro_count(rend_intro_point_t *intro) +{ + if (intro->accepted_intro_rsa_parts == NULL) { + return 0; + } else { + return digestmap_size(intro->accepted_intro_rsa_parts); + } +} + +/** Return non-zero iff <b>intro</b> should 'expire' now (i.e. we + * should stop publishing it in new descriptors and eventually close + * it). */ +static int +intro_point_should_expire_now(rend_intro_point_t *intro, + time_t now) +{ + tor_assert(intro != NULL); + + if (intro->time_published == -1) { + /* Don't expire an intro point if we haven't even published it yet. */ + return 0; + } + + if (intro->time_expiring != -1) { + /* We've already started expiring this intro point. *Don't* let + * this function's result 'flap'. */ + return 1; + } + + if (intro_point_accepted_intro_count(intro) >= + INTRO_POINT_LIFETIME_INTRODUCTIONS) { + /* This intro point has been used too many times. Expire it now. */ + return 1; + } + + if (intro->time_to_expire == -1) { + /* This intro point has been published, but we haven't picked an + * expiration time for it. Pick one now. */ + int intro_point_lifetime_seconds = + INTRO_POINT_LIFETIME_MIN_SECONDS + + crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS - + INTRO_POINT_LIFETIME_MIN_SECONDS); + + /* Start the expiration timer now, rather than when the intro + * point was first published. There shouldn't be much of a time + * difference. */ + intro->time_to_expire = now + intro_point_lifetime_seconds; + + return 0; + } + + /* This intro point has a time to expire set already. Use it. */ + return (now >= intro->time_to_expire); +} + /** For every service, check how many intro points it currently has, and: * - Pick new intro points as necessary. * - Launch circuits to any new intro points. @@ -1866,7 +2116,9 @@ rend_services_introduce(void) const node_t *node; rend_service_t *service; rend_intro_point_t *intro; - int changed, prev_intro_nodes; + int intro_point_set_changed, prev_intro_nodes; + unsigned int n_intro_points_unexpired; + unsigned int n_intro_points_to_open; smartlist_t *intro_nodes; time_t now; const or_options_t *options = get_options(); @@ -1879,7 +2131,16 @@ rend_services_introduce(void) service = smartlist_get(rend_service_list, i); tor_assert(service); - changed = 0; + + /* intro_point_set_changed becomes non-zero iff the set of intro + * points to be published in service's descriptor has changed. */ + intro_point_set_changed = 0; + + /* n_intro_points_unexpired collects the number of non-expiring + * intro points we have, so that we know how many new intro + * circuits we need to launch for this service. */ + n_intro_points_unexpired = 0; + if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) { /* One period has elapsed; we can try building circuits again. */ service->intro_period_started = now; @@ -1893,59 +2154,119 @@ rend_services_introduce(void) /* Find out which introduction points we have in progress for this service. */ - for (j=0; j < smartlist_len(service->intro_nodes); ++j) { - intro = smartlist_get(service->intro_nodes, j); + SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *, + intro) { + origin_circuit_t *intro_circ = + find_intro_circuit(intro, service->pk_digest); + + if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) { + /* This intro point has completely expired. Remove it, and + * mark the circuit for close if it's still alive. */ + if (intro_circ != NULL) { + circuit_mark_for_close(TO_CIRCUIT(intro_circ), + END_CIRC_REASON_FINISHED); + } + rend_intro_point_free(intro); + intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + /* We don't need to set intro_point_set_changed here, because + * this intro point wouldn't have been published in a current + * descriptor anyway. */ + continue; + } + node = node_get_by_id(intro->extend_info->identity_digest); - if (!node || !find_intro_circuit(intro, service->pk_digest)) { - log_info(LD_REND,"Giving up on %s as intro point for %s.", + if (!node || !intro_circ) { + int removing_this_intro_point_changes_the_intro_point_set = 1; + log_info(LD_REND, "Giving up on %s as intro point for %s" + " (circuit disappeared).", safe_str_client(extend_info_describe(intro->extend_info)), safe_str_client(service->service_id)); - if (service->desc) { - SMARTLIST_FOREACH(service->desc->intro_nodes, rend_intro_point_t *, - dintro, { - if (tor_memeq(dintro->extend_info->identity_digest, - intro->extend_info->identity_digest, DIGEST_LEN)) { - log_info(LD_REND, "The intro point we are giving up on was " - "included in the last published descriptor. " - "Marking current descriptor as dirty."); - service->desc_is_dirty = now; - } - }); + rend_service_note_removing_intro_point(service, intro); + if (intro->time_expiring != -1) { + log_info(LD_REND, "We were already expiring the intro point; " + "no need to mark the HS descriptor as dirty over this."); + removing_this_intro_point_changes_the_intro_point_set = 0; + } else if (intro->listed_in_last_desc) { + log_info(LD_REND, "The intro point we are giving up on was " + "included in the last published descriptor. " + "Marking current descriptor as dirty."); + service->desc_is_dirty = now; } rend_intro_point_free(intro); - smartlist_del(service->intro_nodes,j--); - changed = 1; + intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + if (removing_this_intro_point_changes_the_intro_point_set) + intro_point_set_changed = 1; } + + if (intro != NULL && intro_point_should_expire_now(intro, now)) { + log_info(LD_REND, "Expiring %s as intro point for %s.", + safe_str_client(extend_info_describe(intro->extend_info)), + safe_str_client(service->service_id)); + + rend_service_note_removing_intro_point(service, intro); + + /* The polite (and generally Right) way to expire an intro + * point is to establish a new one to replace it, publish a + * new descriptor that doesn't list any expiring intro points, + * and *then*, once our upload attempts for the new descriptor + * have ended (whether in success or failure), close the + * expiring intro points. + * + * Unfortunately, we can't find out when the new descriptor + * has actually been uploaded, so we'll have to settle for a + * five-minute timer. Start it. XXX023 This sucks. */ + intro->time_expiring = now; + + intro_point_set_changed = 1; + } + + if (intro != NULL && intro->time_expiring == -1) + ++n_intro_points_unexpired; + if (node) smartlist_add(intro_nodes, (void*)node); - } - - /* We have enough intro points, and the intro points we thought we had were - * all connected. - */ - if (!changed && smartlist_len(service->intro_nodes) >= NUM_INTRO_POINTS) { - /* We have all our intro points! Start a fresh period and reset the - * circuit count. */ + } SMARTLIST_FOREACH_END(intro); + + if (!intro_point_set_changed && + (n_intro_points_unexpired >= service->n_intro_points_wanted)) { + /* We have enough intro circuits in progress, and none of our + * intro circuits have died since the last call to + * rend_services_introduce! Start a fresh period and reset the + * circuit count. + * + * XXXX WTF? */ service->intro_period_started = now; service->n_intro_circuits_launched = 0; continue; } - /* Remember how many introduction circuits we started with. */ + /* Remember how many introduction circuits we started with. + * + * prev_intro_nodes serves a different purpose than + * n_intro_points_unexpired -- this variable tells us where our + * previously-created intro points end and our new ones begin in + * the intro-point list, so we don't have to launch the circuits + * at the same time as we create the intro points they correspond + * to. XXXX This is daft. */ prev_intro_nodes = smartlist_len(service->intro_nodes); + /* We have enough directory information to start establishing our - * intro points. We want to end up with three intro points, but if - * we're just starting, we launch five and pick the first three that - * complete. + * intro points. We want to end up with n_intro_points_wanted + * intro points, but if we're just starting, we launch two extra + * circuits and use the first n_intro_points_wanted that complete. * * The ones after the first three will be converted to 'general' * internal circuits in rend_service_intro_has_opened(), and then * we'll drop them from the list of intro points next time we * go through the above "find out which introduction points we have * in progress" loop. */ -#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2) - for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ? - NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) { + n_intro_points_to_open = (service->n_intro_points_wanted + + (prev_intro_nodes == 0 ? 2 : 0)); + for (j = (int)n_intro_points_unexpired; + j < (int)n_intro_points_to_open; + ++j) { /* XXXX remove casts */ router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC; if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION) flags |= CRN_ALLOW_INVALID; @@ -1953,16 +2274,21 @@ rend_services_introduce(void) options->ExcludeNodes, flags); if (!node) { log_warn(LD_REND, - "Could only establish %d introduction points for %s.", - smartlist_len(service->intro_nodes), service->service_id); + "Could only establish %d introduction points for %s; " + "wanted %u.", + smartlist_len(service->intro_nodes), service->service_id, + n_intro_points_to_open); break; } - changed = 1; + intro_point_set_changed = 1; smartlist_add(intro_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); - intro->extend_info = extend_info_from_node(node); + intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_new_pk_env(); tor_assert(!crypto_pk_generate_key(intro->intro_key)); + intro->time_published = -1; + intro->time_to_expire = -1; + intro->time_expiring = -1; smartlist_add(service->intro_nodes, intro); log_info(LD_REND, "Picked router %s as an intro point for %s.", safe_str_client(node_describe(node)), @@ -1970,7 +2296,7 @@ rend_services_introduce(void) } /* If there's no need to launch new circuits, stop here. */ - if (!changed) + if (!intro_point_set_changed) continue; /* Establish new introduction points. */ diff --git a/src/or/router.c b/src/or/router.c index b6b96a5fff..a08a2009a7 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -484,6 +484,16 @@ v3_authority_check_key_expiry(void) last_warned = now; } +int +router_initialize_tls_context(void) +{ + return tor_tls_context_init(public_server_mode(get_options()), + get_tlsclient_identity_key(), + server_mode(get_options()) ? + get_server_identity_key() : NULL, + MAX_SSL_KEY_LIFETIME_ADVERTISED); +} + /** Initialize all OR private keys, and the TLS context, as necessary. * On OPs, this only initializes the tls context. Return 0 on success, * or -1 if Tor should die. @@ -530,10 +540,7 @@ init_keys(void) } set_client_identity_key(prkey); /* Create a TLS context. */ - if (tor_tls_context_init(0, - get_tlsclient_identity_key(), - NULL, - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_err(LD_GENERAL,"Error creating TLS context for Tor client."); return -1; } @@ -626,13 +633,11 @@ init_keys(void) tor_free(keydir); /* 3. Initialize link key and TLS context. */ - if (tor_tls_context_init(public_server_mode(options), - get_tlsclient_identity_key(), - get_server_identity_key(), - MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) { + if (router_initialize_tls_context() < 0) { log_err(LD_GENERAL,"Error initializing TLS context"); return -1; } + /* 4. Build our router descriptor. */ /* Must be called after keys are initialized. */ mydesc = router_get_my_descriptor(); @@ -780,7 +785,7 @@ check_whether_dirport_reachable(void) const or_options_t *options = get_options(); return !options->DirPort || options->AssumeReachable || - we_are_hibernating() || + net_is_disabled() || can_reach_dir_port; } @@ -806,7 +811,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) return 0; if (authdir_mode(options)) /* always publish */ return dir_port; - if (we_are_hibernating()) + if (net_is_disabled()) return 0; if (!check_whether_dirport_reachable()) return 0; @@ -888,7 +893,8 @@ consider_testing_reachability(int test_or, int test_dir) log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", !orport_reachable ? "reachability" : "bandwidth", me->address, me->or_port); - ei = extend_info_from_router(me); + /* XXX IPv6 self testing IPv6 orports will need pref_addr */ + ei = extend_info_from_router(me, 0); circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); extend_info_free(ei); @@ -974,6 +980,14 @@ router_perform_bandwidth_test(int num_circs, time_t now) } } +/** Return true iff our network is in some sense disabled: either we're + * hibernating, entering hibernation, or */ +int +net_is_disabled(void) +{ + return get_options()->DisableNetwork || we_are_hibernating(); +} + /** Return true iff we believe ourselves to be an authoritative * directory server. */ @@ -1070,7 +1084,7 @@ int server_mode(const or_options_t *options) { if (options->ClientOnly) return 0; - return (options->ORPort != 0 || options->ORListenAddress); + return (options->ORPort || options->ORListenAddress); } /** Return true iff we are trying to be a non-bridge server. @@ -1121,7 +1135,14 @@ int proxy_mode(const or_options_t *options) { (void)options; - return smartlist_len(get_configured_client_ports()) > 0; + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { + if (p->type == CONN_TYPE_AP_LISTENER || + p->type == CONN_TYPE_AP_TRANS_LISTENER || + p->type == CONN_TYPE_AP_DNS_LISTENER || + p->type == CONN_TYPE_AP_NATD_LISTENER) + return 1; + } SMARTLIST_FOREACH_END(p); + return 0; } /** Decide if we're a publishable server. We are a publishable server if: @@ -1180,17 +1201,21 @@ consider_publishable_server(int force) /** Return the port that we should advertise as our ORPort; this is either * the one configured in the ORPort option, or the one we actually bound to - * if ORPort is "auto". */ + * if ORPort is "auto". + */ uint16_t router_get_advertised_or_port(const or_options_t *options) { - if (options->ORPort == CFG_AUTO_PORT) { + int port = get_primary_or_port(); + (void)options; + + if (port == CFG_AUTO_PORT) { connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER); if (c) return c->port; return 0; } - return options->ORPort; + return port; } /** Return the port that we should advertise as our DirPort; @@ -1201,15 +1226,18 @@ router_get_advertised_or_port(const or_options_t *options) uint16_t router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport) { - if (!options->DirPort) + int dirport_configured = get_primary_dir_port(); + (void)options; + + if (!dirport_configured) return dirport; - if (options->DirPort == CFG_AUTO_PORT) { + if (dirport_configured == CFG_AUTO_PORT) { connection_t *c = connection_get_by_type(CONN_TYPE_DIR_LISTENER); if (c) return c->port; return 0; } - return options->DirPort; + return dirport_configured; } /* @@ -1466,6 +1494,24 @@ router_rebuild_descriptor(int force) ri->cache_info.published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from * main thread */ + if (options->BridgeRelay) { + /* For now, only bridges advertise an ipv6 or-address. And only one. */ + const port_cfg_t *ipv6_orport = NULL; + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), const port_cfg_t *, p) { + if (p->type == CONN_TYPE_OR_LISTENER && + ! p->no_advertise && + ! p->ipv4_only && + tor_addr_family(&p->addr) == AF_INET6 && + ! tor_addr_is_internal(&p->addr, 1)) { + ipv6_orport = p; + break; + } + } SMARTLIST_FOREACH_END(p); + if (ipv6_orport) { + tor_addr_copy(&ri->ipv6_addr, &ipv6_orport->addr); + ri->ipv6_orport = ipv6_orport->port; + } + } ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (crypto_pk_get_digest(ri->identity_pkey, ri->cache_info.identity_digest)<0) { @@ -1875,6 +1921,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, int result=0; addr_policy_t *tmpe; char *family_line; + char *extra_or_address = NULL; const or_options_t *options = get_options(); /* Make sure the identity key matches the one in the routerinfo. */ @@ -1927,9 +1974,22 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, router->cache_info.extra_info_digest, DIGEST_LEN); } + if (router->ipv6_orport && + tor_addr_family(&router->ipv6_addr) == AF_INET6) { + char addr[TOR_ADDR_BUF_LEN]; + const char *a; + a = tor_addr_to_str(addr, &router->ipv6_addr, sizeof(addr), 1); + if (a) { + tor_asprintf(&extra_or_address, + "or-address %s:%d\n", a, router->ipv6_orport); + log_notice(LD_OR, "My line is <%s>", extra_or_address); + } + } + /* Generate the easy portion of the router descriptor. */ result = tor_snprintf(s, maxlen, "router %s %s %d 0 %d\n" + "%s" "platform %s\n" "opt protocols Link 1 2 Circuit 1\n" "published %s\n" @@ -1944,6 +2004,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, router->address, router->or_port, decide_to_advertise_dirport(options, router->dir_port), + extra_or_address ? extra_or_address : "", router->platform, published, fingerprint, @@ -1964,6 +2025,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, tor_free(family_line); tor_free(onion_pkey); tor_free(identity_pkey); + tor_free(extra_or_address); if (result < 0) { log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!"); @@ -2059,6 +2121,52 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, return (int)written+1; } +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * <b>router</b> into *<b>ap_out</b>. */ +void +router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +{ + tor_assert(ap_out != NULL); + tor_addr_from_ipv4h(&ap_out->addr, router->addr); + ap_out->port = router->or_port; +} + +/** Return 1 if we prefer the IPv6 address and OR TCP port of + * <b>router</b>, else 0. + * + * We prefer the IPv6 address if the router has one and + * i) the routerinfo_t says so + * or + * ii) the router has no IPv4 address. */ +int +router_ipv6_preferred(const routerinfo_t *router) +{ + return (!tor_addr_is_null(&router->ipv6_addr) + && (router->ipv6_preferred || router->addr == 0)); +} + +/** Copy the preferred OR port (IP address and TCP port) for + * <b>router</b> into *<b>addr_out</b>. */ +void +router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +{ + if (router_ipv6_preferred(router)) + router_get_pref_ipv6_orport(router, ap_out); + else + router_get_prim_orport(router, ap_out); +} + +/** Copy the preferred IPv6 OR port (IP address and TCP port) for + * <b>router</b> into *<b>ap_out</b>. */ +void +router_get_pref_ipv6_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out) +{ + tor_assert(ap_out != NULL); + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; +} + /** Load the contents of <b>filename</b>, find the last line starting with * <b>end_line</b>, ensure that its timestamp is not more than 25 hours in * the past or more than 1 hour in the future with respect to <b>now</b>, diff --git a/src/or/router.h b/src/or/router.h index f9d156cb09..d426b25da6 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -30,6 +30,7 @@ crypto_pk_env_t *init_key_from_file(const char *fname, int generate, int severity); void v3_authority_check_key_expiry(void); +int router_initialize_tls_context(void); int init_keys(void); int check_whether_orport_reachable(void); @@ -39,6 +40,8 @@ void router_orport_found_reachable(void); void router_dirport_found_reachable(void); void router_perform_bandwidth_test(int num_circs, time_t now); +int net_is_disabled(void); + int authdir_mode(const or_options_t *options); int authdir_mode_v1(const or_options_t *options); int authdir_mode_v2(const or_options_t *options); @@ -82,6 +85,13 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, crypto_pk_env_t *ident_key); +void router_get_prim_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_pref_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_pref_ipv6_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +int router_ipv6_preferred(const routerinfo_t *router); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, crypto_pk_env_t *ident_key); int is_legal_nickname(const char *s); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index d97b978f43..689df99c57 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up) log_debug(LD_DIR,"Marking router %s as %s.", node_describe(node), up ? "up" : "down"); #endif - if (!up && node_is_me(node) && !we_are_hibernating()) + if (!up && node_is_me(node) && !net_is_disabled()) log_warn(LD_NET, "We just marked ourself as down. Are your external " "addresses reachable?"); node->is_running = up; @@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc) void update_all_descriptor_downloads(time_t now) { + if (get_options()->DisableNetwork) + return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); launch_dummy_descriptor_download_as_needed(now, get_options()); @@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now) { router_reset_status_download_failures(); router_reset_descriptor_download_failures(); + if (get_options()->DisableNetwork) + return; update_networkstatus_downloads(now); update_all_descriptor_downloads(now); } diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 4ea7b964cf..678b11971b 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -64,6 +64,7 @@ typedef enum { K_DIR_OPTIONS, K_CLIENT_VERSIONS, K_SERVER_VERSIONS, + K_OR_ADDRESS, K_P, K_R, K_S, @@ -286,6 +287,7 @@ static token_rule_t routerdesc_token_table[] = { T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ), + T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ), @@ -541,6 +543,7 @@ static int router_get_hashes_impl(const char *s, size_t s_len, const char *start_str, const char *end_str, char end_char); static void token_clear(directory_token_t *tok); +static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k); static smartlist_t *find_all_exitpolicy(smartlist_t *s); static directory_token_t *_find_by_keyword(smartlist_t *s, directory_keyword keyword, @@ -1506,6 +1509,27 @@ router_parse_entry_from_string(const char *s, const char *end, "older Tors."); goto err; } + { + 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); + smartlist_free(or_addresses); + } + } exit_policy_tokens = find_all_exitpolicy(tokens); if (!smartlist_len(exit_policy_tokens)) { log_warn(LD_DIR, "No exit policy tokens in descriptor."); @@ -4134,6 +4158,20 @@ _find_by_keyword(smartlist_t *s, directory_keyword keyword, return tok; } +/** DOCDOC */ +static smartlist_t * +find_all_by_keyword(smartlist_t *s, directory_keyword k) +{ + smartlist_t *out = NULL; + SMARTLIST_FOREACH(s, directory_token_t *, t, + if (t->tp == k) { + if (!out) + out = smartlist_create(); + smartlist_add(out, t); + }); + return out; +} + /** Return a newly allocated smartlist of all accept or reject tokens in * <b>s</b>. */ diff --git a/src/or/transports.c b/src/or/transports.c index 6e8200f407..abf9d884f7 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -4,6 +4,83 @@ /** * \file transports.c * \brief Pluggable Transports related code. + * + * \details + * Each managed proxy is represented by a <b>managed_proxy_t</b>. + * Each managed proxy can support multiple transports. + * Each managed proxy gets configured through a multistep process. + * + * ::managed_proxy_list contains all the managed proxies this tor + * instance is supporting. + * In the ::managed_proxy_list there are ::unconfigured_proxies_n + * managed proxies that are still unconfigured. + * + * In every run_scheduled_event() tick, we attempt to launch and then + * configure the unconfiged managed proxies, using the configuration + * protocol defined in the 180_pluggable_transport.txt proposal. A + * managed proxy might need several ticks to get fully configured. + * + * When a managed proxy is fully configured, we register all its + * transports to the circuitbuild.c subsystem. At that point the + * transports are owned by the circuitbuild.c subsystem. + * + * When a managed proxy fails to follow the 180 configuration + * protocol, it gets marked as broken and gets destroyed. + * + * <b>In a little more detail:</b> + * + * While we are serially parsing torrc, we store all the transports + * that a proxy should spawn in its <em>transports_to_launch</em> + * element. + * + * When we finish reading the torrc, we spawn the managed proxy and + * expect {S,C}METHOD lines from its output. We add transports + * described by METHOD lines to its <em>transports</em> element, as + * transport_t structs. + * + * When the managed proxy stops spitting METHOD lines (signified by a + * '{S,C}METHODS DONE' message) we register all the transports + * collected to the circuitbuild.c subsystem. At this point, the + * pointers to transport_t can be transformed into dangling pointers + * at any point by the circuitbuild.c subsystem, and so we replace all + * transport_t pointers with strings describing the transport names. + * We can still go from a transport name to a transport_t using the + * fact that each transport name uniquely identifies a transport_t. + * + * <b>In even more detail, this is what happens when a SIGHUP + * occurs:</b> + * + * We immediately destroy all unconfigured proxies (We shouldn't have + * unconfigured proxies in the first place, except when SIGHUP rings + * immediately after tor is launched.). + * + * We mark all managed proxies and transports to signify that they + * must be removed if they don't contribute by the new torrc + * (we mark using the <b>marked_for_removal</b> element). + * We also mark all managed proxies to signify that they might need to + * be restarted so that they end up supporting all the transports the + * new torrc wants them to support (using the <b>got_hup</b> element). + * We also clear their <b>transports_to_launch</b> list so that we can + * put there the transports we need to launch according to the new + * torrc. + * + * We then start parsing torrc again. + * + * Everytime we encounter a transport line using a known pre-SIGHUP + * managed proxy, we cleanse that proxy from the removal mark. + * We also mark it as unconfigured so that on the next scheduled + * events tick, we investigate whether we need to restart the proxy + * so that it also spawns the new transports. + * If the post-SIGHUP <b>transports_to_launch</b> list is identical to + * the pre-SIGHUP one, it means that no changes were introduced to + * this proxy during the SIGHUP and no restart has to take place. + * + * During the post-SIGHUP torrc parsing, we unmark all transports + * spawned by managed proxies that we find in our torrc. + * We do that so that if we don't need to restart a managed proxy, we + * can continue using its old transports normally. + * If we end up restarting the proxy, we destroy and unregister all + * old transports from the circuitbuild.c subsystem. **/ #define PT_PRIVATE @@ -12,12 +89,20 @@ #include "circuitbuild.h" #include "transports.h" #include "util.h" +#include "router.h" -static void set_managed_proxy_environment(char ***envp, +#ifdef MS_WINDOWS +static void set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp); +#else +static int set_managed_proxy_environment(char ***envp, + const managed_proxy_t *mp); +#endif + static INLINE int proxy_configuration_finished(const managed_proxy_t *mp); -static void managed_proxy_destroy(managed_proxy_t *mp); +static void managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process); static void handle_finished_proxy(managed_proxy_t *mp); static void configure_proxy(managed_proxy_t *mp); @@ -56,84 +141,6 @@ static smartlist_t *managed_proxy_list = NULL; /** Number of still unconfigured proxies. */ static int unconfigured_proxies_n = 0; -/** "The main idea is:" - - Each managed proxy is represented by a 'managed_proxy_t'. - Each managed proxy can support multiple transports. - Each managed proxy gets configured through a multistep process. - - 'managed_proxy_list' contains all the managed proxies this tor - instance is supporting. - In the 'managed_proxy_list' there are 'unconfigured_proxies_n' - managed proxies that are still unconfigured. - - In every run_scheduled_event() tick, we attempt to launch and then - configure the unconfiged managed proxies, using the configuration - protocol defined in the 180_pluggable_transport.txt proposal. A - managed proxy might need several ticks to get fully configured. - - When a managed proxy is fully configured, we register all its - transports to the circuitbuild.c subsystem. At that point the - transports are owned by the circuitbuild.c subsystem. - - When a managed proxy fails to follow the 180 configuration - protocol, it gets marked as broken and gets destroyed. - - "In a little more technical detail:" - - While we are serially parsing torrc, we store all the transports - that a proxy should spawn in its 'transports_to_launch' element. - - When we finish reading the torrc, we spawn the managed proxy and - expect {S,C}METHOD lines from its output. We add transports - described by METHOD lines to its 'transports' element, as - 'transport_t' structs. - - When the managed proxy stops spitting METHOD lines (signified by a - '{S,C}METHODS DONE' message) we register all the transports - collected to the circuitbuild.c subsystem. At this point, the - 'transport_t's can be transformed into dangling pointers at any - point by the circuitbuild.c subsystem, and so we replace all - 'transport_t's with strings describing the transport names. We - can still go from a transport name to a 'transport_t' using the - fact that transport names uniquely identify 'transport_t's. - - "In even more technical detail I shall describe what happens when - the SIGHUP bell tolls:" - - We immediately destroy all unconfigured proxies (We shouldn't have - unconfigured proxies in the first place, except when SIGHUP rings - immediately after tor is launched.). - - We mark all managed proxies and transports to signify that they - must be removed if they don't contribute by the new torrc - (marked_for_removal). - We also mark all managed proxies to signify that they might need - to be restarted so that they end up supporting all the transports - the new torrc wants them to support (got_hup). - We also clear their 'transports_to_launch' list so that we can put - there the transports we need to launch according to the new torrc. - - We then start parsing torrc again. - - Everytime we encounter a transport line using a known pre-SIGHUP - managed proxy, we cleanse that proxy from the removal mark. - - We also mark it as unconfigured so that on the next scheduled - events tick, we investigate whether we need to restart the proxy - so that it also spawns the new transports. - If the post-SIGHUP 'transports_to_launch' list is identical to the - pre-SIGHUP one, it means that no changes were introduced to this - proxy during the SIGHUP and no restart has to take place. - - During the post-SIGHUP torrc parsing, we unmark all transports - spawned by managed proxies that we find in our torrc. - We do that so that if we don't need to restart a managed proxy, we - can continue using its old transports normally. - If we end up restarting the proxy, we destroy and unregister all - old transports from the circuitbuild.c subsystem. -*/ - /** Return true if there are still unconfigured managed proxies. */ int pt_proxies_configuration_pending(void) @@ -229,12 +236,10 @@ proxy_prepare_for_restart(managed_proxy_t *mp) transport_t *t_tmp = NULL; tor_assert(mp->conf_state == PT_PROTO_COMPLETED); - tor_assert(mp->pid); - /* kill the old obfsproxy process */ - tor_terminate_process(mp->pid); - mp->pid = 0; - fclose(mp->_stdout); + /* destroy the process handle and terminate the process. */ + tor_process_handle_destroy(mp->process_handle, 1); + mp->process_handle = NULL; /* destroy all its old transports. we no longer use them. */ SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) { @@ -256,52 +261,52 @@ proxy_prepare_for_restart(managed_proxy_t *mp) static int launch_managed_proxy(managed_proxy_t *mp) { - (void) mp; - (void) set_managed_proxy_environment; - return -1; -#if 0 - /* XXXX023 we must reenable this code for managed proxies to work. - * "All it needs" is revision to work with the new tor_spawn_background - * API. */ - char **envp=NULL; - int pid; - process_handle_t proc; - FILE *stdout_read = NULL; - int stdout_pipe=-1, stderr_pipe=-1; + int retval; + +#ifdef MS_WINDOWS + + LPVOID envp=NULL; - /* prepare the environment variables for the managed proxy */ set_managed_proxy_environment(&envp, mp); + tor_assert(envp); - pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv, - (const char **)envp, &proc); - if (pid < 0) { - log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", - mp->argv[0]); + /* Passing NULL as lpApplicationName makes Windows search for the .exe */ + retval = tor_spawn_background(NULL, (const char **)mp->argv, envp, + &mp->process_handle); + + tor_free(envp); + +#else + + char **envp=NULL; + + /* prepare the environment variables for the managed proxy */ + if (set_managed_proxy_environment(&envp, mp) < 0) { + log_warn(LD_GENERAL, "Could not setup the environment of " + "the managed proxy at '%s'.", mp->argv[0]); + free_execve_args(envp); return -1; } + retval = tor_spawn_background(mp->argv[0], (const char **)mp->argv, + (const char **)envp, &mp->process_handle); + /* free the memory allocated by set_managed_proxy_environment(). */ free_execve_args(envp); - /* Set stdout/stderr pipes to be non-blocking */ -#ifdef _WIN32 - { - u_long nonblocking = 1; - ioctlsocket(stdout_pipe, FIONBIO, &nonblocking); - } -#else - fcntl(stdout_pipe, F_SETFL, O_NONBLOCK); #endif - /* Open the buffered IO streams */ - stdout_read = fdopen(stdout_pipe, "r"); + if (retval == PROCESS_STATUS_ERROR) { + log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.", + mp->argv[0]); + return -1; + } - log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid); + log_info(LD_CONFIG, "Managed proxy at '%s' has spawned with PID '%d'.", + mp->argv[0], tor_process_get_pid(mp->process_handle)); mp->conf_state = PT_PROTO_LAUNCHED; - mp->_stdout = stdout_read; - mp->pid = pid; -#endif + return 0; } @@ -314,7 +319,8 @@ pt_configure_remaining_proxies(void) log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!", unconfigured_proxies_n); SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { - tor_assert(mp->conf_state != PT_PROTO_BROKEN); + tor_assert(mp->conf_state != PT_PROTO_BROKEN || + mp->conf_state != PT_PROTO_FAILED_LAUNCH); if (mp->got_hup) { mp->got_hup = 0; @@ -343,6 +349,65 @@ pt_configure_remaining_proxies(void) } SMARTLIST_FOREACH_END(mp); } +#ifdef MS_WINDOWS + +/** 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; + + /* 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); + + 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"); + 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_create(); + tor_split_lines(lines, stdout_buf, pos); + + /* Handle lines. */ + SMARTLIST_FOREACH_BEGIN(lines, const char *, line) { + handle_proxy_line(line, mp); + if (proxy_configuration_finished(mp)) + goto done; + } SMARTLIST_FOREACH_END(line); + + done: + /* if the proxy finished configuring, exit the loop. */ + if (proxy_configuration_finished(mp)) + handle_finished_proxy(mp); + + if (lines) + smartlist_free(lines); +} + +#else /* MS_WINDOWS */ + /** Attempt to continue configuring managed proxy <b>mp</b>. */ static void configure_proxy(managed_proxy_t *mp) @@ -352,15 +417,19 @@ configure_proxy(managed_proxy_t *mp) /* if we haven't launched the proxy yet, do it now */ if (mp->conf_state == PT_PROTO_INFANT) { - launch_managed_proxy(mp); + 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(mp->_stdout, stdout_buf, - sizeof(stdout_buf) - 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); @@ -382,6 +451,8 @@ configure_proxy(managed_proxy_t *mp) } } +#endif /* MS_WINDOWS */ + /** Register server managed proxy <b>mp</b> transports to state */ static void register_server_proxy(managed_proxy_t *mp) @@ -394,6 +465,10 @@ register_server_proxy(managed_proxy_t *mp) tor_assert(mp->conf_state != PT_PROTO_COMPLETED); SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) { save_transport_to_state(t->name, &t->addr, t->port); + /* LOG_WARN so that the bridge operator can easily find the + transport's port in the log file and send it to the users. */ + log_warn(LD_GENERAL, "Registered server transport '%s' at '%s:%d'", + t->name, fmt_addr(&t->addr), (int)t->port); smartlist_add(sm_tmp, tor_strdup(t->name)); } SMARTLIST_FOREACH_END(t); @@ -453,7 +528,8 @@ register_proxy(managed_proxy_t *mp) /** Free memory allocated by managed proxy <b>mp</b>. */ static void -managed_proxy_destroy(managed_proxy_t *mp) +managed_proxy_destroy(managed_proxy_t *mp, + int also_terminate_process) { if (mp->conf_state != PT_PROTO_COMPLETED) SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t)); @@ -470,15 +546,11 @@ managed_proxy_destroy(managed_proxy_t *mp) /* remove it from the list of managed proxies */ smartlist_remove(managed_proxy_list, mp); - /* close its stdout stream */ - if (mp->_stdout) - fclose(mp->_stdout); - /* free the argv */ free_execve_args(mp->argv); - if (mp->pid) - tor_terminate_process(mp->pid); + tor_process_handle_destroy(mp->process_handle, also_terminate_process); + mp->process_handle = NULL; tor_free(mp); } @@ -489,7 +561,10 @@ handle_finished_proxy(managed_proxy_t *mp) { switch (mp->conf_state) { case PT_PROTO_BROKEN: /* if broken: */ - managed_proxy_destroy(mp); /* annihilate it. */ + managed_proxy_destroy(mp, 1); /* annihilate it. */ + break; + case PT_PROTO_FAILED_LAUNCH: /* if it failed before launching: */ + managed_proxy_destroy(mp, 0); /* destroy it but don't terminate */ break; case PT_PROTO_CONFIGURED: /* if configured correctly: */ register_proxy(mp); /* register its transports */ @@ -515,7 +590,8 @@ static INLINE int proxy_configuration_finished(const managed_proxy_t *mp) { return (mp->conf_state == PT_PROTO_CONFIGURED || - mp->conf_state == PT_PROTO_BROKEN); + mp->conf_state == PT_PROTO_BROKEN || + mp->conf_state == PT_PROTO_FAILED_LAUNCH); } /** This function is called when a proxy sends an {S,C}METHODS DONE message. */ @@ -537,7 +613,7 @@ handle_methods_done(const managed_proxy_t *mp) void handle_proxy_line(const char *line, managed_proxy_t *mp) { - log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line); + log_debug(LD_GENERAL, "Got a line from managed proxy: %s", line); if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) { log_warn(LD_GENERAL, "Managed proxy configuration line is too small. " @@ -614,14 +690,16 @@ handle_proxy_line(const char *line, managed_proxy_t *mp) return; } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) { log_warn(LD_GENERAL, "Could not launch managed proxy executable!"); - goto err; + mp->conf_state = PT_PROTO_FAILED_LAUNCH; + return; } log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line); err: mp->conf_state = PT_PROTO_BROKEN; - return; + log_warn(LD_CONFIG, "Managed proxy at '%s' failed the configuration protocol" + " and will be destroyed.", mp->argv ? mp->argv[0] : ""); } /** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */ @@ -841,13 +919,18 @@ static char * get_bindaddr_for_proxy(const managed_proxy_t *mp) { char *bindaddr = NULL; + char *bindaddr_tmp = NULL; smartlist_t *string_tmp = smartlist_create(); tor_assert(mp->is_server); SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t) { - tor_asprintf(&bindaddr, "%s-%s", t, get_bindaddr_for_transport(t)); + bindaddr_tmp = get_bindaddr_for_transport(t); + + tor_asprintf(&bindaddr, "%s-%s", t, bindaddr_tmp); smartlist_add(string_tmp, bindaddr); + + tor_free(bindaddr_tmp); } SMARTLIST_FOREACH_END(t); bindaddr = smartlist_join_strings(string_tmp, ",", 0, NULL); @@ -858,8 +941,114 @@ get_bindaddr_for_proxy(const managed_proxy_t *mp) return bindaddr; } -/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */ +#ifdef MS_WINDOWS + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ static void +set_managed_proxy_environment(LPVOID *envp, const managed_proxy_t *mp) +{ + const or_options_t *options = get_options(); + + char *tmp=NULL; + char *state_tmp=NULL; + char *state_env=NULL; + char *transports_to_launch=NULL; + char *transports_env=NULL; + char *bindaddr_tmp=NULL; + char *bindaddr_env=NULL; + char *orport_env=NULL; + + char version_env[31]; /* XXX temp */ + char extended_env[43]; /* XXX temp */ + + int env_size = 0; + + /* A smartlist carrying all the env. variables that the managed + proxy should inherit. */ + smartlist_t *envs = smartlist_create(); + + /* Copy the whole environment of the Tor process. + It should also copy PATH and HOME of the Tor process.*/ + char **environ_tmp = environ; + while (*environ_tmp) + smartlist_add(envs, *environ_tmp++); + + /* Create the TOR_PT_* environment variables. */ + state_tmp = get_datadir_fname("pt_state/"); /* XXX temp */ + tor_asprintf(&state_env, "TOR_PT_STATE_LOCATION=%s", state_tmp); + + strcpy(version_env, "TOR_PT_MANAGED_TRANSPORT_VER=1"); + + transports_to_launch = + smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); + + tor_asprintf(&transports_env, + mp->is_server ? + "TOR_PT_SERVER_TRANSPORTS=%s" : "TOR_PT_CLIENT_TRANSPORTS=%s", + transports_to_launch); + + smartlist_add(envs, state_env); + smartlist_add(envs, version_env); + smartlist_add(envs, transports_env); + + if (mp->is_server) { + tor_asprintf(&orport_env, "TOR_PT_ORPORT=127.0.0.1:%s", + options->ORPort->value); + + bindaddr_tmp = get_bindaddr_for_proxy(mp); + tor_asprintf(&bindaddr_env, "TOR_PT_SERVER_BINDADDR=%s", bindaddr_tmp); + + strcpy(extended_env, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200"); + + smartlist_add(envs, orport_env); + smartlist_add(envs, extended_env); + smartlist_add(envs, bindaddr_env); + } + + /* It seems like some versions of Windows need a sorted lpEnvironment + block. */ + smartlist_sort_strings(envs); + + /* An environment block consists of a null-terminated block of + null-terminated strings: */ + + /* Calculate the block's size. */ + SMARTLIST_FOREACH(envs, const char *, s, + env_size += strlen(s) + 1); + env_size += 1; /* space for last NUL */ + + *envp = tor_malloc(env_size); + tmp = *envp; + + /* Create the block. */ + SMARTLIST_FOREACH_BEGIN(envs, const char *, s) { + memcpy(tmp, s, strlen(s)); /* copy the env. variable string */ + tmp += strlen(s); + memset(tmp, '\0', 1); /* append NUL at the end of the string */ + tmp += 1; + } SMARTLIST_FOREACH_END(s); + memset(tmp, '\0', 1); /* last NUL */ + + /* Finally, free the whole mess. */ + tor_free(state_tmp); + tor_free(state_env); + tor_free(transports_to_launch); + tor_free(transports_env); + tor_free(bindaddr_tmp); + tor_free(bindaddr_env); + tor_free(orport_env); + + smartlist_free(envs); +} + +#else /* MS_WINDOWS */ + +/** Prepare the environment <b>envp</b> of managed proxy <b>mp</b>. + * <b>envp</b> is allocated on the heap and should be freed by the + * caller after its use. */ +static int set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) { const or_options_t *options = get_options(); @@ -867,7 +1056,10 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) char *state_loc=NULL; char *transports_to_launch=NULL; char *bindaddr=NULL; + char *home_env=NULL; + char *path_env=NULL; + int r = -1; int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT; /* allocate enough space for our env. vars and a NULL pointer */ @@ -878,15 +1070,21 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) transports_to_launch = smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL); - tor_asprintf(tmp++, "HOME=%s", getenv("HOME")); - tor_asprintf(tmp++, "PATH=%s", getenv("PATH")); + home_env = getenv("HOME"); + path_env = getenv("PATH"); + if (!home_env || !path_env) + goto done; + + tor_asprintf(tmp++, "HOME=%s", home_env); + tor_asprintf(tmp++, "PATH=%s", path_env); tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc); tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */ if (mp->is_server) { bindaddr = get_bindaddr_for_proxy(mp); /* XXX temp */ - tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort); + tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", + router_get_advertised_or_port(options)); tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr); tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch); /* XXX temp*/ @@ -896,11 +1094,18 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) } *tmp = NULL; + r = 0; + + done: tor_free(state_loc); tor_free(transports_to_launch); tor_free(bindaddr); + + return r; } +#endif /* MS_WINDOWS */ + /** Create and return a new managed proxy for <b>transport</b> using * <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server * managed proxy. */ @@ -995,9 +1200,9 @@ pt_prepare_proxy_list_for_config_read(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { /* Destroy unconfigured proxies. */ if (mp->conf_state != PT_PROTO_COMPLETED) { - managed_proxy_destroy(mp); - unconfigured_proxies_n--; - continue; + managed_proxy_destroy(mp, 1); + unconfigured_proxies_n--; + continue; } tor_assert(mp->conf_state == PT_PROTO_COMPLETED); @@ -1024,7 +1229,7 @@ sweep_proxy_list(void) SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) { if (mp->marked_for_removal) { SMARTLIST_DEL_CURRENT(managed_proxy_list, mp); - managed_proxy_destroy(mp); + managed_proxy_destroy(mp, 1); } } SMARTLIST_FOREACH_END(mp); } @@ -1039,7 +1244,7 @@ pt_free_all(void) free them. Otherwise, it hasn't registered its transports yet and we should free them here. */ SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp, - managed_proxy_destroy(mp)); + managed_proxy_destroy(mp, 1)); smartlist_free(managed_proxy_list); managed_proxy_list=NULL; diff --git a/src/or/transports.h b/src/or/transports.h index 4a93387596..314af2b3a0 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -36,7 +36,8 @@ enum pt_proto_state { PT_PROTO_ACCEPTING_METHODS, /* accepting methods */ PT_PROTO_CONFIGURED, /* configured successfully */ PT_PROTO_COMPLETED, /* configure and registered its transports */ - PT_PROTO_BROKEN + PT_PROTO_BROKEN, /* broke during the protocol */ + PT_PROTO_FAILED_LAUNCH /* failed while launching */ }; /** Structure containing information of a managed proxy. */ @@ -47,8 +48,8 @@ typedef struct { int is_server; /* is it a server proxy? */ - FILE *_stdout; /* a stream to its stdout - (closed in managed_proxy_destroy()) */ + /* A pointer to the process handle of this managed proxy. */ + process_handle_t *process_handle; int pid; /* The Process ID this managed proxy is using. */ |