mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Implement RESOLVE/RESOLVED cells and socks resolve code
svn:r1978
This commit is contained in:
parent
dfaa5ce70f
commit
3708886939
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
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;
|
||||
|
||||
|
@ -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 */
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
59
src/or/dns.c
59
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,21 +488,25 @@ 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;
|
||||
|
||||
if(resolve->state == CACHE_STATE_FAILED) {
|
||||
pendconn = pend->conn; /* don't pass complex things to the
|
||||
connection_mark_for_close macro */
|
||||
|
||||
if(resolve->state == CACHE_STATE_FAILED) {
|
||||
/* prevent double-remove. */
|
||||
pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
|
||||
circuit_detach_stream(circuit_get_by_conn(pendconn), pendconn);
|
||||
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 {
|
||||
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);
|
||||
tor_assert(circ);
|
||||
/* unlink pend->conn from resolving_streams, */
|
||||
circuit_detach_stream(circ, pend->conn);
|
||||
/* and link it to n_streams */
|
||||
@ -475,6 +514,16 @@ static void dns_found_answer(char *address, uint32_t addr, char outcome) {
|
||||
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);
|
||||
|
26
src/or/or.h
26
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);
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user