From 5f0a8dcd2cfcbafc3deb3a6717808b607a459dac Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 8 Nov 2011 16:10:38 -0500 Subject: [PATCH 01/18] Initial hacking for proposal 186. This code handles the new ORPort options, and incidentally makes all remaining port types use the new port configuration systems. There are some rough edges! It doesn't do well in the case where your Address says one thing but you say to Advertise another ORPort. It doesn't handle AllAddrs. It doesn't actually advertise anything besides the first listed advertised IPv4 ORPort and DirPort. It doesn't do port forwarding to them either. It's not tested either, it needs more documentation, and it probably forgets to put the milk back in the refrigerator. --- src/or/config.c | 563 ++++++++++++++++++++++++++++++-------------- src/or/config.h | 7 +- src/or/connection.c | 115 +-------- src/or/directory.c | 2 +- src/or/directory.h | 2 +- src/or/dirserv.c | 8 +- src/or/main.c | 5 +- src/or/or.h | 13 +- src/or/router.c | 31 ++- src/or/transports.c | 4 +- 10 files changed, 443 insertions(+), 307 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index a0cf3c80ad..76b2bcb479 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), @@ -237,7 +237,7 @@ static config_var_t _option_vars[] = { 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"), @@ -343,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"), @@ -600,8 +600,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); @@ -614,9 +617,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); @@ -675,8 +675,8 @@ static or_state_t *global_state = NULL; 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 * @@ -821,11 +821,11 @@ 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); @@ -1075,7 +1075,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) { @@ -1091,10 +1091,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; } @@ -1555,7 +1555,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; } @@ -3034,37 +3034,6 @@ options_init(or_options_t *options) config_init(&options_format, options); } -/* Check if the port number given in port_option in combination with - * the specified port in listen_options 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 options to their default * values. */ static void @@ -3290,7 +3259,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 @@ -3308,13 +3277,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) @@ -3361,7 +3324,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. " @@ -3377,19 +3342,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); @@ -3643,7 +3595,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) { @@ -3866,39 +3819,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) { @@ -4121,8 +4041,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 @@ -4213,7 +4134,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 || @@ -4243,8 +4164,8 @@ 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 != @@ -5380,12 +5301,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 ports, and * entries of the "FooListenAddress" type from the list @@ -5405,17 +5367,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 flags, 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 flags, warn * if FooListenAddress is set but FooPort is 0. * + * If CL_PORT_SERVER_OPTIONS is set in flags, do not allow stream + * isolation options in the FooPort entries; instead allow the + * server-port option set. + * * On success, if out is given, add a new port_cfg_t entry to * out 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, @@ -5426,8 +5393,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; @@ -5463,6 +5433,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; @@ -5478,12 +5459,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) */ @@ -5515,6 +5501,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); @@ -5523,7 +5511,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; } @@ -5562,56 +5550,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; + } SMARTLIST_FOREACH_END(elt); + + 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 (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 (ipv4_only && tor_addr_family(&addr) == AF_INET6) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + 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; } - sessiongroup = group; - continue; - } - if (!strcasecmpstart(elt, "No")) { - no = 1; - elt += 2; - } - if (!strcasecmpend(elt, "s")) - elt[strlen(elt)-1] = '\0'; /* kill plurals. */ + 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 (!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 (no) { + isolation &= ~isoflag; + } else { + isolation |= isoflag; + } + } SMARTLIST_FOREACH_END(elt); + } if (out && port) { port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t)); @@ -5620,14 +5659,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: @@ -5636,6 +5685,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 * options. On success, set *n_ports_out to the number of * ports that are listed and return 0. On failure, set *msg to a @@ -5645,8 +5715,8 @@ parse_client_port_config(smartlist_t *out, * new list of ports parsed from options. **/ 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; @@ -5655,7 +5725,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, @@ -5663,7 +5733,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, @@ -5671,7 +5741,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, @@ -5679,7 +5749,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, @@ -5687,16 +5757,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. */ } @@ -5709,14 +5826,102 @@ 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; +} + +/** DOCDOC */ +int +get_first_advertised_v4_port_by_type(int listener_type) +{ + 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) == AF_INET || + (tor_addr_family(&cfg->addr) == AF_UNSPEC && !cfg->ipv6_only))) { + return cfg->port; + } + } SMARTLIST_FOREACH_END(cfg); + return 0; } /** Adjust the value of options->DataDirectory, or fill it in if it's @@ -6094,7 +6299,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(); diff --git a/src/or/config.h b/src/or/config.h index e1fc5cfe9a..a202bffa50 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -64,7 +64,12 @@ 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_v4_port_by_type(int listener_type); +#define get_primary_or_port() \ + (get_first_advertised_v4_port_by_type(CONN_TYPE_OR_LISTENER)) +#define get_primary_dir_port() \ + (get_first_advertised_v4_port_by_type(CONN_TYPE_DIR_LISTENER)) int options_need_geoip_info(const or_options_t *options, const char **reason_out); diff --git a/src/or/connection.c b/src/or/connection.c index b87f922175..897b0c9dfc 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1814,6 +1814,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)) { @@ -1852,6 +1855,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 *) @@ -1888,82 +1893,6 @@ retry_listener_ports(smartlist_t *old_conns, return r; } -/** - * Launch any configured listener connections of type type. (A - * listener is configured if port_option is non-zero. If any - * ListenAddress configuration options are given in cfg, create a - * connection binding to each one. Otherwise, create a single - * connection binding to the address default_addr.) - * - * We assume that we're starting with a list of existing listener connection_t - * pointers in old_conns: 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 new_conns. - */ -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. @@ -1986,37 +1915,9 @@ retry_all_listeners(smartlist_t *replaced_conns, smartlist_add(listeners, conn); } SMARTLIST_FOREACH_END(conn); - if (! options->ClientOnly && ! options->DisableNetwork) { - 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 (!options->DisableNetwork) { - if (retry_listener_ports(listeners, - get_configured_client_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) + if (retry_listener_ports(listeners, + get_configured_ports(), + new_conns) < 0) retval = -1; /* Any members that were still in 'listeners' don't correspond to diff --git a/src/or/directory.c b/src/or/directory.c index 776b7a25f9..a9ce483e34 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3729,7 +3729,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 sc. */ #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 8fe1b18a35..f4bbca8500 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -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/main.c b/src/or/main.c index da45f5a681..1b5a56f04b 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1511,9 +1511,10 @@ run_scheduled_events(time_t now) options->PortForwarding && is_server) { #define PORT_FORWARDING_CHECK_INTERVAL 5 + /* XXXXX this should take a list of ports, not just two! */ tor_check_port_forwarding(options->PortForwardingHelper, - options->DirPort, - options->ORPort, + get_primary_dir_port(), + get_primary_or_port(), now); time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; } diff --git a/src/or/or.h b/src/or/or.h index 9d6f605024..c2db88aab6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2833,6 +2833,13 @@ 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]; @@ -2948,17 +2955,17 @@ 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? */ diff --git a/src/or/router.c b/src/or/router.c index 8fe45dd6f8..82e05dbec9 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1083,7 +1083,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. @@ -1134,7 +1134,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: @@ -1193,17 +1200,22 @@ 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". + * DOCDOC + */ 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; @@ -1214,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; } /* diff --git a/src/or/transports.c b/src/or/transports.c index 10155c4475..eaaf1a3bfb 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -12,6 +12,7 @@ #include "circuitbuild.h" #include "transports.h" #include "util.h" +#include "router.h" #ifdef MS_WINDOWS static void set_managed_proxy_environment(LPVOID *envp, @@ -1079,7 +1080,8 @@ set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp) 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*/ From 11d5a9f63d85adda25d1227befcebf249107a1bd Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 8 Nov 2011 16:51:30 -0500 Subject: [PATCH 02/18] Initial support for simplest use of prop186 or-address lines This lets a routerinfo_t have a single IPv6 or-address, and adds support for formatting and parsing those lines. --- src/or/or.h | 7 +++++++ src/or/router.c | 16 ++++++++++++++++ src/or/routerparse.c | 38 ++++++++++++++++++++++++++++++++++++++ src/test/test_dir.c | 3 +++ 4 files changed, 64 insertions(+) diff --git a/src/or/or.h b/src/or/or.h index c2db88aab6..2adaa2239c 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1722,6 +1722,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. */ diff --git a/src/or/router.c b/src/or/router.c index 82e05dbec9..2e9b256cc2 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1903,6 +1903,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. */ @@ -1955,9 +1956,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" @@ -1972,6 +1986,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, @@ -1992,6 +2007,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!"); 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 * s. */ diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 5b7ce5cabe..046a1f25b5 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -96,6 +96,8 @@ test_dir_formats(void) r1->cache_info.published_on = 0; r1->or_port = 9000; r1->dir_port = 9003; + tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::"); + r1->ipv6_orport = 9999; r1->onion_pkey = crypto_pk_dup_key(pk1); r1->identity_pkey = crypto_pk_dup_key(pk2); r1->bandwidthrate = 1000; @@ -141,6 +143,7 @@ test_dir_formats(void) test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0); strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n" + "or-address [1:2:3:4::]:9999\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" From 19a82d293615f48acc2adf1aca1160982f247261 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 8 Nov 2011 21:13:54 -0500 Subject: [PATCH 03/18] Bridges can advertise a single IPv6 address with orport This is deliberately more restrictive than we'd want to be. Needs testing! --- src/or/router.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/or/router.c b/src/or/router.c index 2e9b256cc2..c92c5bd3ef 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1494,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) { From 1c2c3314a9b8990533cc8632d0a02aa5f79950b6 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 17:40:10 +0100 Subject: [PATCH 04/18] Add some logging and comments. --- src/or/circuitbuild.c | 4 ++-- src/or/connection.c | 1 + src/or/directory.c | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b04bd10120..1cfeb030d3 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -5179,8 +5179,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/connection.c b/src/or/connection.c index 897b0c9dfc..af8663eae8 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1935,6 +1935,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 diff --git a/src/or/directory.c b/src/or/directory.c index a9ce483e34..43bef5ccc0 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -1619,9 +1619,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) { From 5bee213d236943dac2e08e04c1525e96a62f13f7 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 17:49:31 +0100 Subject: [PATCH 05/18] Turn get_first_advertised_v4_port_by_type() into get_first_advertised_port_by_type_af(). --- src/or/config.c | 15 ++++++++++----- src/or/config.h | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 76b2bcb479..c9320f48fe 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -5907,18 +5907,23 @@ get_configured_ports(void) return configured_ports; } -/** DOCDOC */ +/** Return the first advertised port of type listener_type in + address_family. */ int -get_first_advertised_v4_port_by_type(int listener_type) +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) == AF_INET || - (tor_addr_family(&cfg->addr) == AF_UNSPEC && !cfg->ipv6_only))) { - return cfg->port; + (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; diff --git a/src/or/config.h b/src/or/config.h index a202bffa50..cbba9e697d 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -65,11 +65,11 @@ int did_last_state_file_write_fail(void); int or_state_save(time_t now); const smartlist_t *get_configured_ports(void); -int get_first_advertised_v4_port_by_type(int listener_type); +int get_first_advertised_port_by_type_af(int listener_type, int address_family); #define get_primary_or_port() \ - (get_first_advertised_v4_port_by_type(CONN_TYPE_OR_LISTENER)) + (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) #define get_primary_dir_port() \ - (get_first_advertised_v4_port_by_type(CONN_TYPE_DIR_LISTENER)) + (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); From f786307ab7f601a3cb41c46df0a09758671a0bcf Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 18:29:56 +0100 Subject: [PATCH 06/18] First chunk of support for bridges on IPv6 Comments below focus on changes, see diff for added code. New type tor_addr_port_t holding an IP address and a TCP/UDP port. New flag in routerinfo_t, ipv6_preferred. This should go in the node_t instead but not now. Replace node_get_addr() with - node_get_prim_addr() for primary address, i.e. IPv4 for now - node_get_pref_addr() for preferred address, IPv4 or IPv6. Rename node_get_addr_ipv4h() node_get_prim_addr_ipv4h() for consistency. The primary address will not allways be an IPv4 address. Same for node_get_orport() -> node_get_prim_orport(). Rewrite node_is_a_configured_bridge() to take all OR ports into account. Extend argument list to extend_info_from_node and extend_info_from_router with a flag indicating if we want to use the routers primary address or the preferred address. Use the preferred address in as few situtations as possible for allowing clients to connect to bridges over IPv6. --- src/common/address.h | 7 +++ src/or/circuitbuild.c | 77 +++++++++++++++++--------- src/or/circuitbuild.h | 6 ++- src/or/circuituse.c | 5 +- src/or/control.c | 2 +- src/or/nodelist.c | 123 ++++++++++++++++++++++++++++++++++++------ src/or/nodelist.h | 16 ++++-- src/or/or.h | 2 + src/or/rendclient.c | 2 +- src/or/rendservice.c | 4 +- src/or/router.c | 56 ++++++++++++++++++- src/or/router.h | 7 +++ 12 files changed, 255 insertions(+), 52 deletions(-) diff --git a/src/common/address.h b/src/common/address.h index bc225a65ea..4568c32bf9 100644 --- a/src/common/address.h +++ b/src/common/address.h @@ -31,6 +31,13 @@ typedef struct tor_addr_t } addr; } tor_addr_t; +/** Holds an IP address and a TCP/UDP port. */ +typedef struct tor_addr_port_t +{ + tor_addr_t addr; + uint16_t port; +} tor_addr_port_t; + static INLINE const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); static INLINE uint32_t tor_addr_to_ipv4h(const tor_addr_t *a); diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 1cfeb030d3..fbd55ddfee 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -3012,7 +3012,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 +3254,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 +3325,37 @@ 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 r. */ +/** Allocate and return a new extend_info_t that can be used to build + * a circuit to or through the router r. Use the primary + * address of the router unless for_direct_connect 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; + uint16_t port; tor_assert(r); - tor_addr_from_ipv4h(&addr, r->addr); + + if (for_direct_connect) + router_get_pref_addr_port(r, &addr, &port); + else + router_get_prim_addr_port(r, &addr, &port); return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &addr, r->or_port); + r->onion_pkey, &addr, port); } -/** Allocate and return a new extend_info that can be used to build a ircuit - * to or through the node node. May return NULL if there is not - * enough info about node 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 node. Use the primary address + * of the node unless for_direct_connect is true, in which case + * the preferred address is used instead. May return NULL if there is + * not enough info about node 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); @@ -4858,18 +4872,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; - if (!node) - return 0; - if (node_get_addr(node, &addr) < 0) - return 0; - orport = node_get_orport(node); - if (orport == 0) - return 0; + int retval = 0; /* Negative. */ + smartlist_t *orports = NULL; - return get_configured_bridge_by_addr_port_digest( - &addr, orport, node->identity) != NULL; + if (!node) + goto out; + + orports = node_get_all_orports(node); + if (orports == NULL) + goto out; + + 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 addr:port 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..4382d44127 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1439,7 +1439,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); diff --git a/src/or/control.c b/src/or/control.c index e2d0eece02..65b161996a 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2402,7 +2402,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/nodelist.c b/src/or/nodelist.c index b93b919c13..08dcde6f02 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -646,24 +646,59 @@ node_exit_policy_rejects_all(const node_t *node) return 1; } -/** Copy the address for node into *addr_out. */ -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 node. 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, address for node into + * *addr_out. */ +void +node_get_prim_addr(const node_t *node, tor_addr_t *addr_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_addr_port(node->ri, addr_out, NULL); + } + else if (node->rs) { + tor_addr_from_ipv4h(addr_out, node->rs->addr); } - return -1; } /** Return the host-order IPv4 address for node, 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 +708,45 @@ node_get_addr_ipv4h(const node_t *node) return 0; } -/** Copy a string representation of the IP address for node into the - * len-byte buffer at buf. - */ +/** Return 1 if we prefer the IPv6 address of node, else 0. */ +static int +node_ipv6_preferred(const node_t *node) +{ + if (node->ri != NULL) + return router_ipv6_preferred(node->ri); + return 0; +} + +/** Copy the preferred address for node into + * addr_out. */ +void +node_get_pref_addr(const node_t *node, tor_addr_t *addr_out) +{ + if (node->ri) { + router_get_pref_addr_port(node->ri, addr_out, NULL); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_from_ipv4h(addr_out, node->rs->addr); + } +} + +/** Copy the preferred IPv6 address for node into + * *addr_out. */ +void +node_get_pref_ipv6_addr(const node_t *node, tor_addr_t *addr_out) +{ + if (node->ri) { + tor_addr_copy(addr_out, &node->ri->ipv6_addr); + } else if (node->rs) { + /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private + bridges but needs fixing */ + tor_addr_make_unspec(addr_out); + } +} + +/** Copy a string representation of an IP address for node into + * the len-byte buffer at buf. */ void node_get_address_string(const node_t *node, char *buf, size_t len) { @@ -701,9 +772,9 @@ node_get_declared_uptime(const node_t *node) return -1; } -/** Return node's declared or_port */ +/** Return node's declared primary (IPv4) or_port. */ uint16_t -node_get_orport(const node_t *node) +node_get_prim_orport(const node_t *node) { if (node->ri) return node->ri->or_port; @@ -713,6 +784,28 @@ node_get_orport(const node_t *node) return 0; } +/** Return node's preferred or_port. */ +uint16_t +node_get_pref_orport(const node_t *node) +{ + if (node_ipv6_preferred(node)) + return node_get_pref_ipv6_orport(node); + else + return node_get_prim_orport(node); +} + +/** Return node's preferred IPv6 or_port. */ +uint16_t +node_get_pref_ipv6_orport(const node_t *node) +{ + if (node->ri) + return node->ri->ipv6_orport; + else if (node->rs) + return 0; /* No IPv6 in routerstatus_t yet. */ + else + return 0; +} + /** Return node'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..70c76d6d13 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -37,10 +37,15 @@ 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_addr(const node_t *node, tor_addr_t *addr_out); +void node_get_pref_addr(const node_t *node, tor_addr_t *addr_out); +void node_get_pref_ipv6_addr(const node_t *node, tor_addr_t *addr_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); +uint16_t node_get_prim_orport(const node_t *node); +uint16_t node_get_pref_orport(const node_t *node); +uint16_t node_get_pref_ipv6_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 +55,11 @@ const smartlist_t *node_get_declared_family(const node_t *node); smartlist_t *nodelist_get_list(void); +/* Temporary aliases during transition to multiple addresses. */ +#define node_get_addr(n,a) node_get_prim_addr((n),(a)) +#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) +#define node_get_orport(n) node_get_prim_orport((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 2adaa2239c..c342880fbc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1760,6 +1760,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 diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 6a45207e29..cda03c21ef 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1059,7 +1059,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/rendservice.c b/src/or/rendservice.c index 0ded538bef..b4ef8b4708 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -1142,7 +1142,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) { @@ -2151,7 +2151,7 @@ rend_services_introduce(void) 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; diff --git a/src/or/router.c b/src/or/router.c index c92c5bd3ef..d0217371ed 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -893,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); @@ -1201,7 +1202,6 @@ 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". - * DOCDOC */ uint16_t router_get_advertised_or_port(const or_options_t *options) @@ -2121,6 +2121,58 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, return (int)written+1; } + +/** Copy the primary, IPv4, address and port for router into + *addr_out and *port_out. */ +void +router_get_prim_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, + uint16_t *port_out) +{ + if (addr_out != NULL) + tor_addr_from_ipv4h(addr_out, router->addr); + if (port_out != NULL) + *port_out = router->or_port; +} + +/** Copy the alternative, presumably IPv6, address and port for + router into *addr_out and *port_out. */ +void +router_get_alt_addr_port(const routerinfo_t *router, + tor_addr_t *addr_out, + uint16_t *port_out) +{ + if (addr_out != NULL) + tor_addr_copy(addr_out, &router->ipv6_addr); + if (port_out != NULL) + *port_out = router->ipv6_orport; +} + +/** Return 1 if we prefer the IPv6 address of router, 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 IP address and port for router into + *addr_out and *port_out . */ +void +router_get_pref_addr_port(const routerinfo_t *router, + tor_addr_t *addr_out, + uint16_t *port_out) +{ + if (router_ipv6_preferred(router)) + router_get_alt_addr_port(router, addr_out, port_out); + else + router_get_prim_addr_port(router, addr_out, port_out); +} + /** Load the contents of filename, find the last line starting with * end_line, ensure that its timestamp is not more than 25 hours in * the past or more than 1 hour in the future with respect to now, diff --git a/src/or/router.h b/src/or/router.h index 6a9851cdbd..4094b665dc 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -85,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_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, + uint16_t *port_out); +void router_get_alt_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, + uint16_t *port_out); +void router_get_pref_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, + uint16_t *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); From 7b02d1a73ea4b7b3d780116055bbedaa43d67809 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 18:39:27 +0100 Subject: [PATCH 07/18] Clarify function documentation. --- src/or/connection_or.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index e178f3a8c0..6990952e94 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -630,7 +630,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. + * started_here is 1 if we are the initiator of conn 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, From 6048f019710c1c4bc225e0a3205dfc45a0a25a00 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 18:42:15 +0100 Subject: [PATCH 08/18] Use preferred address when looking for bridges by routerinfo_t. This should be safe to do for all uses of get_configured_bridge_by_routerinfo(). --- src/or/circuitbuild.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index fbd55ddfee..edf73acc4b 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -4856,9 +4856,11 @@ 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); + uint16_t port; + + router_get_pref_addr_port(ri, &addr, &port); + return get_configured_bridge_by_addr_port_digest(&addr, port, + ri->cache_info.identity_digest); } /** Return 1 if ri is one of our known bridges, else 0. */ From f6ce9e4ea51d1d1ae6d3eb9d5b23d28341e6dd6d Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 18:54:20 +0100 Subject: [PATCH 09/18] Take IPv6 into account when rewriting routerinfo for a bridge and maintain ipv6_preferred. Don't touch the string representation in routerinfo_t->address. Also, set or clear the routerinfo_t->ipv6_preferred flag based on the address family of the bridge. --- src/or/circuitbuild.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index edf73acc4b..1283256b01 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -5152,19 +5152,39 @@ 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; } if (node->rs) { routerstatus_t *rs = node->rs; From f89c6196797a6a4b485b3a5eb8788e36eacd9819 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 18:59:24 +0100 Subject: [PATCH 10/18] Use the preferred address and port when initiating a connection. This is not as conservative as we could do it, f.ex. by looking at the connection and only do this for connections to bridges. A non-bridge should never have anything else than its primary IPv4 address set though, so I think this is safe. --- src/or/connection_or.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 6990952e94..771e889c91 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -648,8 +648,9 @@ connection_or_init_conn_from_address(or_connection_t *conn, 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. */ + node_get_pref_addr(r, &node_addr); + /* 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_addr)) conn->is_canonical = 1; if (!started_here) { @@ -664,7 +665,7 @@ connection_or_init_conn_from_address(or_connection_t *conn, * 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); + conn->_base.port = node_get_pref_orport(r); } conn->nickname = tor_strdup(node_get_nickname(r)); tor_free(conn->_base.address); From 529820f8baeaccc6efffde1d9b241eb9f11ce82f Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 24 Nov 2011 19:01:56 +0100 Subject: [PATCH 11/18] Use correct address family where necessary for bridges on IPv6. --- src/or/connection.c | 5 +++-- src/or/connection_edge.c | 4 ++-- src/or/connection_or.c | 2 +- src/or/directory.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index af8663eae8..7b95acac66 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)) { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index efaad79b6a..ad0e8c53f7 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -2593,7 +2593,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 +3199,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_or.c b/src/or/connection_or.c index 771e889c91..24878bfb08 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1036,7 +1036,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); diff --git a/src/or/directory.c b/src/or/directory.c index 43bef5ccc0..65d8d953a6 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -919,7 +919,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); From 2376a6ade4c5258da5512885eba28e3e0461f2a8 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 28 Nov 2011 12:15:58 +0100 Subject: [PATCH 12/18] Merge node_get_{prim,pref,pref_ipv6}_addr with their _orport counterparts. This keeps the IP address and TCP for a given OR port together, reducing the risk of using an address for one address family with a port of another. Make node_get_addr() a wrapper function for compatibility. --- src/or/connection_or.c | 12 +++--- src/or/nodelist.c | 87 ++++++++++++++---------------------------- src/or/nodelist.h | 15 +++----- 3 files changed, 41 insertions(+), 73 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 24878bfb08..470f6d2a30 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -647,11 +647,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_pref_addr(r, &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_addr)) + 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. @@ -664,12 +664,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_pref_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 diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 08dcde6f02..493c03f825 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -682,19 +682,30 @@ node_get_all_orports(const node_t *node) return sl; } -/** Copy the primary, IPv4, address for node into - * *addr_out. */ +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * node into *ap_out. */ void -node_get_prim_addr(const node_t *node, tor_addr_t *addr_out) +node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - router_get_prim_addr_port(node->ri, addr_out, NULL); + router_get_prim_addr_port(node->ri, &ap_out->addr, &ap_out->port); } else if (node->rs) { - tor_addr_from_ipv4h(addr_out, node->rs->addr); + 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 node, or 0 if it doesn't * seem to have one. */ uint32_t @@ -708,40 +719,34 @@ node_get_prim_addr_ipv4h(const node_t *node) return 0; } -/** Return 1 if we prefer the IPv6 address of node, else 0. */ -static int -node_ipv6_preferred(const node_t *node) -{ - if (node->ri != NULL) - return router_ipv6_preferred(node->ri); - return 0; -} - -/** Copy the preferred address for node into - * addr_out. */ +/** Copy the preferred OR port (IP address and TCP port) for + * node into ap_out. */ void -node_get_pref_addr(const node_t *node, tor_addr_t *addr_out) +node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - router_get_pref_addr_port(node->ri, addr_out, NULL); + router_get_pref_addr_port(node->ri, &ap_out->addr, &ap_out->port); } else if (node->rs) { /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private bridges but needs fixing */ - tor_addr_from_ipv4h(addr_out, node->rs->addr); + tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); + ap_out->port = node->rs->or_port; } } -/** Copy the preferred IPv6 address for node into - * *addr_out. */ +/** Copy the preferred IPv6 OR port (address and TCP port) for + * node into *ap_out. */ void -node_get_pref_ipv6_addr(const node_t *node, tor_addr_t *addr_out) +node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - tor_addr_copy(addr_out, &node->ri->ipv6_addr); + tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); + ap_out->port = node->ri->ipv6_orport; } else if (node->rs) { /* No IPv6 in routerstatus_t yet. XXXprop186 ok for private bridges but needs fixing */ - tor_addr_make_unspec(addr_out); + tor_addr_make_unspec(&ap_out->addr); + ap_out->port = 0; } } @@ -772,40 +777,6 @@ node_get_declared_uptime(const node_t *node) return -1; } -/** Return node's declared primary (IPv4) or_port. */ -uint16_t -node_get_prim_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 node's preferred or_port. */ -uint16_t -node_get_pref_orport(const node_t *node) -{ - if (node_ipv6_preferred(node)) - return node_get_pref_ipv6_orport(node); - else - return node_get_prim_orport(node); -} - -/** Return node's preferred IPv6 or_port. */ -uint16_t -node_get_pref_ipv6_orport(const node_t *node) -{ - if (node->ri) - return node->ri->ipv6_orport; - else if (node->rs) - return 0; /* No IPv6 in routerstatus_t yet. */ - else - return 0; -} - /** Return node'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 70c76d6d13..1b7549dade 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -38,14 +38,12 @@ int node_get_purpose(const node_t *node); int node_is_me(const node_t *node); int node_exit_policy_rejects_all(const node_t *node); smartlist_t *node_get_all_orports(const node_t *node); -void node_get_prim_addr(const node_t *node, tor_addr_t *addr_out); -void node_get_pref_addr(const node_t *node, tor_addr_t *addr_out); -void node_get_pref_ipv6_addr(const node_t *node, tor_addr_t *addr_out); +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_prim_orport(const node_t *node); -uint16_t node_get_pref_orport(const node_t *node); -uint16_t node_get_pref_ipv6_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); @@ -55,10 +53,9 @@ const smartlist_t *node_get_declared_family(const node_t *node); smartlist_t *nodelist_get_list(void); -/* Temporary aliases during transition to multiple addresses. */ -#define node_get_addr(n,a) node_get_prim_addr((n),(a)) +/* 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)) -#define node_get_orport(n) node_get_prim_orport((n)) /* XXXX These need to move out of routerlist.c */ void nodelist_refresh_countries(void); From 3b51b326e4fb71b7ec75c5c8ef5a4869f5c4acc1 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 28 Nov 2011 13:47:12 +0100 Subject: [PATCH 13/18] Make router_get_{prim,alt,pref}_addr_port take tor_addr_port_t *. Rename to *_orport for consistency with node_*. --- src/or/circuitbuild.c | 16 ++++++------- src/or/nodelist.c | 6 ++--- src/or/router.c | 54 +++++++++++++++++++------------------------ src/or/router.h | 12 +++++----- 4 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 1283256b01..faf939ec12 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -3332,16 +3332,15 @@ extend_info_alloc(const char *nickname, const char *digest, extend_info_t * extend_info_from_router(const routerinfo_t *r, int for_direct_connect) { - tor_addr_t addr; - uint16_t port; + tor_addr_port_t ap; tor_assert(r); if (for_direct_connect) - router_get_pref_addr_port(r, &addr, &port); + router_get_pref_orport(r, &ap); else - router_get_prim_addr_port(r, &addr, &port); + router_get_prim_orport(r, &ap); return extend_info_alloc(r->nickname, r->cache_info.identity_digest, - r->onion_pkey, &addr, port); + r->onion_pkey, &ap.addr, ap.port); } /** Allocate and return a new extend_info that can be used to build a @@ -4855,11 +4854,10 @@ 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; - uint16_t port; + tor_addr_port_t ap; - router_get_pref_addr_port(ri, &addr, &port); - return get_configured_bridge_by_addr_port_digest(&addr, port, + router_get_pref_orport(ri, &ap); + return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, ri->cache_info.identity_digest); } diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 493c03f825..921ce1ad34 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -688,7 +688,7 @@ void node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - router_get_prim_addr_port(node->ri, &ap_out->addr, &ap_out->port); + router_get_prim_orport(node->ri, ap_out); } else if (node->rs) { tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); @@ -725,7 +725,7 @@ void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - router_get_pref_addr_port(node->ri, &ap_out->addr, &ap_out->port); + 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 */ @@ -737,7 +737,7 @@ node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) /** Copy the preferred IPv6 OR port (address and TCP port) for * node into *ap_out. */ void -node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) +node_get_pref_ipv6_addr(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); diff --git a/src/or/router.c b/src/or/router.c index d0217371ed..a0ae79a2a7 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2122,37 +2122,33 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, } -/** Copy the primary, IPv4, address and port for router into - *addr_out and *port_out. */ +/** Copy the primary (IPv4) OR port (IP address and TCP port) for + * router into *ap_out. */ void -router_get_prim_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, - uint16_t *port_out) +router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) { - if (addr_out != NULL) - tor_addr_from_ipv4h(addr_out, router->addr); - if (port_out != NULL) - *port_out = router->or_port; + tor_assert(ap_out != NULL); + tor_addr_from_ipv4h(&ap_out->addr, router->addr); + ap_out->port = router->or_port; } -/** Copy the alternative, presumably IPv6, address and port for - router into *addr_out and *port_out. */ +/** Copy the alternative, presumably IPv6, OR port (IP address and TCP + * port) for router into *ap_out. */ void -router_get_alt_addr_port(const routerinfo_t *router, - tor_addr_t *addr_out, - uint16_t *port_out) +router_get_alt_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) { - if (addr_out != NULL) - tor_addr_copy(addr_out, &router->ipv6_addr); - if (port_out != NULL) - *port_out = router->ipv6_orport; + tor_assert(ap_out != NULL); + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; } -/** Return 1 if we prefer the IPv6 address of router, 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. */ +/** Return 1 if we prefer the IPv6 address and OR TCP port of + * router, 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) { @@ -2160,17 +2156,15 @@ router_ipv6_preferred(const routerinfo_t *router) && (router->ipv6_preferred || router->addr == 0)); } -/** Copy the preferred IP address and port for router into - *addr_out and *port_out . */ +/** Copy the preferred OR port (IP address and TCP port) for + * router into *addr_out. */ void -router_get_pref_addr_port(const routerinfo_t *router, - tor_addr_t *addr_out, - uint16_t *port_out) +router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) { if (router_ipv6_preferred(router)) - router_get_alt_addr_port(router, addr_out, port_out); + router_get_alt_orport(router, ap_out); else - router_get_prim_addr_port(router, addr_out, port_out); + router_get_prim_orport(router, ap_out); } /** Load the contents of filename, find the last line starting with diff --git a/src/or/router.h b/src/or/router.h index 4094b665dc..6095d025e4 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -85,12 +85,12 @@ 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_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, - uint16_t *port_out); -void router_get_alt_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, - uint16_t *port_out); -void router_get_pref_addr_port(const routerinfo_t *router, tor_addr_t *addr_out, - uint16_t *port_out); +void router_get_prim_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out); +void router_get_alt_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); int router_ipv6_preferred(const routerinfo_t *router); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, crypto_pk_env_t *ident_key); From 39ec781b8fa164dd2b68cec349f2b38d81fa3cbb Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 28 Nov 2011 13:51:56 +0100 Subject: [PATCH 14/18] Make the router_get_*_orport interface consistent with node_*. --- src/or/nodelist.c | 3 +-- src/or/router.c | 22 +++++++++++----------- src/or/router.h | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 921ce1ad34..7d634a138e 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -740,8 +740,7 @@ void node_get_pref_ipv6_addr(const node_t *node, tor_addr_port_t *ap_out) { if (node->ri) { - tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); - ap_out->port = node->ri->ipv6_orport; + 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 */ diff --git a/src/or/router.c b/src/or/router.c index a0ae79a2a7..6904e85157 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2132,16 +2132,6 @@ router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) ap_out->port = router->or_port; } -/** Copy the alternative, presumably IPv6, OR port (IP address and TCP - * port) for router into *ap_out. */ -void -router_get_alt_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; -} - /** Return 1 if we prefer the IPv6 address and OR TCP port of * router, else 0. * @@ -2162,11 +2152,21 @@ void router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) { if (router_ipv6_preferred(router)) - router_get_alt_orport(router, ap_out); + 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 + * router into *ap_out. */ +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 filename, find the last line starting with * end_line, ensure that its timestamp is not more than 25 hours in * the past or more than 1 hour in the future with respect to now, diff --git a/src/or/router.h b/src/or/router.h index 6095d025e4..d426b25da6 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -87,10 +87,10 @@ 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_alt_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); From 32d10bdfb3a55fe3752f85a7937e1f3f5c7e2c56 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Tue, 29 Nov 2011 11:00:43 +0100 Subject: [PATCH 15/18] Whitespace changes. --- src/or/circuitbuild.c | 8 ++++---- src/or/config.c | 8 ++++---- src/or/config.h | 3 ++- src/or/router.c | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index faf939ec12..6b7be6652d 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -4858,7 +4858,7 @@ get_configured_bridge_by_routerinfo(const routerinfo_t *ri) router_get_pref_orport(ri, &ap); return get_configured_bridge_by_addr_port_digest(&ap.addr, ap.port, - ri->cache_info.identity_digest); + ri->cache_info.identity_digest); } /** Return 1 if ri is one of our known bridges, else 0. */ @@ -4872,7 +4872,7 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) int node_is_a_configured_bridge(const node_t *node) { - int retval = 0; /* Negative. */ + int retval = 0; /* Negative. */ smartlist_t *orports = NULL; if (!node) @@ -4884,13 +4884,13 @@ node_is_a_configured_bridge(const node_t *node) SMARTLIST_FOREACH_BEGIN(orports, tor_addr_port_t *, orport) { if (get_configured_bridge_by_addr_port_digest(&orport->addr, orport->port, - node->identity) != NULL) { + node->identity) != NULL) { retval = 1; goto out; } } SMARTLIST_FOREACH_END(orport); -out: + out: if (orports != NULL) { SMARTLIST_FOREACH(orports, tor_addr_port_t *, p, tor_free(p)); smartlist_free(orports); diff --git a/src/or/config.c b/src/or/config.c index c9320f48fe..40b61872fa 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -5918,11 +5918,11 @@ get_first_advertised_port_by_type_af(int listener_type, int address_family) if (cfg->type == listener_type && !cfg->no_advertise && (tor_addr_family(&cfg->addr) == address_family || - tor_addr_family(&cfg->addr) == AF_UNSPEC)) { + 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; + (address_family == AF_INET && !cfg->ipv6_only) || + (address_family == AF_INET6 && !cfg->ipv4_only)) { + return cfg->port; } } } SMARTLIST_FOREACH_END(cfg); diff --git a/src/or/config.h b/src/or/config.h index cbba9e697d..2d94192d3b 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -65,7 +65,8 @@ int did_last_state_file_write_fail(void); int or_state_save(time_t now); const smartlist_t *get_configured_ports(void); -int get_first_advertised_port_by_type_af(int listener_type, int address_family); +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() \ diff --git a/src/or/router.c b/src/or/router.c index 6904e85157..a08a2009a7 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2121,7 +2121,6 @@ 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 * router into *ap_out. */ void @@ -2143,7 +2142,7 @@ int router_ipv6_preferred(const routerinfo_t *router) { return (!tor_addr_is_null(&router->ipv6_addr) - && (router->ipv6_preferred || router->addr == 0)); + && (router->ipv6_preferred || router->addr == 0)); } /** Copy the preferred OR port (IP address and TCP port) for @@ -2160,7 +2159,8 @@ router_get_pref_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) /** Copy the preferred IPv6 OR port (IP address and TCP port) for * router into *ap_out. */ void -router_get_pref_ipv6_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +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); From 1dca559db8525e18336144535de1d2b2e77a41b2 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Tue, 29 Nov 2011 15:25:04 +0100 Subject: [PATCH 16/18] Warn user about client ignoring non-preferred IP address for a bridge. --- src/or/circuitbuild.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 6b7be6652d..7934a2e7ff 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -5183,6 +5183,20 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) 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) { routerstatus_t *rs = node->rs; From 192de441e01b6da013f573b46e066b380848873e Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Tue, 29 Nov 2011 15:39:22 +0100 Subject: [PATCH 17/18] Add changes file. --- changes/bug3786 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changes/bug3786 diff --git a/changes/bug3786 b/changes/bug3786 new file mode 100644 index 0000000000..8e61ee0c19 --- /dev/null +++ b/changes/bug3786 @@ -0,0 +1,7 @@ + o Major features: + - Implement support for clients connecting to private bridges over + IPv6. Bridges still need at least one IPv4 address in order to + connect to other relays. Currently, adding Bridge lines with + both an IPv4 and an IPv6 address to the same bridge will most + probably result in the IPv6 address not being used. Implements + parts of proposal 186. From 5412b3728cc0af782650f38ddb8e1566443f882c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 30 Nov 2011 12:01:16 -0500 Subject: [PATCH 18/18] Rename one more recalcitrant function. --- src/or/nodelist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 7d634a138e..eafc9b8b74 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -737,7 +737,7 @@ node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) /** Copy the preferred IPv6 OR port (address and TCP port) for * node into *ap_out. */ void -node_get_pref_ipv6_addr(const node_t *node, tor_addr_port_t *ap_out) +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);