diff --git a/doc/man/tor.1.txt b/doc/man/tor.1.txt
index 1814801b710..0b89f15b7cb 100644
--- a/doc/man/tor.1.txt
+++ b/doc/man/tor.1.txt
@@ -3421,7 +3421,7 @@ The next section describes the per service options that can only be set
Number of introduction points the hidden service will have. You can't
have more than 20. (Default: 3)
-[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__]::
+[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__ [__EXPORT-CIRCUIT-ID-PROTOCOL__]]::
Configure a virtual port VIRTPORT for a hidden service. You may use this
option multiple times; each time applies to the service using the most
recent HiddenServiceDir. By default, this option maps the virtual port to
@@ -3431,7 +3431,13 @@ The next section describes the per service options that can only be set
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. Note that address-port pairs have to be comma-separated.
+ chosen at random. Note that address-port pairs have to be
+ comma-separated. +
+ +
+ You also may specify the export circuit id protocol which is similar to
+ the one specified in **HiddenServiceExportCircuitID** but the procool
+ will be applied only to this port instead of being applied globally
+ in the service.
[[HiddenServiceVersion]] **HiddenServiceVersion** **3**::
A list of rendezvous service descriptor versions to publish for the hidden
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index ea4bf00735a..3b8416a14a6 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -936,7 +936,7 @@ connected_cell_format_payload(uint8_t *payload_out,
/* This is an onion service client connection: Export the client circuit ID
* according to the HAProxy proxy protocol. */
-STATIC void
+static void
export_hs_client_circuit_id(edge_connection_t *edge_conn,
hs_circuit_id_protocol_t protocol)
{
@@ -3810,7 +3810,7 @@ begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
* connection, attach it to the circ and connect it. Return 0 on success
* or END_CIRC_AT_ORIGIN if we can't find the requested hidden service port
* where the caller should close the circuit. */
-static int
+STATIC int
handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
{
int ret;
@@ -3881,9 +3881,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
/* If it's an onion service connection, we might want to include the proxy
* protocol header: */
if (conn->hs_ident) {
- hs_circuit_id_protocol_t circuit_id_protocol =
- hs_service_exports_circuit_id(&conn->hs_ident->identity_pk);
- export_hs_client_circuit_id(conn, circuit_id_protocol);
+ export_hs_client_circuit_id(conn, conn->hs_ident->circuit_id_protocol);
}
/* Connect tor to the hidden service destination. */
@@ -4187,8 +4185,8 @@ network_reentry_is_allowed(void)
* address, but only if it's a general exit stream. (Rendezvous
* streams must not reveal what IP they connected to.)
*/
-void
-connection_exit_connect(edge_connection_t *edge_conn)
+MOCK_IMPL(void,
+connection_exit_connect,(edge_connection_t *edge_conn))
{
const tor_addr_t *addr;
uint16_t port;
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
index 966a9391d89..c53954109c6 100644
--- a/src/core/or/connection_edge.h
+++ b/src/core/or/connection_edge.h
@@ -129,7 +129,7 @@ void connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn,
int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
-void connection_exit_connect(edge_connection_t *conn);
+MOCK_DECL(void,connection_exit_connect,(edge_connection_t *conn));
int connection_edge_is_rendezvous_stream(const edge_connection_t *conn);
int connection_ap_can_use_exit(const entry_connection_t *conn,
const node_t *exit);
@@ -290,8 +290,7 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out);
STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
-STATIC void export_hs_client_circuit_id(edge_connection_t *edge_conn,
- hs_circuit_id_protocol_t protocol);
+STATIC int handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn);
struct half_edge_t;
STATIC void connection_half_edge_add(const edge_connection_t *conn,
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index c9195c29347..7b0da8d4552 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -640,6 +640,8 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
/* There is always a connection identifier at this point. Regardless of a
* Unix or TCP port, note the virtual port. */
conn->hs_ident->orig_virtual_port = chosen_port->virtual_port;
+ /* Note the export circuit id protocol to the connection. */
+ conn->hs_ident->circuit_id_protocol = chosen_port->circuit_id_protocol;
}
if (!(chosen_port->is_unix_addr)) {
@@ -677,7 +679,8 @@ hs_port_config_new(const char *socket_path)
* the provided separator and returns a new hs_port_config_t,
* or NULL and an optional error string on failure.
*
- * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)?
+ * The format is: VirtualPort SEP ((IP|RealPort|IP:RealPort|'socket':path)
+ * SEP (ExportCircuitIdProtocol)?)?
*
* IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
*/
@@ -691,6 +694,7 @@ hs_parse_port_config(const char *string, const char *sep,
uint16_t p;
tor_addr_t addr;
hs_port_config_t *result = NULL;
+ hs_circuit_id_protocol_t circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE;
unsigned int is_unix_addr = 0;
const char *socket_path = NULL;
char *err_msg = NULL;
@@ -729,12 +733,6 @@ hs_parse_port_config(const char *string, const char *sep,
goto err;
}
- if (rest && strlen(rest)) {
- err_msg = tor_strdup("HiddenServicePort parse error: invalid port "
- "mapping");
- goto err;
- }
-
if (is_unix) {
socket_path = addrport;
is_unix_addr = 1;
@@ -757,12 +755,24 @@ hs_parse_port_config(const char *string, const char *sep,
}
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */
}
+
+ if (rest && strlen(rest)) {
+ int ok;
+ circuit_id_protocol =
+ hs_parse_circuit_id_protocol(rest, &ok);
+ if (!ok) {
+ err_msg = tor_strdup("HiddenServicePort parse error: export circuit "
+ "id protocol must be 'haproxy' or 'none'.");
+ goto err;
+ }
+ }
}
/* Allow room for unix_addr */
result = hs_port_config_new(socket_path);
result->virtual_port = virtport;
result->is_unix_addr = is_unix_addr;
+ result->circuit_id_protocol = circuit_id_protocol;
if (!is_unix_addr) {
result->real_port = realport;
tor_addr_copy(&result->real_addr, &addr);
@@ -782,6 +792,33 @@ hs_parse_port_config(const char *string, const char *sep,
return result;
}
+/** Given a configuration string, parse the value as a
+ * hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is
+ * the parse value. On error, ok is set to 0 and the "none"
+ * hs_circuit_id_protocol_t is returned. */
+hs_circuit_id_protocol_t
+hs_parse_circuit_id_protocol(const char *protocol_str, int *ok)
+{
+ tor_assert(protocol_str);
+ tor_assert(ok);
+
+ hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
+ *ok = 0;
+
+ if (! strcasecmp(protocol_str, "haproxy")) {
+ *ok = 1;
+ ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
+ } else if (! strcasecmp(protocol_str, "none")) {
+ *ok = 1;
+ ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
+ } else {
+ goto err;
+ }
+
+ err:
+ return ret;
+}
+
/** Release all storage held in a hs_port_config_t. */
void
hs_port_config_free_(hs_port_config_t *p)
diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h
index a7a8f23a3ca..981e01c6e51 100644
--- a/src/feature/hs/hs_common.h
+++ b/src/feature/hs/hs_common.h
@@ -145,6 +145,15 @@ typedef enum {
RSAE_OKAY = 0 /**< Service added as expected */
} hs_service_add_ephemeral_status_t;
+/** Which protocol to use for exporting HS client circuit ID. */
+typedef enum {
+ /** Don't expose the circuit id. */
+ HS_CIRCUIT_ID_PROTOCOL_NONE,
+
+ /** Use the HAProxy proxy protocol. */
+ HS_CIRCUIT_ID_PROTOCOL_HAPROXY
+} hs_circuit_id_protocol_t;
+
/** Represents the mapping from a virtual port of a rendezvous service to a
* real port on some IP. */
typedef struct hs_port_config_t {
@@ -156,6 +165,8 @@ typedef struct hs_port_config_t {
uint16_t real_port;
/** The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */
tor_addr_t real_addr;
+ /** Does this port export the circuit ID of its clients? */
+ hs_circuit_id_protocol_t circuit_id_protocol;
/** The socket path to connect to, if is_unix_addr */
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} hs_port_config_t;
@@ -241,6 +252,8 @@ void hs_purge_last_hid_serv_requests(void);
int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn);
hs_port_config_t *hs_parse_port_config(const char *string, const char *sep,
char **err_msg_out);
+hs_circuit_id_protocol_t hs_parse_circuit_id_protocol(const char *protocol_str,
+ int *ok);
void hs_port_config_free_(hs_port_config_t *p);
#define hs_port_config_free(p) \
FREE_AND_NULL(hs_port_config_t, hs_port_config_free_, (p))
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index a76893fe1a4..677c5724ceb 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -177,34 +177,6 @@ check_value_oob(int i, const char *name, int low, int high)
#define CHECK_OOB(opts, name, low, high) \
check_value_oob((opts)->name, #name, (low), (high))
-/** Helper function: Given a configuration option and its value, parse the
- * value as a hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is
- * the parse value. On error, ok is set to 0 and the "none"
- * hs_circuit_id_protocol_t is returned. This function logs on error. */
-static hs_circuit_id_protocol_t
-helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok)
-{
- tor_assert(value);
- tor_assert(ok);
-
- hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
- *ok = 0;
-
- if (! strcasecmp(value, "haproxy")) {
- *ok = 1;
- ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
- } else if (! strcasecmp(value, "none")) {
- *ok = 1;
- ret = HS_CIRCUIT_ID_PROTOCOL_NONE;
- } else {
- log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", key);
- goto err;
- }
-
- err:
- return ret;
-}
-
/** Return the service version by trying to learn it from the key on disk if
* any. If nothing is found, the current service configured version is
* returned. */
@@ -351,10 +323,10 @@ config_service_v3(const hs_opts_t *hs_opts,
if (hs_opts->HiddenServiceExportCircuitID) {
int ok;
config->circuit_id_protocol =
- helper_parse_circuit_id_protocol("HiddenServcieExportCircuitID",
- hs_opts->HiddenServiceExportCircuitID,
- &ok);
+ hs_parse_circuit_id_protocol(hs_opts->HiddenServiceExportCircuitID, &ok);
if (!ok) {
+ log_warn(LD_CONFIG, "HiddenServiceExportCircuitID must be "
+ "'haproxy' or 'none'.");
goto err;
}
}
diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h
index cb1249cbdcc..4e47b3e40d3 100644
--- a/src/feature/hs/hs_ident.h
+++ b/src/feature/hs/hs_ident.h
@@ -109,6 +109,9 @@ typedef struct hs_ident_edge_conn_t {
* service, regardless of the internal port forwarding that might have
* happened on the service-side. */
uint16_t orig_virtual_port;
+
+ /** The export circuit id protocol that is used in this connection. */
+ hs_circuit_id_protocol_t circuit_id_protocol;
/* XXX: Client authorization. */
} hs_ident_edge_conn_t;
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 9b7b5901404..4e271f8b107 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -3998,6 +3998,12 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ,
goto err_no_close;
}
+ /* If the the export circuit id protocol is none, looks at
+ * the HiddenServiceExportCircuitID config instead. */
+ if (conn->hs_ident->circuit_id_protocol == HS_CIRCUIT_ID_PROTOCOL_NONE) {
+ conn->hs_ident->circuit_id_protocol = service->config.circuit_id_protocol;
+ }
+
/* Success. */
return 0;
err_close:
@@ -4008,19 +4014,6 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ,
return -1;
}
-/** Does the service with identity pubkey pk export the circuit IDs of
- * its clients? */
-hs_circuit_id_protocol_t
-hs_service_exports_circuit_id(const ed25519_public_key_t *pk)
-{
- hs_service_t *service = find_service(hs_service_map, pk);
- if (!service) {
- return HS_CIRCUIT_ID_PROTOCOL_NONE;
- }
-
- return service->config.circuit_id_protocol;
-}
-
/** Add to file_list every filename used by a configured hidden service, and to
* dir_list every directory path used by a configured hidden service. This is
* used by the sandbox subsystem to allowlist those. */
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index c48f4702457..2bb48e8237b 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -190,15 +190,6 @@ typedef struct hs_service_authorized_client_t {
curve25519_public_key_t client_pk;
} hs_service_authorized_client_t;
-/** Which protocol to use for exporting HS client circuit ID. */
-typedef enum {
- /** Don't expose the circuit id. */
- HS_CIRCUIT_ID_PROTOCOL_NONE,
-
- /** Use the HAProxy proxy protocol. */
- HS_CIRCUIT_ID_PROTOCOL_HAPROXY
-} hs_circuit_id_protocol_t;
-
/** Service configuration. The following are set from the torrc options either
* set by the configuration file or by the control port. Nothing else should
* change those values. */
@@ -380,9 +371,6 @@ void hs_service_upload_desc_to_dir(const char *encoded_desc,
const ed25519_public_key_t *blinded_pk,
const routerstatus_t *hsdir_rs);
-hs_circuit_id_protocol_t
-hs_service_exports_circuit_id(const ed25519_public_key_t *pk);
-
void hs_service_dump_stats(int severity);
void hs_service_circuit_cleanup_on_close(const circuit_t *circ);
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 85042e9ec26..362c46c7974 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -366,24 +366,28 @@ test_hs_parse_port_config(void *arg)
/* Test "VIRTPORT" only. */
cfg = hs_parse_port_config("80", sep, &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is port). */
hs_port_config_free(cfg);
cfg = hs_parse_port_config("80,8080", sep, &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is IPv4:port). */
hs_port_config_free(cfg);
cfg = hs_parse_port_config("80,192.0.2.1:8080", sep, &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is IPv6:port). */
hs_port_config_free(cfg);
cfg = hs_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
hs_port_config_free(cfg);
cfg = NULL;
@@ -411,6 +415,7 @@ test_hs_parse_port_config(void *arg)
cfg = hs_parse_port_config("100 unix:\"/tmp/foo bar\"",
" ", &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
hs_port_config_free(cfg);
cfg = NULL;
@@ -420,6 +425,7 @@ test_hs_parse_port_config(void *arg)
cfg = hs_parse_port_config("100 unix:\"/tmp/foo bar\"",
" ", &err_msg);
tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
tt_ptr_op(err_msg, OP_EQ, NULL);
hs_port_config_free(cfg);
cfg = NULL;
@@ -450,6 +456,29 @@ test_hs_parse_port_config(void *arg)
"in hidden service port configuration.");
tor_free(err_msg);
+ /* bad export circuit id protocol */
+ cfg = hs_parse_port_config("100 1000 badprotocol", " ", &err_msg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
+ tt_str_op(err_msg, OP_EQ, "HiddenServicePort parse error: export circuit "
+ "id protocol must be 'haproxy' or 'none'.");
+ tor_free(err_msg);
+
+ /* none export circuit id protocol */
+ cfg = hs_parse_port_config("100 1000 none", " ", &err_msg);
+ tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ hs_port_config_free(cfg);
+ cfg = NULL;
+
+ /* haproxy export circuit id protocol */
+ cfg = hs_parse_port_config("100 1000 haproxy", " ", &err_msg);
+ tt_assert(cfg);
+ tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_HAPROXY);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ hs_port_config_free(cfg);
+ cfg = NULL;
+
/* Wrong target address and port separation */
cfg = hs_parse_port_config("80,127.0.0.1 1234", sep,
&err_msg);
diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c
index 74f823f897f..2b937129394 100644
--- a/src/test/test_hs_config.c
+++ b/src/test/test_hs_config.c
@@ -151,8 +151,22 @@ test_invalid_service(void *arg)
setup_full_capture_of_logs(LOG_WARN);
ret = helper_config_service(conf, 1);
tt_int_op(ret, OP_EQ, -1);
- expect_log_msg_containing("HiddenServicePort parse error: "
- "invalid port mapping");
+ expect_log_msg_containing("HiddenServicePort parse error: export circuit "
+ "id protocol must be 'haproxy' or 'none'.");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad export circuit id protocol. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 80 127.0.0.1 badprotocol\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServicePort parse error: export circuit "
+ "id protocol must be 'haproxy' or 'none'.");
teardown_capture_of_logs();
}
@@ -190,6 +204,24 @@ test_valid_service(void *arg)
tt_int_op(ret, OP_EQ, 0);
}
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 81 127.0.0.1:81 haproxy\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 81 127.0.0.1:81 none\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
done:
;
}
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 33a3f279c67..d47ef5149c7 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -144,6 +144,12 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
return 0;
}
+static void
+mock_connection_exit_connect(edge_connection_t *edge_conn)
+{
+ tor_assert(edge_conn);
+}
+
static unsigned int num_intro_points = 0;
static unsigned int
mock_count_desc_circuit_established(const hs_service_descriptor_t *desc)
@@ -2097,6 +2103,7 @@ static void
test_export_client_circuit_id(void *arg)
{
origin_circuit_t *or_circ = NULL;
+ int retval;
size_t sz;
char *cp1=NULL, *cp2=NULL;
connection_t *conn = NULL;
@@ -2104,77 +2111,255 @@ test_export_client_circuit_id(void *arg)
(void) arg;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+ MOCK(connection_exit_connect, mock_connection_exit_connect);
hs_service_init();
/* Create service */
hs_service_t *service = helper_create_service();
- /* Check that export circuit ID detection works */
- service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE;
- tt_int_op(0, OP_EQ,
- hs_service_exports_circuit_id(&service->keys.identity_pk));
- service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
- tt_int_op(1, OP_EQ,
- hs_service_exports_circuit_id(&service->keys.identity_pk));
-
- /* Create client connection */
- conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT, CONN_TYPE_AP, 0);
-
- /* Create client edge conn hs_ident */
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- edge_conn->hs_ident = hs_ident_edge_conn_new(&service->keys.identity_pk);
- edge_conn->hs_ident->orig_virtual_port = 42;
+ /* Add HiddenServicePort directives */
+ smartlist_add(service->config.ports,
+ hs_parse_port_config("8000 8000", " ", NULL));
+ smartlist_add(service->config.ports,
+ hs_parse_port_config("8001 8001 none", " ", NULL));
+ smartlist_add(service->config.ports,
+ hs_parse_port_config("8002 8002 haproxy", " ", NULL));
/* Create rend circuit */
or_circ = origin_circuit_new();
- or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_JOINED;
- edge_conn->on_circuit = TO_CIRCUIT(or_circ);
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_S_CONNECT_REND;
+ or_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk);
or_circ->global_identifier = 666;
- /* Export circuit ID */
- export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
+ /* Setup the circuit: do the ntor key exchange */
+ uint8_t ntor_key_seed[DIGEST256_LEN] = {2};
+ retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed,
+ sizeof(ntor_key_seed), 1);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Has HiddenServiceExportCircuitID set to none. The export circuit id
+ * protocol will be determined only by HiddenServicePort directives. */
+ {
+ service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE;
+
+ /* Unspecified protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8000;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ, "");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* None protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8001;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ, "");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* HAProxy protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 8002\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+ }
+
+ /* Has HiddenServiceExportCircuitID set to none. The export circuit id
+ * protocol will be determined only by HiddenServicePort directives. */
+ {
+ service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY;
+
+ /* Unspecified protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8000;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 8000\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* None protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8001;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 8001\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* HAProxy protocol in HiddenServiceDir */
+ {
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 8002\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+ }
- /* Check contents */
- cp1 = buf_get_contents(conn->outbuf, &sz);
- tt_str_op(cp1, OP_EQ,
- "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 42\r\n");
+ /* Edge cases for circuit ids */
- /* Change circ GID and see that the reported circuit ID also changes */
- or_circ->global_identifier = 22;
+ /* Change circuit id */
+ {
+ or_circ->global_identifier = 22;
- /* check changes */
- export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
- cp2 = buf_get_contents(conn->outbuf, &sz);
- tt_str_op(cp1, OP_NE, cp2);
- tor_free(cp1);
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
- /* Check that GID with UINT32_MAX works. */
- or_circ->global_identifier = UINT32_MAX;
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
- export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
- cp1 = buf_get_contents(conn->outbuf, &sz);
- tt_str_op(cp1, OP_EQ,
- "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n");
- tor_free(cp1);
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:16 ::1 22 8002\r\n");
- /* Check that GID with UINT16_MAX works. */
- or_circ->global_identifier = UINT16_MAX;
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
- export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
- cp1 = buf_get_contents(conn->outbuf, &sz);
- tt_str_op(cp1, OP_EQ,
- "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 42\r\n");
- tor_free(cp1);
+ /* Circuit id of UINT32_MAX */
+ {
+ or_circ->global_identifier = UINT32_MAX;
+
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 8002\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* Circuit id of UINT16_MAX */
+ {
+ or_circ->global_identifier = UINT16_MAX;
+
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
- /* Check that GID with UINT16_MAX + 7 works. */
- or_circ->global_identifier = UINT16_MAX + 7;
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
- export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol);
- cp1 = buf_get_contents(conn->outbuf, &sz);
- tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 42\r\n");
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 8002\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
+
+ /* Circuit id of UINT16_MAX + 7*/
+ {
+ or_circ->global_identifier = UINT16_MAX + 7;
+
+ conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT,
+ EXIT_PURPOSE_CONNECT);
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /* Connect to the virtual port 8000 */
+ edge_conn->base_.port = 8002;
+
+ handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn);
+
+ /* Check contents */
+ cp1 = buf_get_contents(conn->outbuf, &sz);
+ tt_str_op(cp1, OP_EQ,
+ "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 8002\r\n");
+
+ connection_free_minimal(conn);
+ conn = NULL;
+ tor_free(cp1);
+ }
done:
UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(connection_exit_connect);
circuit_free_(TO_CIRCUIT(or_circ));
connection_free_minimal(conn);
hs_service_free(service);