Implement RESOLVE/RESOLVED cells and socks resolve code

svn:r1978
This commit is contained in:
Nick Mathewson 2004-06-17 18:13:09 +00:00
parent dfaa5ce70f
commit 3708886939
7 changed files with 328 additions and 28 deletions

View File

@ -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;
}

View File

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

View File

@ -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 */
}

View File

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

View File

@ -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;
if (pendconn->purpose == EXIT_PURPOSE_CONNECT) {
/* 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;
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);
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);

View File

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

View File

@ -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: