diff options
Diffstat (limited to 'src/or/connection_edge.c')
-rw-r--r-- | src/or/connection_edge.c | 175 |
1 files changed, 164 insertions, 11 deletions
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 5de3d16a6..01ed1f8cb 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -3,6 +3,7 @@ /* $Id$ */ #include "or.h" +#include "tree.h" extern or_options_t options; /* command-line and config-file options */ @@ -15,6 +16,10 @@ static int connection_ap_handshake_socks_reply(connection_t *conn, char *reply, static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); static void connection_edge_consider_sending_sendme(connection_t *conn); +static uint32_t client_dns_lookup_entry(const char *address); +static void client_dns_set_entry(const char *address, uint32_t val); +static void client_dns_clean(void); + int connection_edge_process_inbuf(connection_t *conn) { assert(conn); @@ -96,7 +101,6 @@ void connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_la payload[0] = reason; if(reason == END_STREAM_REASON_EXITPOLICY) { *(uint32_t *)(payload+1) = htonl(conn->addr); - *(uint16_t *)(payload+5) = htons(conn->port); payload_len += 6; } @@ -163,6 +167,7 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection int edge_type, crypt_path_t *layer_hint) { int relay_command; static int num_seen=0; + uint32_t addr; assert(cell && circ); @@ -241,19 +246,21 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection *(int*)conn->stream_id); return 0; } - log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.", - connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length), - *(int*)conn->stream_id); - if(cell->length && *(cell->payload+RELAY_HEADER_SIZE) == - END_STREAM_REASON_EXITPOLICY) { + if(cell->length-RELAY_HEADER_SIZE >= 5 && + *(cell->payload+RELAY_HEADER_SIZE) == END_STREAM_REASON_EXITPOLICY) { /* No need to close the connection. We'll hold it open while * we try a new exit node. * cell->payload+RELAY_HEADER_SIZE+1 holds the addr and then * port of the destination. Which is good, because we've * forgotten it. */ - /* XXX */ + addr = ntohl(*cell->payload+RELAY_HEADER_SIZE+1); + client_dns_set_entry(conn->address, addr); + /* XXX Move state back into CIRCUIT_WAIT. */ } + log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, cell->length), + *(int*)conn->stream_id); #ifdef HALF_OPEN conn->done_sending = 1; @@ -313,6 +320,10 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection break; } log_fn(LOG_INFO,"Connected! Notifying application."); + if (cell->length-RELAY_HEADER_SIZE == 4) { + addr = htonl(*(uint32_t*)(cell->payload + RELAY_HEADER_SIZE)); + client_dns_set_entry(conn->socks_request->addr, addr); + } if(connection_ap_handshake_socks_reply(conn, NULL, 0, 1) < 0) { log_fn(LOG_INFO,"Writing to socks-speaking application failed. Closing."); connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); @@ -386,7 +397,7 @@ int connection_edge_finished_flushing(connection_t *conn) { connection_stop_writing(conn); return 0; default: - log_fn(LOG_WARN,"BUG: called in unexpected state."); + log_fn(LOG_WARN,"BUG: called in unexpected state: %d", conn->state); return -1; } return 0; @@ -583,6 +594,8 @@ static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t { char payload[CELL_PAYLOAD_SIZE]; int payload_len; + struct in_addr in; + const char *string_addr; assert(ap_conn->type == CONN_TYPE_AP); assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); @@ -592,10 +605,15 @@ static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t crypto_pseudo_rand(STREAM_ID_SIZE, ap_conn->stream_id); /* FIXME check for collisions */ + in.s_addr = client_dns_lookup_entry(ap_conn->socks_request->addr); + string_addr = in.s_addr ? inet_ntoa(in) : NULL; + memcpy(payload, ap_conn->stream_id, STREAM_ID_SIZE); payload_len = STREAM_ID_SIZE + 1 + snprintf(payload+STREAM_ID_SIZE,CELL_PAYLOAD_SIZE-RELAY_HEADER_SIZE-STREAM_ID_SIZE, - "%s:%d", ap_conn->socks_request->addr, ap_conn->socks_request->port); + "%s:%d", + string_addr ? string_addr : ap_conn->socks_request->addr, + ap_conn->socks_request->port); log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",*(int *)ap_conn->stream_id); @@ -606,6 +624,12 @@ static void connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t ap_conn->package_window = STREAMWINDOW_START; ap_conn->deliver_window = STREAMWINDOW_START; ap_conn->state = AP_CONN_STATE_OPEN; + /* XXX Right now, we rely on the socks client not to send us any data + * XXX until we've sent back a socks reply. (If it does, we could wind + * XXX up packaging that data and sending it to the exit, then later having + * XXX the exit refuse us.) + * XXX Perhaps we should grow an AP_CONN_STATE_CONNECTING state. + */ log_fn(LOG_INFO,"Address/port sent, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id); return; } @@ -701,6 +725,7 @@ static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { } void connection_exit_connect(connection_t *conn) { + unsigned char connected_payload[4]; if(router_compare_to_exit_policy(conn) < 0) { log_fn(LOG_INFO,"%s:%d failed exit policy. Closing.", conn->address, conn->port); @@ -733,8 +758,137 @@ void connection_exit_connect(connection_t *conn) { connection_watch_events(conn, POLLIN); /* also, deliver a 'connected' cell back through the circuit. */ + *((uint32_t*) connected_payload) = htonl(conn->addr); connection_edge_send_command(conn, circuit_get_by_conn(conn), RELAY_COMMAND_CONNECTED, - NULL, 0, conn->cpath_layer); + connected_payload, 4, conn->cpath_layer); +} + +/* ***** Client DNS code ***** */ +#define MAX_DNS_ENTRY_AGE 30*60 + +/* XXX Perhaps this should get merged with the dns.c code somehow. */ +struct client_dns_entry { + SPLAY_ENTRY(client_dns_entry) node; + char *address; + uint32_t addr; + time_t expires; +}; +static int client_dns_size = 0; +static SPLAY_HEAD(client_dns_tree, client_dns_entry) client_dns_root; + +static int compare_client_dns_entries(struct client_dns_entry *a, + struct client_dns_entry *b) +{ + return strcasecmp(a->address, b->address); +} + +static void client_dns_entry_free(struct client_dns_entry *ent) +{ + tor_free(ent->address); + tor_free(ent); +} + +SPLAY_PROTOTYPE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries); +SPLAY_GENERATE(client_dns_tree, client_dns_entry, node, compare_client_dns_entries); + +void client_dns_init(void) { + SPLAY_INIT(&client_dns_root); + client_dns_size = 0; +} + +static uint32_t client_dns_lookup_entry(const char *address) +{ + struct client_dns_entry *ent; + struct client_dns_entry search; + struct in_addr in; + time_t now; + + assert(address); + + if (inet_aton(address, &in)) { + log_fn(LOG_DEBUG, "Using static address %s", address); + return in.s_addr; + } + search.address = (char*)address; + ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search); + if (!ent) { + log_fn(LOG_DEBUG, "No entry found for address %s", address); + return 0; + } else { + now = time(NULL); + if (ent->expires < now) { + log_fn(LOG_DEBUG, "Expired entry found for address %s", address); + SPLAY_REMOVE(client_dns_tree, &client_dns_root, ent); + client_dns_entry_free(ent); + --client_dns_size; + return 0; + } + in.s_addr = ent->addr; + log_fn(LOG_DEBUG, "Found cached entry for address %s: %s", address, + inet_ntoa(in)); + return ent->addr; + } +} +static void client_dns_set_entry(const char *address, uint32_t val) +{ + struct client_dns_entry *ent; + struct client_dns_entry search; + struct in_addr in; + time_t now; + + assert(address); + assert(val); + + if (inet_aton(address, &in)) { + if (in.s_addr == val) + return; + in.s_addr = val; + log_fn(LOG_WARN, + "Trying to store incompatible cached value %s for static address %s", + inet_ntoa(in), address); + return; + } + search.address = (char*) address; + now = time(NULL); + ent = SPLAY_FIND(client_dns_tree, &client_dns_root, &search); + if (ent) { + log_fn(LOG_DEBUG, "Updating entry for address %s", address); + ent->addr = val; + ent->expires = now+MAX_DNS_ENTRY_AGE; + } else { + in.s_addr = val; + log_fn(LOG_DEBUG, "Caching result for address %s: %s", address, + inet_ntoa(in)); + ent = tor_malloc(sizeof(struct client_dns_entry)); + ent->address = tor_strdup(address); + ent->addr = val; + ent->expires = now+MAX_DNS_ENTRY_AGE; + SPLAY_INSERT(client_dns_tree, &client_dns_root, ent); + ++client_dns_size; + } +} +static void client_dns_clean() +{ + struct client_dns_entry **expired_entries; + int n_expired_entries = 0; + struct client_dns_entry *ent; + time_t now; + int i; + + expired_entries = tor_malloc(client_dns_size * + sizeof(struct client_dns_entry *)); + + now = time(NULL); + SPLAY_FOREACH(ent, client_dns_tree, &client_dns_root) { + if (ent->expires < now) { + expired_entries[n_expired_entries++] = ent; + } + } + for (i = 0; i < n_expired_entries; ++i) { + SPLAY_REMOVE(client_dns_tree, &client_dns_root, expired_entries[i]); + client_dns_entry_free(expired_entries[i]); + } + tor_free(expired_entries); } /* @@ -744,4 +898,3 @@ void connection_exit_connect(connection_t *conn) { c-basic-offset:2 End: */ - |