diff options
author | Nick Mathewson <nickm@torproject.org> | 2004-06-17 18:13:09 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2004-06-17 18:13:09 +0000 |
commit | 37088869396bd862ff24a46b1ff554f7d7f8c772 (patch) | |
tree | 8cc653886db276a2389cb8275a1c85f3c927d2dd | |
parent | dfaa5ce70ff2c96275512c4921998d0865828f81 (diff) | |
download | tor-37088869396bd862ff24a46b1ff554f7d7f8c772.tar.gz tor-37088869396bd862ff24a46b1ff554f7d7f8c772.zip |
Implement RESOLVE/RESOLVED cells and socks resolve code
svn:r1978
-rw-r--r-- | src/or/buffers.c | 21 | ||||
-rw-r--r-- | src/or/circuituse.c | 21 | ||||
-rw-r--r-- | src/or/connection.c | 5 | ||||
-rw-r--r-- | src/or/connection_edge.c | 163 | ||||
-rw-r--r-- | src/or/dns.c | 83 | ||||
-rw-r--r-- | src/or/or.h | 26 | ||||
-rw-r--r-- | src/or/relay.c | 41 |
7 files changed, 330 insertions, 30 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index 13824a20a7..855443dd3f 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -409,6 +409,9 @@ int fetch_from_buf_http(buf_t *buf, return 1; } +#define SOCKS_COMMAND_CONNECT 0x01 +#define SOCKS_COMMAND_RESOLVE 0xF0 + /** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one * of the forms * - socks4: "socksheader username\\0" @@ -467,8 +470,12 @@ int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) { log_fn(LOG_DEBUG,"socks5: checking request"); if(buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */ return 0; /* not yet */ - if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */ - log_fn(LOG_WARN,"socks5: command %d not '1'. Rejecting.",*(buf->mem+1)); + req->command = (unsigned char) *(buf->mem+1); + if(req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE) { + /* not a connect or resolve? we don't support it. */ + log_fn(LOG_WARN,"socks5: command %d not recognized. Rejecting.", + req->command); return -1; } switch(*(buf->mem+3)) { /* address type */ @@ -516,14 +523,18 @@ int fetch_from_buf_socks(buf_t *buf, socks_request_t *req) { if(buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */ return 0; /* not yet */ - if(*(buf->mem+1) != 1) { /* not a connect? we don't support it. */ - log_fn(LOG_WARN,"socks4: command %d not '1'. Rejecting.",*(buf->mem+1)); + req->command = (unsigned char) *(buf->mem+1); + if(req->command != SOCKS_COMMAND_CONNECT && + req->command != SOCKS_COMMAND_RESOLVE) { + /* not a connect or resolve? we don't support it. */ + log_fn(LOG_WARN,"socks4: command %d not recognized. Rejecting.", + req->command); return -1; } req->port = ntohs(*(uint16_t*)(buf->mem+2)); destip = ntohl(*(uint32_t*)(buf->mem+4)); - if(!req->port || !destip) { + if((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) { log_fn(LOG_WARN,"socks4: Port or DestIP is zero. Rejecting."); return -1; } diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 35bfb968d3..e61cfa2351 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -73,7 +73,14 @@ static int circuit_is_acceptable(circuit_t *circ, return 0; /* this circuit is screwed and doesn't know it yet */ } - if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if (conn->socks_request && + conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { + /* 0.0.7 servers and earlier don't support DNS resolution. There are no + * ORs running code before 0.0.7, so we only worry about 0.0.7. Once all + * servers are running 0.0.8, remove this check. */ + if (!strncmp(exitrouter->platform, "Tor 0.0.7", 9)) + return 0; + } else if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) { /* can't exit from this router */ return 0; @@ -618,10 +625,12 @@ circuit_get_open_circ_or_launch(connection_t *conn, circuit_t **circp) { circuit_t *circ; uint32_t addr; + int is_resolve; tor_assert(conn); tor_assert(circp); tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + is_resolve = conn->socks_request->command == SOCKS_COMMAND_RESOLVE; circ = circuit_get_best(conn, 1, desired_circuit_purpose); @@ -630,7 +639,8 @@ circuit_get_open_circ_or_launch(connection_t *conn, return 1; /* we're happy */ } - if(!connection_edge_is_rendezvous_stream(conn)) { /* general purpose circ */ + /* Do we need to check exit policy? */ + if(!is_resolve && !connection_edge_is_rendezvous_stream(conn)) { addr = client_dns_lookup_entry(conn->socks_request->address); if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) { log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.", @@ -742,10 +752,13 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) { circ->timestamp_dirty = time(NULL); link_apconn_to_circ(conn, circ); - connection_ap_handshake_send_begin(conn, circ); + tor_assert(conn->socks_request); + if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) + connection_ap_handshake_send_begin(conn, circ); + else + connection_ap_handshake_send_resolve(conn, circ); return 1; - } else { /* we're a rendezvous conn */ circuit_t *rendcirc=NULL, *introcirc=NULL; diff --git a/src/or/connection.c b/src/or/connection.c index aaafbe3068..bd8a534cf2 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1276,7 +1276,10 @@ void assert_connection_ok(connection_t *conn, time_t now) } else { tor_assert(!conn->socks_request); } - if(conn->type != CONN_TYPE_DIR) { + if (conn->type == CONN_TYPE_EXIT) { + tor_assert(conn->purpose == EXIT_PURPOSE_CONNECT || + conn->purpose == EXIT_PURPOSE_RESOLVE); + } else if(conn->type != CONN_TYPE_DIR) { tor_assert(!conn->purpose); /* only used for dir types currently */ } diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 4f96b14680..20a9341a89 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -371,6 +371,23 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { return sockshere; } /* else socks handshake is done, continue processing */ + if (socks->command == SOCKS_COMMAND_RESOLVE) { + /* Reply to resolves immediately if we can. */ + if (strlen(socks->address) > RELAY_PAYLOAD_SIZE) { + connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,0,NULL); + conn->socks_request->has_finished = 1; + connection_mark_for_close(conn); + } + uint32_t answer = htonl(client_dns_lookup_entry(socks->address)); + if (answer) { + connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_IPV4,4, + (char*)&answer); + conn->socks_request->has_finished = 1; + connection_mark_for_close(conn); + return 0; + } + } + /* this call _modifies_ socks->address iff it's a hidden-service request */ if (rend_parse_rendezvous_address(socks->address) < 0) { /* normal request */ @@ -487,6 +504,46 @@ int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ) return 0; } +/** Write a relay resolve cell, using destaddr and destport from ap_conn's + * socks_request field, and send it down circ. + * + * If ap_conn is broken, mark it for close and return -1. Else return 0. + */ +int connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ) +{ + int payload_len; + const char *string_addr; + + tor_assert(ap_conn->type == CONN_TYPE_AP); + tor_assert(ap_conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(ap_conn->socks_request); + tor_assert(ap_conn->socks_request->command == SOCKS_COMMAND_RESOLVE); + tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_GENERAL); + + ap_conn->stream_id = get_unique_stream_id_by_circ(circ); + if (ap_conn->stream_id==0) { + /* Don't send end: there is no 'other side' yet */ + ap_conn->has_sent_end = 1; + connection_mark_for_close(ap_conn); + circuit_mark_for_close(circ); + return -1; + } + + string_addr = ap_conn->socks_request->address; + payload_len = strlen(string_addr); + tor_assert(strlen(string_addr) <= RELAY_PAYLOAD_SIZE); + + log_fn(LOG_DEBUG,"Sending relay cell to begin stream %d.",ap_conn->stream_id); + + if(connection_edge_send_command(ap_conn, circ, RELAY_COMMAND_RESOLVE, + string_addr, payload_len, ap_conn->cpath_layer) < 0) + return -1; /* circuit is closed, don't continue */ + + ap_conn->state = AP_CONN_STATE_RESOLVE_WAIT; + log_fn(LOG_INFO,"Address sent for resolve, ap socket %d, n_circ_id %d",ap_conn->s,circ->n_circ_id); + return 0; +} + /** Make an AP connection_t, do a socketpair and attach one side * to the conn, connection_add it, initialize it to circuit_wait, * and call connection_ap_handshake_attach_circuit(conn) on it. @@ -544,6 +601,59 @@ int connection_ap_make_bridge(char *address, uint16_t port) { return fd[1]; } +void connection_ap_handshake_socks_resolved(connection_t *conn, + int answer_type, + int answer_len, + const char *answer) +{ + char buf[256]; + int replylen; + + if (answer_type == RESOLVED_TYPE_IPV4) { + uint32_t a = get_uint32(answer); + client_dns_set_entry(conn->socks_request->address, ntohl(a)); + } + + if (conn->socks_request->socks_version == 4) { + buf[0] = 0x00; /* version */ + if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { + buf[1] = 90; /* "Granted" */ + set_uint16(buf+2, 0); + memcpy(buf+4, answer, 4); /* address */ + replylen = SOCKS4_NETWORK_LEN; + } else { + buf[1] = 91; /* "error" */ + memset(buf+2, 0, 6); + replylen = SOCKS4_NETWORK_LEN; + } + } else { + /* SOCKS5 */ + buf[0] = 0x05; /* version */ + if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) { + buf[1] = 0; /* succeeded */ + buf[2] = 0; /* reserved */ + buf[3] = 0x01; /* IPv4 address type */ + memcpy(buf+4, answer, 4); /* address */ + set_uint16(buf+8, 0); /* port == 0. */ + replylen = 10; + } else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) { + buf[1] = 0; /* succeeded */ + buf[2] = 0; /* reserved */ + buf[3] = 0x04; /* IPv6 address type */ + memcpy(buf+4, answer, 16); /* address */ + set_uint16(buf+20, 0); /* port == 0. */ + replylen = 22; + } else { + buf[1] = 0x04; /* host unreachable */ + memset(buf+2, 0, 8); + replylen = 10; + } + } + connection_ap_handshake_socks_reply(conn, buf, replylen, + answer_type == RESOLVED_TYPE_IPV4 || + answer_type == RESOLVED_TYPE_IPV6); +} + /** Send a socks reply to stream <b>conn</b>, using the appropriate * socks version, etc. * @@ -631,6 +741,7 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { log_fn(LOG_DEBUG,"Creating new exit connection."); n_stream = connection_new(CONN_TYPE_EXIT); + n_stream->purpose = EXIT_PURPOSE_CONNECT; n_stream->stream_id = rh.stream_id; n_stream->port = atoi(colon+1); @@ -694,6 +805,52 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { return 0; } +/** + * Called when we receive a RELAY_RESOLVE cell 'cell' along the circuit 'circ'; + * begin resolving the hostname, and (eventually) reply with a RESOLVED cell. + */ +int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ) { + connection_t *dummy_conn; + relay_header_t rh; + + assert_circuit_ok(circ); + relay_header_unpack(&rh, cell->payload); + + + /* This 'dummy_conn' only exists to remember the stream ID + * associated with the resolve request; and to make the + * implementation of dns.c more uniform. (We really only need to + * remember the circuit, the stream ID, and the hostname to be + * resolved; but if we didn't store them in a connection like this, + * the housekeeping in dns.c would get way more complicated.) + */ + dummy_conn = connection_new(CONN_TYPE_EXIT); + dummy_conn->stream_id = rh.stream_id; + dummy_conn->address = tor_strndup(cell->payload+RELAY_HEADER_SIZE, + rh.length); + dummy_conn->port = 0; + dummy_conn->state = EXIT_CONN_STATE_RESOLVEFAILED; + dummy_conn->purpose = EXIT_PURPOSE_RESOLVE; + + /* send it off to the gethostbyname farm */ + switch(dns_resolve(dummy_conn)) { + case 1: /* resolve worked; resolved cell was sent. */ + connection_free(dummy_conn); + return 0; + case -1: /* resolve failed; resolved cell was sent. */ + log_fn(LOG_INFO,"Resolve failed (%s).",dummy_conn->address); + connection_free(dummy_conn); + break; + case 0: /* resolve added to pending list */ + /* add it into the linked list of resolving_streams on this circuit */ + dummy_conn->next_stream = circ->resolving_streams; + circ->resolving_streams = dummy_conn; + assert_circuit_ok(circ); + ; + } + return 0; +} + /** Connect to conn's specified addr and port. If it worked, conn * has now been added to the connection_array. * @@ -776,6 +933,12 @@ int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit) log_fn(LOG_DEBUG,"considering nickname %s, for address %s / port %d:", exit->nickname, conn->socks_request->address, conn->socks_request->port); + if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { + /* 0.0.7 servers and earlier don't support DNS resolution. There are no + * ORs running code before 0.0.7, so we only worry about 0.0.7. Once all + * servers are running 0.0.8, remove this check. */ + return strncmp(exit->platform, "Tor 0.0.7", 9) ? 1 : 0; + } addr = client_dns_lookup_entry(conn->socks_request->address); return router_compare_addr_to_exit_policy(addr, conn->socks_request->port, exit->exit_policy); diff --git a/src/or/dns.c b/src/or/dns.c index fa25de5e2c..66e9334b14 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -71,6 +71,7 @@ static void dns_found_answer(char *address, uint32_t addr, char outcome); static int dnsworker_main(void *data); static int spawn_dnsworker(void); static void spawn_enough_dnsworkers(void); +static void send_resolved_cell(connection_t *conn, uint8_t answer_type); /** Splay tree of cached_resolve objects. */ static SPLAY_HEAD(cache_tree, cached_resolve) cache_root; @@ -141,6 +142,34 @@ static void purge_expired_resolves(uint32_t now) { } } +static void send_resolved_cell(connection_t *conn, uint8_t answer_type) +{ + char buf[RELAY_PAYLOAD_SIZE]; + int buflen; + + buf[0] = answer_type; + + switch (answer_type) + { + case RESOLVED_TYPE_IPV4: + buf[1] = 4; + set_uint32(buf+2, htonl(conn->addr)); + buflen = 6; + break; + case RESOLVED_TYPE_ERROR_TRANSIENT: + case RESOLVED_TYPE_ERROR: + buf[1] = 24; /* length of "error resolving hostname" */ + strcpy(buf+2, "error resolving hostname"); + buflen = 26; + break; + default: + tor_assert(0); + } + connection_edge_send_command(conn, circuit_get_by_conn(conn), + RELAY_COMMAND_RESOLVED, buf, buflen, + conn->cpath_layer); +} + /** See if we have a cache entry for <b>exitconn</b>-\>address. if so, * if resolve valid, put it into <b>exitconn</b>-\>addr and return 1. * If resolve failed, return -1. @@ -179,7 +208,8 @@ int dns_resolve(connection_t *exitconn) { switch(resolve->state) { case CACHE_STATE_PENDING: /* add us to the pending list */ - pending_connection = tor_malloc(sizeof(struct pending_connection_t)); + pending_connection = tor_malloc_zero( + sizeof(struct pending_connection_t)); pending_connection->conn = exitconn; pending_connection->next = resolve->pending_connections; resolve->pending_connections = pending_connection; @@ -191,8 +221,12 @@ int dns_resolve(connection_t *exitconn) { exitconn->addr = resolve->addr; log_fn(LOG_DEBUG,"Connection (fd %d) found cached answer for '%s'", exitconn->s, exitconn->address); + if (exitconn->purpose == EXIT_PURPOSE_RESOLVE) + send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4); return 1; case CACHE_STATE_FAILED: + if (exitconn->purpose == EXIT_PURPOSE_RESOLVE) + send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR); return -1; } tor_assert(0); @@ -205,7 +239,7 @@ int dns_resolve(connection_t *exitconn) { resolve->address[MAX_ADDRESSLEN-1] = 0; /* add us to the pending list */ - pending_connection = tor_malloc(sizeof(struct pending_connection_t)); + pending_connection = tor_malloc_zero(sizeof(struct pending_connection_t)); pending_connection->conn = exitconn; pending_connection->next = NULL; resolve->pending_connections = pending_connection; @@ -240,6 +274,7 @@ static int assign_to_dnsworker(connection_t *exitconn) { if(!dnsconn) { log_fn(LOG_WARN,"no idle dns workers. Failing."); dns_cancel_pending_resolve(exitconn->address); + send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT); return -1; } @@ -453,28 +488,42 @@ static void dns_found_answer(char *address, uint32_t addr, char outcome) { pend = resolve->pending_connections; assert_connection_ok(pend->conn,time(NULL)); pend->conn->addr = resolve->addr; + pendconn = pend->conn; /* don't pass complex things to the + connection_mark_for_close macro */ if(resolve->state == CACHE_STATE_FAILED) { - pendconn = pend->conn; /* don't pass complex things to the - connection_mark_for_close macro */ /* prevent double-remove. */ pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED; circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn); - connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer); + if (pendconn->purpose == EXIT_PURPOSE_CONNECT) + connection_edge_end(pendconn, END_STREAM_REASON_MISC, pendconn->cpath_layer); + else + send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR); connection_free(pendconn); } else { - /* prevent double-remove. */ - pend->conn->state = EXIT_CONN_STATE_CONNECTING; - - circ = circuit_get_by_conn(pend->conn); - assert(circ); - /* unlink pend->conn from resolving_streams, */ - circuit_detach_stream(circ, pend->conn); - /* and link it to n_streams */ - pend->conn->next_stream = circ->n_streams; - circ->n_streams = pend->conn; - - connection_exit_connect(pend->conn); + if (pendconn->purpose == EXIT_PURPOSE_CONNECT) { + /* prevent double-remove. */ + pend->conn->state = EXIT_CONN_STATE_CONNECTING; + + circ = circuit_get_by_conn(pend->conn); + tor_assert(circ); + /* unlink pend->conn from resolving_streams, */ + circuit_detach_stream(circ, pend->conn); + /* and link it to n_streams */ + pend->conn->next_stream = circ->n_streams; + circ->n_streams = pend->conn; + + connection_exit_connect(pend->conn); + } else { + /* prevent double-remove. This isn't really an accurate state, + * but it does the right thing. */ + pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED; + send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4); + circ = circuit_get_by_conn(pendconn); + tor_assert(circ); + circuit_detach_stream(circ, pendconn); + connection_free(pendconn); + } } resolve->pending_connections = pend->next; tor_free(pend); diff --git a/src/or/or.h b/src/or/or.h index d2a460c8c6..a91fae4199 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -221,9 +221,11 @@ #define AP_CONN_STATE_CIRCUIT_WAIT 7 /** State for a SOCKS connection: sent BEGIN, waiting for CONNECTED. */ #define AP_CONN_STATE_CONNECT_WAIT 8 +/** State for a SOCKS connection: send RESOLVE, waiting for RESOLVED. */ +#define AP_CONN_STATE_RESOLVE_WAIT 9 /** State for a SOCKS connection: ready to send and receive. */ -#define AP_CONN_STATE_OPEN 9 -#define _AP_CONN_STATE_MAX 9 +#define AP_CONN_STATE_OPEN 10 +#define _AP_CONN_STATE_MAX 10 #define _DIR_CONN_STATE_MIN 1 /** State for connection to directory server: waiting for connect(). */ @@ -259,6 +261,11 @@ #define DIR_PURPOSE_SERVER 7 #define _DIR_PURPOSE_MAX 7 +#define _EXIT_PURPOSE_MIN 1 +#define EXIT_PURPOSE_CONNECT 1 +#define EXIT_PURPOSE_RESOLVE 2 +#define _EXIT_PURPOSE_MAX 2 + /** Circuit state: I'm the OP, still haven't done all my handshakes. */ #define CIRCUIT_STATE_BUILDING 0 /** Circuit state: Waiting to process the onionskin. */ @@ -371,6 +378,11 @@ #define END_STREAM_REASON_TIMEOUT 7 #define _MAX_END_STREAM_REASON 7 +#define RESOLVED_TYPE_IPV4 4 +#define RESOLVED_TYPE_IPV6 6 +#define RESOLVED_TYPE_ERROR_TRANSIENT 0xF0 +#define RESOLVED_TYPE_ERROR 0xF1 + /** Length of 'y' portion of 'y.onion' URL. */ #define REND_SERVICE_ID_LEN 16 @@ -841,9 +853,12 @@ typedef struct { /* XXX are these good enough defaults? */ #define MAX_SOCKS_REPLY_LEN 1024 #define MAX_SOCKS_ADDR_LEN 256 +#define SOCKS_COMMAND_CONNECT 0x01 +#define SOCKS_COMMAND_RESOLVE 0xF0 /** State of a SOCKS request from a user to an OP */ struct socks_request_t { char socks_version; /**< Which version of SOCKS did the client use? */ + int command; /**< What has the user requested? One of CONNECT or RESOLVE. */ int replylen; /**< Length of <b>reply</b>. */ char reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if * we want to specify our own socks reply, @@ -1048,13 +1063,18 @@ int connection_edge_finished_flushing(connection_t *conn); int connection_edge_finished_connecting(connection_t *conn); int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ); +int connection_ap_handshake_send_resolve(connection_t *ap_conn, circuit_t *circ); int connection_ap_make_bridge(char *address, uint16_t port); - void connection_ap_handshake_socks_reply(connection_t *conn, char *reply, int replylen, char success); +void connection_ap_handshake_socks_resolved(connection_t *conn, + int answer_type, + int answer_len, + const char *answer); int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); +int connection_exit_begin_resolve(cell_t *cell, circuit_t *circ); void connection_exit_connect(connection_t *conn); int connection_edge_is_rendezvous_stream(connection_t *conn); int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit); diff --git a/src/or/relay.c b/src/or/relay.c index 47a3c67d6c..36fd24c770 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -562,6 +562,26 @@ connection_edge_process_relay_cell_not_open( } return 0; } + if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_RESOLVED) { + if (conn->state != AP_CONN_STATE_RESOLVE_WAIT) { + log_fn(LOG_WARN,"Got a 'resolved' cell while not in state resolve_wait. Dropping."); + return 0; + } + tor_assert(conn->socks_request->command == SOCKS_COMMAND_RESOLVE); + if (rh->length < 2 || cell->payload[RELAY_HEADER_SIZE+1]+2>rh->length) { + log_fn(LOG_WARN, "Dropping malformed 'resolved' cell"); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return 0; + } + connection_ap_handshake_socks_resolved(conn, + cell->payload[RELAY_HEADER_SIZE], /*answer_type*/ + cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/ + cell->payload+RELAY_HEADER_SIZE+2); /* answer */ + conn->socks_request->has_finished = 1; + connection_mark_for_close(conn); + return 0; + } log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.", rh->command, conn->state, conn_state_to_string[conn->type][conn->state]); @@ -744,6 +764,27 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_start_reading(conn); connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */ return 0; + case RELAY_COMMAND_RESOLVE: + if (layer_hint) { + log_fn(LOG_WARN,"resolve request unsupported at AP; dropping."); + return 0; + } else if (conn) { + log_fn(LOG_WARN, "resolve request for known stream; dropping."); + return 0; + } else if (circ->purpose != CIRCUIT_PURPOSE_OR) { + log_fn(LOG_WARN, "resolve request on circ with purpose %d; dropping", + circ->purpose); + return 0; + } + connection_exit_begin_resolve(cell, circ); + return 0; + case RELAY_COMMAND_RESOLVED: + if(conn) { + log_fn(LOG_WARN,"'resolved' unsupported while open. Closing circ."); + return -1; + } + log_fn(LOG_INFO,"'resolved' received, no conn attached anymore. Ignoring."); + return 0; case RELAY_COMMAND_ESTABLISH_INTRO: case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: case RELAY_COMMAND_INTRODUCE1: |