commit c3a0f757964de0e8a24911d72abff5df20bb323c Author: David Goulet dgoulet@torproject.org Date: Tue Jul 21 09:28:52 2020 -0400
relay: Automatically Enable an IPv6 ORPort
This commit makes it that if the ORPort is set with a single port, it will bind to both global listen IPv4 and IPv6 addresses.
To pin an "ORPort <PORT>" to be IPv4 or IPv6, the IPv4Only/IPv6Only flags are honored thus this will _only_ bind on IPv6 for that port value:
ORPort 9050 IPv6Only Results in: [::]:9050
ORPort 9051 IPv4Only Results in: [0.0.0.0]:9051
Attempting to configure an explicit IPv4 address with IPv6Only flag is an error and vice versa.
Closes #33246
Signed-off-by: David Goulet dgoulet@torproject.org --- changes/ticket33246 | 3 +++ src/app/config/config.c | 51 ++++++++++++++++++++++++++++++---------- src/core/or/port_cfg_st.h | 2 ++ src/feature/relay/relay_config.c | 8 +++++++ src/test/test_config.c | 38 ++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 12 deletions(-)
diff --git a/changes/ticket33246 b/changes/ticket33246 new file mode 100644 index 0000000000..c44c2992b0 --- /dev/null +++ b/changes/ticket33246 @@ -0,0 +1,3 @@ + o Major feature (relay, IPv6): + - Relays now automatically bind on IPv6 for their ORPort unless specified + otherwise with the IPv4Only flag. Closes ticket 33246. diff --git a/src/app/config/config.c b/src/app/config/config.c index 1671f295e3..a70c1d651e 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -5831,6 +5831,15 @@ port_parse_config(smartlist_t *out, int got_zero_port=0, got_nonzero_port=0; char *unix_socket_path = NULL; port_cfg_t *cfg = NULL; + bool addr_is_explicit = false; + int family = -1; + + /* Parse default address. This can fail for Unix socket for instance so + * family can be -1 and the default_addr will be made UNSPEC. */ + tor_addr_t default_addr = TOR_ADDR_NULL; + if (defaultaddr) { + family = tor_addr_parse(&default_addr, defaultaddr); + }
/* If there's no FooPort, then maybe make a default one. */ if (! ports) { @@ -5907,8 +5916,8 @@ port_parse_config(smartlist_t *out, port = 1; } else if (!strcasecmp(addrport, "auto")) { port = CFG_AUTO_PORT; - int af = tor_addr_parse(&addr, defaultaddr); - tor_assert(af >= 0); + tor_assert(family >= 0); + tor_addr_copy(&addr, &default_addr); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); port = CFG_AUTO_PORT; @@ -5924,14 +5933,20 @@ port_parse_config(smartlist_t *out, "9050" might be a valid address. */ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); if (ok) { - int af = tor_addr_parse(&addr, defaultaddr); - tor_assert(af >= 0); + tor_assert(family >= 0); + tor_addr_copy(&addr, &default_addr); } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { if (ptmp == 0) { log_warn(LD_CONFIG, "%sPort line has address but no port", portname); goto err; } + if (family != -1 && tor_addr_family(&addr) != family) { + /* This means we are parsing another ORPort family but we are + * attempting to find the default address' family ORPort. */ + goto ignore; + } port = ptmp; + addr_is_explicit = true; } else { log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort", escaped(addrport), portname); @@ -5942,6 +5957,7 @@ port_parse_config(smartlist_t *out, /* Default port_cfg_t object initialization */ cfg = port_cfg_new(unix_socket_path ? strlen(unix_socket_path) : 0);
+ cfg->explicit_addr = addr_is_explicit; if (unix_socket_path && default_to_group_writable) cfg->is_group_writable = 1;
@@ -5984,15 +6000,25 @@ port_parse_config(smartlist_t *out, } if (cfg->server_cfg.bind_ipv4_only && tor_addr_family(&addr) != AF_INET) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", - portname); - goto err; + if (cfg->explicit_addr) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + portname); + goto err; + } + /* This ORPort is IPv4Only but the default address is IPv6, ignore it + * since this will be configured with an IPv4 default address. */ + goto ignore; } if (cfg->server_cfg.bind_ipv6_only && tor_addr_family(&addr) != AF_INET6) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", - portname); - goto err; + if (cfg->explicit_addr) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + /* This ORPort is IPv6Only but the default address is IPv4, ignore it + * since this will be configured with an IPv6 default address. */ + goto ignore; } } else { /* This is a client port; parse isolation options */ @@ -6205,9 +6231,10 @@ port_parse_config(smartlist_t *out, smartlist_add(out, cfg); /* out owns cfg now, don't re-use or free it */ cfg = NULL; - } else { - tor_free(cfg); } + + ignore: + tor_free(cfg); SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); tor_free(addrport); diff --git a/src/core/or/port_cfg_st.h b/src/core/or/port_cfg_st.h index 064e679d78..f8ff6f8cc8 100644 --- a/src/core/or/port_cfg_st.h +++ b/src/core/or/port_cfg_st.h @@ -26,6 +26,8 @@ struct port_cfg_t { unsigned is_group_writable : 1; unsigned is_world_writable : 1; unsigned relax_dirmode_check : 1; + unsigned explicit_addr : 1; /** Indicate if address was explicitly set or + * we are using the default address. */
entry_port_cfg_t entry_cfg;
diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index 7cb7f2ccfd..cddb031f8f 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -270,6 +270,14 @@ port_parse_ports_relay(or_options_t *options, *msg = tor_strdup("Invalid ORPort configuration"); goto err; } + if (port_parse_config(ports, + options->ORPort_lines, + "OR", CONN_TYPE_OR_LISTENER, + "[::]", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort configuration"); + goto err; + } if (port_parse_config(ports, options->ExtORPort_lines, "ExtOR", CONN_TYPE_EXT_OR_LISTENER, diff --git a/src/test/test_config.c b/src/test/test_config.c index e61a62818d..121b51e925 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -5162,6 +5162,44 @@ test_config_parse_port_config__ports__server_options(void *data) 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1);
+ /* Default address is IPv4 but pass IPv6Only flag. Should be ignored. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", "9050 IPv6Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "127.0.0.1", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, 0); + + /* Default address is IPv6 but pass IPv4Only flag. Should be ignored. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", "9050 IPv4Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "[::]", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, 0); + + /* Explicit address is IPv6 but pass IPv4Only flag. Should error. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", + "[4242::4242]:9050 IPv4Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "[::]", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, -1); + + /* Explicit address is IPv4 but pass IPv6Only flag. Should error. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", + "1.2.3.4:9050 IPv6Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "127.0.0.1", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, -1); + done: if (slout) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));