From ea0a9b16b9459c9b45ac320e87392dbcaa799b1d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 6 Jul 2011 16:03:47 -0400 Subject: [PATCH] (Unused) backend logic for stream isolation This patch adds fields to track how streams should be isolated, and ensures that those fields are set correctly. It also adds fields to track what streams can go on a circuit, and adds functions to see whether a streams can go on a circuit and update the circuit accordingly. Those functions aren't yet called. --- src/or/circuitlist.c | 2 + src/or/config.c | 24 ++++--- src/or/connection.c | 34 +++++++--- src/or/connection_edge.c | 141 +++++++++++++++++++++++++++++++++++++++ src/or/connection_edge.h | 8 +++ src/or/dnsserv.c | 10 ++- src/or/or.h | 57 ++++++++++++++-- 7 files changed, 253 insertions(+), 23 deletions(-) diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 93f5fd3493..9f688801f3 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -550,6 +550,8 @@ circuit_free(circuit_t *circ) crypto_free_pk_env(ocirc->intro_key); rend_data_free(ocirc->rend_data); + + tor_free(ocirc->dest_address); } else { or_circuit_t *ocirc = TO_OR_CIRCUIT(circ); /* Remember cell statistics for this circuit before deallocating. */ diff --git a/src/or/config.c b/src/or/config.c index 0774b28916..2ca9c66999 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -577,6 +577,7 @@ static int parse_client_transport_line(const char *line, int validate_only); 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, char **msg_out, int *n_ports_out); static int validate_data_directory(or_options_t *options); @@ -4846,6 +4847,13 @@ parse_dir_server_line(const char *line, dirinfo_type_t required_type, return r; } +/** Free all storage held in port */ +static void +port_cfg_free(port_cfg_t *port) +{ + tor_free(port); +} + /** Warn for every port in ports that is not on a loopback address. */ static void warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname) @@ -4955,8 +4963,8 @@ parse_client_port_config(smartlist_t *out, cfg->type = listener_type; cfg->port = port ? port : defaultport; tor_addr_copy(&cfg->addr, &addr); - cfg->sessiongroup = -1; - cfg->isolate = ISO_DEFAULT; + cfg->session_group = -1; + cfg->isolation_flags = ISO_DEFAULT; smartlist_add(out, cfg); } } @@ -4974,8 +4982,8 @@ parse_client_port_config(smartlist_t *out, cfg->type = listener_type; cfg->port = defaultport; tor_addr_from_str(&cfg->addr, defaultaddr); - cfg->sessiongroup = -1; - cfg->isolate = ISO_DEFAULT; + cfg->session_group = -1; + cfg->isolation_flags = ISO_DEFAULT; smartlist_add(out, cfg); } return 0; @@ -5094,8 +5102,8 @@ parse_client_port_config(smartlist_t *out, cfg->type = listener_type; cfg->port = port; tor_addr_copy(&cfg->addr, &addr); - cfg->sessiongroup = sessiongroup; - cfg->isolate = isolation; + cfg->session_group = sessiongroup; + cfg->isolation_flags = isolation; smartlist_add(out, cfg); } SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); @@ -5169,7 +5177,7 @@ parse_client_ports(const or_options_t *options, int validate_only, if (!validate_only) { if (configured_client_ports) { SMARTLIST_FOREACH(configured_client_ports, - port_cfg_t *, p, tor_free(p)); + port_cfg_t *, p, port_cfg_free(p)); smartlist_free(configured_client_ports); } configured_client_ports = ports; @@ -5179,7 +5187,7 @@ parse_client_ports(const or_options_t *options, int validate_only, retval = 0; err: if (ports) { - SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p)); + SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p)); smartlist_free(ports); } return retval; diff --git a/src/or/connection.c b/src/or/connection.c index db0d78975a..b627dcae16 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -43,11 +43,12 @@ static connection_t *connection_create_listener( const struct sockaddr *listensockaddr, socklen_t listensocklen, int type, - char* address); + const char *address, + const port_cfg_t *portcfg); static void connection_init(time_t now, connection_t *conn, int type, int socket_family); static int connection_init_accepted_conn(connection_t *conn, - uint8_t listener_type); + const listener_connection_t *listener); static int connection_handle_listener_read(connection_t *conn, int new_type); #ifndef USE_BUFFEREVENTS static int connection_bucket_should_increase(int bucket, @@ -859,12 +860,15 @@ make_socket_reuseable(tor_socket_t sock) static connection_t * connection_create_listener(const struct sockaddr *listensockaddr, socklen_t socklen, - int type, char* address) + int type, const char *address, + const port_cfg_t *port_cfg) { + listener_connection_t *lis_conn; connection_t *conn; tor_socket_t s; /* the socket we're going to make */ uint16_t usePort = 0, gotPort = 0; int start_reading = 0; + static int global_next_session_group = -2; if (get_n_open_sockets() >= get_options()->_ConnLimit-1) { warn_too_many_conns(); @@ -981,12 +985,23 @@ connection_create_listener(const struct sockaddr *listensockaddr, set_socket_nonblocking(s); - conn = connection_new(type, listensockaddr->sa_family); + lis_conn = listener_connection_new(type, listensockaddr->sa_family); + conn = TO_CONN(lis_conn); conn->socket_family = listensockaddr->sa_family; conn->s = s; conn->address = tor_strdup(address); conn->port = gotPort; + if (port_cfg->isolation_flags) { + lis_conn->isolation_flags = port_cfg->isolation_flags; + if (port_cfg->session_group >= 0) { + lis_conn->session_group = port_cfg->session_group; + } else { + /* XXXX023 This can wrap after ~INT_MAX ports are opened. */ + lis_conn->session_group = global_next_session_group--; + } + } + if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for listener failed. Giving up."); connection_free(conn); @@ -1199,7 +1214,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) return 0; /* no need to tear down the parent */ } - if (connection_init_accepted_conn(newconn, conn->type) < 0) { + if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) { if (! newconn->marked_for_close) connection_mark_for_close(newconn); return 0; @@ -1213,7 +1228,8 @@ connection_handle_listener_read(connection_t *conn, int new_type) * and place it in circuit_wait. */ static int -connection_init_accepted_conn(connection_t *conn, uint8_t listener_type) +connection_init_accepted_conn(connection_t *conn, + const listener_connection_t *listener) { connection_start_reading(conn); @@ -1222,7 +1238,9 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type) control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0); return connection_tls_start_handshake(TO_OR_CONN(conn), 1); case CONN_TYPE_AP: - switch (listener_type) { + TO_EDGE_CONN(conn)->isolation_flags = listener->isolation_flags; + TO_EDGE_CONN(conn)->session_group = listener->session_group; + switch (TO_CONN(listener)->type) { case CONN_TYPE_AP_LISTENER: conn->state = AP_CONN_STATE_SOCKS_WAIT; break; @@ -1808,7 +1826,7 @@ retry_listener_ports(smartlist_t *old_conns, if (listensockaddr) { conn = connection_create_listener(listensockaddr, listensocklen, - port->type, address); + port->type, address, port); tor_free(listensockaddr); tor_free(address); } else { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index f2ddfc76dc..6d247dcebd 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3266,3 +3266,144 @@ parse_extended_hostname(char *address, int allowdotexit) return BAD_HOSTNAME; } +/** Return true iff a and b have isolation rules and fields that + * make it permissible to put them on the same circuit.*/ +int +connection_edge_streams_are_compatible(const edge_connection_t *a, + const edge_connection_t *b) +{ + const uint8_t iso = a->isolation_flags | b->isolation_flags; + + if ((iso & ISO_DESTPORT) && a->socks_request->port != b->socks_request->port) + return 0; + /* XXXX023 Not quite right: we care about addresses that resolve to the same + place */ + if ((iso & ISO_DESTADDR) && + strcasecmp(a->socks_request->address, b->socks_request->address)) + return 0; + /* XXXX023 Waititing for ticket #1666 */ + /* + if ((iso & ISO_SOCKSAUTH) && + strcasecmp(a->socks_request->auth, b->socks_request->auth)) + return 0; + */ + if ((iso & ISO_CLIENTPROTO) && + (TO_CONN(a)->type != TO_CONN(b)->type || + a->socks_request->socks_version != b->socks_request->socks_version)) + return 0; + if ((iso & ISO_CLIENTADDR) && + !tor_addr_eq(&TO_CONN(a)->addr, &TO_CONN(b)->addr)) + return 0; + if ((iso & ISO_SESSIONGRP) && a->session_group != b->session_group) + return 0; + + return 1; +} + +/** + * Return true iff none of the isolation flags and fields in conn + * should prevent it from being attached to circ. + */ +int +connection_edge_compatible_with_circuit(const edge_connection_t *conn, + const origin_circuit_t *circ) +{ + const uint8_t iso = conn->isolation_flags; + + /* If circ has never been used for an isolated connection, we can + * totally use it for this one. */ + if (!circ->isolation_values_set) + return 1; + + /* If circ has been used for connections having more than one value + * for some field f, it will have the corresponding bit set in + * isolation_flags_mixed. If isolation_flags_mixed has any bits + * in common with iso, then conn must be isolated from at least + * one stream that has been attached to circ. */ + if ((iso & circ->isolation_flags_mixed) != 0) { + /* For at least one field where conn is isolated, the circuit + * already has mixed streams. */ + return 0; + } + + if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port) + return 0; + /* XXXX023 Not quite right: we care about addresses that resolve to the same + place */ + if ((iso & ISO_DESTADDR) && + strcasecmp(conn->socks_request->address, circ->dest_address)) + return 0; + /* XXXX023 Waititing for ticket #1666 */ + /* + if ((iso & ISO_SOCKSAUTH) && + strcasecmp(a->socks_request->auth, b->socks_request->auth)) + return 0; + */ + if ((iso & ISO_CLIENTPROTO) && + (TO_CONN(conn)->type != circ->client_proto_type || + conn->socks_request->socks_version != circ->client_proto_socksver)) + return 0; + if ((iso & ISO_CLIENTADDR) && + !tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr)) + return 0; + if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group) + return 0; + + return 1; +} + +/** + * If dry_run is false, update circ's isolation flags and fields + * to reflect having had conn attached to it, and return 0. Otherwise, + * if dry_run is true, then make no changes to circ, and return + * a bitfield of isolation flags that we would have to set in + * isolation_flags_mixed to add conn to circ, or -1 if + * circ has had no streams attached to it. + */ +int +connection_edge_update_circuit_isolation(const edge_connection_t *conn, + origin_circuit_t *circ, + int dry_run) +{ + if (!circ->isolation_values_set) { + if (dry_run) + return -1; + circ->dest_port = conn->socks_request->port; + circ->dest_address = tor_strdup(conn->socks_request->address); + circ->client_proto_type = TO_CONN(conn)->type; + circ->client_proto_socksver = conn->socks_request->socks_version; + tor_addr_copy(&circ->client_addr, &TO_CONN(conn)->addr); + circ->session_group = conn->session_group; + /* XXXX023 auth too, once #1666 is in. */ + + circ->isolation_values_set = 1; + return 0; + } else { + uint8_t mixed = 0; + if (conn->socks_request->port != circ->dest_port) + mixed |= ISO_DESTPORT; + /* XXXX023 Not quite right: we care about addresses that resolve to the + same place */ + if (strcasecmp(conn->socks_request->address, circ->dest_address)) + mixed |= ISO_DESTADDR; + /* XXXX023 auth too, once #1666 is in. */ + if ((TO_CONN(conn)->type != circ->client_proto_type || + conn->socks_request->socks_version != circ->client_proto_socksver)) + mixed |= ISO_CLIENTPROTO; + if (!tor_addr_eq(&TO_CONN(conn)->addr, &circ->client_addr)) + mixed |= ISO_CLIENTADDR; + if (conn->session_group != circ->session_group) + mixed |= ISO_SESSIONGRP; + + if (dry_run) + return mixed; + + if ((mixed & conn->isolation_flags) != 0) { + log_warn(LD_BUG, "Updating a circuit with seemingly incomaptible " + "isolation flags."); + } + circ->isolation_flags_mixed |= mixed; + return 0; + } +} + diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 1cb1281dbd..59865f414b 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -103,5 +103,13 @@ hostname_type_t parse_extended_hostname(char *address, int allowdotexit); int get_pf_socket(void); #endif +int connection_edge_streams_are_compatible(const edge_connection_t *a, + const edge_connection_t *b); +int connection_edge_compatible_with_circuit(const edge_connection_t *conn, + const origin_circuit_t *circ); +int connection_edge_update_circuit_isolation(const edge_connection_t *conn, + origin_circuit_t *circ, + int dry_run); + #endif diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index 350b138726..b1316ecc6a 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -29,8 +29,9 @@ * DNSPort. We need to eventually answer the request req. */ static void -evdns_server_callback(struct evdns_server_request *req, void *_data) +evdns_server_callback(struct evdns_server_request *req, void *data_) { + const listener_connection_t *listener = data_; edge_connection_t *conn; int i = 0; struct evdns_server_question *q = NULL; @@ -43,7 +44,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data) char *q_name; tor_assert(req); - tor_assert(_data == NULL); + log_info(LD_APP, "Got a new DNS request!"); req->flags |= 0x80; /* set RA */ @@ -131,6 +132,8 @@ evdns_server_callback(struct evdns_server_request *req, void *_data) sizeof(conn->socks_request->address)); conn->dns_server_request = req; + conn->isolation_flags = listener->isolation_flags; + conn->session_group = listener->session_group; if (connection_add(TO_CONN(conn)) < 0) { log_warn(LD_APP, "Couldn't register dummy connection for DNS request"); @@ -312,7 +315,8 @@ dnsserv_configure_listener(connection_t *conn) listener_conn = TO_LISTENER_CONN(conn); listener_conn->dns_server_port = - tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, NULL); + tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, + listener_conn); } /** Free the evdns server port for conn, which must be an diff --git a/src/or/or.h b/src/or/or.h index e284a14ce3..c5e5793720 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1055,6 +1055,19 @@ typedef struct listener_connection_t { * to the evdns_server_port it uses to listen to and answer connections. */ struct evdns_server_port *dns_server_port; + /** @name Isolation parameters + * + * For an AP listener, these fields describe how to isolate streams that + * arrive on the listener. + * + * @{ + */ + /** The session group for this listener. */ + int session_group; + /** One or more ISO_ flags to describe how to isolate streams. */ + uint8_t isolation_flags; + /**@}*/ + } listener_connection_t; /** Stores flags and information related to the portion of a v2 Tor OR @@ -1194,6 +1207,16 @@ typedef struct edge_connection_t { /** What rendezvous service are we querying for? (AP only) */ rend_data_t *rend_data; + /* === Isolation related, AP only. === */ + /** AP only: based on which factors do we isolate this stream? */ + uint8_t isolation_flags; + /** AP only: what session group is this stream in? */ + int session_group; + /* Other fields to isolate on already exist. The ClientAddr is addr. The + ClientProtocol is a combination of type and socks_request-> + socks_version. SocksAuth will be added to socks_request by ticket + #1666. DestAddr and DestPort are in socks_request->address. */ + /** Number of times we've reassigned this application connection to * a new circuit. We keep track because the timeout is longer if we've * already retried several times. */ @@ -2436,6 +2459,32 @@ typedef struct origin_circuit_t { /* XXXX NM This can get re-used after 2**32 circuits. */ uint32_t global_identifier; + /** True if we have attached at least one stream to this circuit, thereby + * setting the isolation paramaters for this circuit. */ + unsigned int isolation_values_set : 1; + /** A bitfield of ISO_* flags for every isolation field such that this + * circuit has had streams with more than one value for that field + * attached to it. */ + uint8_t isolation_flags_mixed; + + /** @name Isolation parameters + * + * If any streams have been attached to this circuit (isolation_values_set + * == 1), and all streams attached to the circuit have had the same value + * for some field ((isolation_flags_mixed & ISO_FOO) == 0), then these + * elements hold the value for that field. + * + * @{ + */ + uint8_t client_proto_type; + uint8_t client_proto_socksver; + uint16_t dest_port; + tor_addr_t client_addr; + char *dest_address; + int session_group; + /* XXXX023 do auth once #1666 is merged */ + /**@}*/ + } origin_circuit_t; /** An or_circuit_t holds information needed to implement a circuit at an @@ -2579,16 +2628,16 @@ typedef enum invalid_router_usage_t { /** Configuration for a single port that we're listening on. */ typedef struct port_cfg_t { - tor_addr_t addr; /**< The configured address to listen on. */ + tor_addr_t addr; /**< The actual IP to listen on, if !is_unix_addr. */ int port; /**< The configured port, or CFG_AUTO_PORT to tell Tor to pick its * own port. */ uint8_t type; /**< One of CONN_TYPE_*_LISTENER */ unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */ /* Client port types (socks, dns, trans, natd) only: */ - uint8_t isolate; /**< Zero or more isolation flags */ - int sessiongroup; /**< A session group, or -1 if this port is not in a - * session group. */ + uint8_t isolation_flags; /**< Zero or more isolation flags */ + int session_group; /**< A session group, or -1 if this port is not in a + * session group. */ /* Unix sockets only: */ /** Path for an AF_UNIX address */