diff --git a/changes/feature18753 b/changes/feature18753
new file mode 100644
index 0000000000..2eb0e359da
--- /dev/null
+++ b/changes/feature18753
@@ -0,0 +1,6 @@
+ o Minor features (unix domain sockets):
+ - When configuring a unix domain socket for a SocksPort,
+ ControlPort, or Hidden service, you can now wrap the address
+ in quotes, using C-style escapes inside the quotes. This
+ allows unix domain socket paths to contain spaces.
+
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 354478a42e..9d8b631e35 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -323,6 +323,8 @@ GENERAL OPTIONS
any process on the local host to control it. (Setting both authentication
methods means either method is sufficient to authenticate to Tor.) This
option is required for many Tor controllers; most use the value of 9051.
+ If a unix domain socket is used, you may quote the path using standard
+ C escape sequences.
Set it to "auto" to have Tor pick a port for you. (Default: 0) +
+
Recognized flags are...
@@ -1032,7 +1034,9 @@ The following options are useful only for clients (that is, if
applications. Set this to 0 if you don't want to allow application
connections via SOCKS. Set it to "auto" to have Tor pick a port for
you. This directive can be specified multiple times to bind
- to multiple addresses/ports. (Default: 9050) +
+ to multiple addresses/ports. If a unix domain socket is used, you may
+ quote the path using standard C escape sequences.
+ (Default: 9050) +
+
NOTE: Although this option allows you to specify an IP address
other than localhost, you should do so only with extreme caution.
@@ -2343,7 +2347,8 @@ The following options are used to configure a hidden service.
recent HiddenServiceDir. By default, this option maps the virtual port to
the same port on 127.0.0.1 over TCP. You may override the target port,
address, or both by specifying a target of addr, port, addr:port, or
- **unix:**__path__. (You can specify an IPv6 target as [addr]:port.)
+ **unix:**__path__. (You can specify an IPv6 target as [addr]:port. Unix
+ paths may be quoted, and may use standard C escapes.)
You may also have multiple lines with the same VIRTPORT: when a user
connects to that VIRTPORT, one of the TARGETs from those lines will be
chosen at random.
diff --git a/src/common/util.c b/src/common/util.c
index d1c87d3e85..9162967907 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -2906,7 +2906,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
* provided), and return a pointer to the position in s immediately
* after the string. On failure, return NULL.
*/
-static const char *
+const char *
unescape_string(const char *s, char **result, size_t *size_out)
{
const char *cp;
diff --git a/src/common/util.h b/src/common/util.h
index 57605ccfd1..479fc8d610 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -385,6 +385,7 @@ MOCK_DECL_ATTR(char *, read_file_to_str,
char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
size_t *sz_out)
ATTR_MALLOC;
+const char *unescape_string(const char *s, char **result, size_t *size_out);
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
const char **err_out);
diff --git a/src/or/config.c b/src/or/config.c
index 166f232f46..e3aa6d9d69 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -68,6 +68,9 @@
/* Prefix used to indicate a Unix socket in a FooPort configuration. */
static const char unix_socket_prefix[] = "unix:";
+/* Prefix used to indicate a Unix socket with spaces in it, in a FooPort
+ * configuration. */
+static const char unix_q_socket_prefix[] = "unix:\"";
/** A list of abbreviations and aliases to map command-line options, obsolete
* option names, or alternative option names, to their current values. */
@@ -6296,55 +6299,62 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
} SMARTLIST_FOREACH_END(port);
}
-#ifdef HAVE_SYS_UN_H
-
-/** Parse the given addrport and set path_out if a Unix socket
- * path is found. Return 0 on success. On error, a negative value is
- * returned, -ENOENT if no Unix statement found, -EINVAL if the socket path
- * is empty and -ENOSYS if AF_UNIX is not supported (see function in the
- * #else statement below). */
-
+/**
+ * Take a string (line) that begins with either an address:port, a
+ * port, or an AF_UNIX address, optionally quoted, prefixed with
+ * "unix:". Parse that line, and on success, set addrport_out to a new
+ * string containing the beginning portion (without prefix). Iff there was a
+ * unix: prefix, set is_unix_out to true. On success, also set
+ * rest_out to point to the part of the line after the address portion.
+ *
+ * Return 0 on success, -1 on failure.
+ */
int
-config_parse_unix_port(const char *addrport, char **path_out)
+port_cfg_line_extract_addrport(const char *line,
+ char **addrport_out,
+ int *is_unix_out,
+ const char **rest_out)
{
- tor_assert(path_out);
- tor_assert(addrport);
+ tor_assert(line);
+ tor_assert(addrport_out);
+ tor_assert(is_unix_out);
+ tor_assert(rest_out);
- if (strcmpstart(addrport, unix_socket_prefix)) {
- /* Not a Unix socket path. */
- return -ENOENT;
+ line = eat_whitespace(line);
+
+ if (!strcmpstart(line, unix_q_socket_prefix)) {
+ // It starts with unix:"
+ size_t sz;
+ *is_unix_out = 1;
+ *addrport_out = NULL;
+ line += strlen(unix_socket_prefix); /*No q: Keep the quote */
+ *rest_out = unescape_string(line, addrport_out, &sz);
+ if (!*rest_out || (*addrport_out && sz != strlen(*addrport_out))) {
+ tor_free(*addrport_out);
+ return -1;
+ }
+ *rest_out = eat_whitespace(*rest_out);
+ return 0;
+ } else {
+ // Is there a unix: prefix?
+ if (!strcmpstart(line, unix_socket_prefix)) {
+ line += strlen(unix_socket_prefix);
+ *is_unix_out = 1;
+ } else {
+ *is_unix_out = 0;
+ }
+
+ const char *end = find_whitespace(line);
+ if (BUG(!end)) {
+ end = strchr(line, '\0'); // LCOV_EXCL_LINE -- this can't be NULL
+ }
+ tor_assert(end && end >= line);
+ *addrport_out = tor_strndup(line, end - line);
+ *rest_out = eat_whitespace(end);
+ return 0;
}
-
- if (strlen(addrport + strlen(unix_socket_prefix)) == 0) {
- /* Empty socket path, not very usable. */
- return -EINVAL;
- }
-
- *path_out = tor_strdup(addrport + strlen(unix_socket_prefix));
- return 0;
}
-#else /* defined(HAVE_SYS_UN_H) */
-
-int
-config_parse_unix_port(const char *addrport, char **path_out)
-{
- tor_assert(path_out);
- tor_assert(addrport);
-
- if (strcmpstart(addrport, unix_socket_prefix)) {
- /* Not a Unix socket path. */
- return -ENOENT;
- }
-
- log_warn(LD_CONFIG,
- "Port configuration %s is for an AF_UNIX socket, but we have no"
- "support available on this platform",
- escaped(addrport));
- return -ENOSYS;
-}
-#endif /* defined(HAVE_SYS_UN_H) */
-
static void
warn_client_dns_cache(const char *option, int disabling)
{
@@ -6523,16 +6533,16 @@ parse_port_config(smartlist_t *out,
/* At last we can actually parse the FooPort lines. The syntax is:
* [Addr:](Port|auto) [Options].*/
elts = smartlist_new();
+ char *addrport = NULL;
for (; ports; ports = ports->next) {
tor_addr_t addr;
- int port, ret;
+ int port;
int sessiongroup = SESSION_GROUP_UNSET;
unsigned isolation = ISO_DEFAULT;
int prefer_no_auth = 0;
int socks_iso_keep_alive = 0;
- char *addrport;
uint16_t ptmp=0;
int ok;
/* This must be kept in sync with port_cfg_new's defaults */
@@ -6546,23 +6556,31 @@ parse_port_config(smartlist_t *out,
relax_dirmode_check = 0,
has_used_unix_socket_only_option = 0;
- smartlist_split_string(elts, ports->value, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(elts) == 0) {
- log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname);
+ int is_unix_tagged_addr = 0;
+ const char *rest_of_line = NULL;
+ if (port_cfg_line_extract_addrport(ports->value,
+ &addrport, &is_unix_tagged_addr, &rest_of_line)<0) {
+ log_warn(LD_CONFIG, "Invalid %sPort line with unparsable address",
+ portname);
+ goto err;
+ }
+ if (strlen(addrport) == 0) {
+ log_warn(LD_CONFIG, "Invalid %sPort line with no address", portname);
goto err;
}
- /* Now parse the addr/port value */
- addrport = smartlist_get(elts, 0);
+ /* Split the remainder... */
+ smartlist_split_string(elts, rest_of_line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
/* Let's start to check if it's a Unix socket path. */
- ret = config_parse_unix_port(addrport, &unix_socket_path);
- if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL) {
- log_warn(LD_CONFIG, "Empty Unix socket path.");
- }
+ if (is_unix_tagged_addr) {
+#ifndef HAVE_SYS_UN_H
+ log_warn(LD_CONFIG, "Unix sockets not supported on this system.");
goto err;
+#endif
+ unix_socket_path = addrport;
+ addrport = NULL;
}
if (unix_socket_path &&
@@ -6620,9 +6638,6 @@ parse_port_config(smartlist_t *out,
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")) {
@@ -6670,8 +6685,6 @@ parse_port_config(smartlist_t *out,
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="),
@@ -6895,6 +6908,7 @@ parse_port_config(smartlist_t *out,
}
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_clear(elts);
+ tor_free(addrport);
}
if (warn_nonlocal && out) {
@@ -6918,6 +6932,7 @@ parse_port_config(smartlist_t *out,
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_free(elts);
tor_free(unix_socket_path);
+ tor_free(addrport);
return retval;
}
diff --git a/src/or/config.h b/src/or/config.h
index 89429329f3..6645532514 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -130,7 +130,11 @@ int addressmap_register_auto(const char *from, const char *to,
time_t expires,
addressmap_entry_source_t addrmap_source,
const char **msg);
-int config_parse_unix_port(const char *addrport, char **path_out);
+
+int port_cfg_line_extract_addrport(const char *line,
+ char **addrport_out,
+ int *is_unix_out,
+ const char **rest_out);
/** Represents the information stored in a torrc Bridge line. */
typedef struct bridge_line_t {
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 114a56b74e..a2d7a37378 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -347,22 +347,20 @@ rend_service_parse_port_config(const char *string, const char *sep,
int realport = 0;
uint16_t p;
tor_addr_t addr;
- const char *addrport;
rend_service_port_config_t *result = NULL;
unsigned int is_unix_addr = 0;
- char *socket_path = NULL;
+ const char *socket_path = NULL;
char *err_msg = NULL;
+ char *addrport = NULL;
sl = smartlist_new();
smartlist_split_string(sl, string, sep,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
+ if (smartlist_len(sl) < 1 || BUG(smartlist_len(sl) > 2)) {
if (err_msg_out)
err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
-
goto err;
}
-
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
if (!virtport) {
if (err_msg_out)
@@ -371,7 +369,6 @@ rend_service_parse_port_config(const char *string, const char *sep,
goto err;
}
-
if (smartlist_len(sl) == 1) {
/* No addr:port part; use default. */
realport = virtport;
@@ -379,17 +376,18 @@ rend_service_parse_port_config(const char *string, const char *sep,
} else {
int ret;
- addrport = smartlist_get(sl,1);
- ret = config_parse_unix_port(addrport, &socket_path);
- if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL)
- if (err_msg_out)
- err_msg = tor_strdup("Empty socket path in hidden service port "
- "configuration.");
-
+ const char *addrport_element = smartlist_get(sl,1);
+ const char *rest = NULL;
+ int is_unix;
+ ret = port_cfg_line_extract_addrport(addrport_element, &addrport,
+ &is_unix, &rest);
+ if (ret < 0) {
+ tor_asprintf(&err_msg, "Couldn't process address <%s> from hidden service "
+ "configuration", addrport_element);
goto err;
}
- if (socket_path) {
+ if (is_unix) {
+ socket_path = addrport;
is_unix_addr = 1;
} else if (strchr(addrport, ':') || strchr(addrport, '.')) {
/* else try it as an IP:port pair if it has a : or . in it */
@@ -427,10 +425,10 @@ rend_service_parse_port_config(const char *string, const char *sep,
}
err:
+ tor_free(addrport);
if (err_msg_out) *err_msg_out = err_msg;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
- if (socket_path) tor_free(socket_path);
return result;
}
diff --git a/src/test/test_config.c b/src/test/test_config.c
index ee1686c65a..4efefa8050 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -3710,6 +3710,145 @@ test_config_default_fallback_dirs(void *arg)
clear_dir_servers();
}
+static void
+test_config_port_cfg_line_extract_addrport(void *arg)
+{
+ (void)arg;
+ int unixy = 0;
+ const char *rest = NULL;
+ char *a = NULL;
+
+ tt_int_op(port_cfg_line_extract_addrport("", &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("hello", &a, &unixy, &rest),
+ OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "hello");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport(" flipperwalt gersplut",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(rest, OP_EQ, "gersplut");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport(" flipperwalt \t gersplut",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(rest, OP_EQ, "gersplut");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("flipperwalt \t gersplut",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(rest, OP_EQ, "gersplut");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:flipperwalt \t gersplut",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(rest, OP_EQ, "gersplut");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("lolol",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:lolol",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:lolol ",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport(" unix:lolol",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("foobar:lolol",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, "foobar:lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport(":lolol",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 0);
+ tt_str_op(a, OP_EQ, ":lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\"",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" ",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" foo ",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(rest, OP_EQ, "foo ");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol ol\" foo ",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lol ol");;
+ tt_str_op(rest, OP_EQ, "foo ");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol\" foo ",
+ &a, &unixy, &rest), OP_EQ, 0);
+ tt_int_op(unixy, OP_EQ, 1);
+ tt_str_op(a, OP_EQ, "lol\" ol");;
+ tt_str_op(rest, OP_EQ, "foo ");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol foo ",
+ &a, &unixy, &rest), OP_EQ, -1);
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\0\" ol foo ",
+ &a, &unixy, &rest), OP_EQ, -1);
+ tor_free(a);
+
+ done:
+ tor_free(a);
+}
+
+
static config_line_t *
mock_config_line(const char *key, const char *val)
{
@@ -4050,6 +4189,49 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
#endif
+ // Test success with quoted unix: address.
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar\" "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(smartlist_len(slout), OP_EQ, 1);
+ port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
+ tt_int_op(port_cfg->entry_cfg.dns_request, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
+ tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
+#endif
+
+ // Test failure with broken quoted unix: address.
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure with empty quoted unix: address.
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"\" "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
// Test success with OnionTrafficOnly (no DNS, no ipv4, no ipv6)
config_free_lines(config_port_valid); config_port_valid = NULL;
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
@@ -4690,6 +4872,15 @@ test_config_parse_port_config__ports__server_options(void *data)
0, CL_PORT_SERVER_OPTIONS);
tt_int_op(ret, OP_EQ, -1);
+ // Check for failure with empty unix: address.
+ 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", "unix:\"\"");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "ORPort", 0, NULL,
+ 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));
@@ -4719,6 +4910,7 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(write_to_data_subdir, TT_FORK),
CONFIG_TEST(fix_my_family, 0),
CONFIG_TEST(directory_fetch, 0),
+ CONFIG_TEST(port_cfg_line_extract_addrport, 0),
CONFIG_TEST(parse_port_config__listenaddress, 0),
CONFIG_TEST(parse_port_config__ports__no_ports_given, 0),
CONFIG_TEST(parse_port_config__ports__server_options, 0),
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 0dea8473b9..4ae35c8fe0 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -137,6 +137,8 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
tt_assert(cfg);
tt_assert(!err_msg);
+ rend_service_port_config_free(cfg);
+ cfg = NULL;
/* XXX: Someone should add tests for AF_UNIX targets if supported. */
@@ -151,6 +153,53 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("90001", sep, &err_msg);
tt_assert(!cfg);
tt_assert(err_msg);
+ tor_free(err_msg);
+
+ /* unix port */
+ cfg = NULL;
+
+ /* quoted unix port */
+ tor_free(err_msg);
+ cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
+ " ", &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+ rend_service_port_config_free(cfg);
+ cfg = NULL;
+
+ /* quoted unix port */
+ tor_free(err_msg);
+ cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
+ " ", &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+ rend_service_port_config_free(cfg);
+ cfg = NULL;
+
+ /* quoted unix port, missing end quote */
+ cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar",
+ " ", &err_msg);
+ tt_assert(!cfg);
+ tt_str_op(err_msg, OP_EQ, "Couldn't process address "
+ "from hidden service configuration");
+ tor_free(err_msg);
+
+ /* bogus IP address */
+ cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000",
+ " ", &err_msg);
+ tt_assert(!cfg);
+ tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port "
+ "configuration.");
+ tor_free(err_msg);
+
+ /* bogus port port */
+ cfg = rend_service_parse_port_config("100 99999",
+ " ", &err_msg);
+ tt_assert(!cfg);
+ tt_str_op(err_msg, OP_EQ, "Unparseable or out-of-range port \"99999\" "
+ "in hidden service port configuration.");
+ tor_free(err_msg);
+
done:
rend_service_port_config_free(cfg);