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);