Commit d9829255 authored by Roger Dingledine's avatar Roger Dingledine
Browse files

Integrated onion proxy into or/

The 'or' process can now be told (by the global_role variable) what
roles this server should play -- connect to all ORs, listen for ORs,
listen for OPs, listen for APs, or any combination.

* everything in /src/op/ is now obsolete.
* connection_ap.c now handles all interactions with application proxies
* "port" is now or_port, op_port, ap_port. But routers are still always
  referenced (say, in conn_get_by_addr_port()) by addr / or_port. We
  should make routers.c actually read these new ports (currently I've
  kludged it so op_port = or_port+10, ap_port=or_port+20)
* circuits currently know if they're at the beginning of the path because
  circ->cpath is set. They use this instead for crypts (both ways),
  if it's set.
* I still obey the "send a 0 back to the AP when you're ready" protocol,
  but I think we should phase it out. I can simply not read from the AP
  socket until I'm ready.

I need to do a lot of cleanup work here, but the code appears to work, so
now's a good time for a checkin.


svn:r22
parent b34fad4d
......@@ -4,7 +4,7 @@ bin_PROGRAMS = or
or_LDADD = -L../common -lor
or_SOURCES = args.c buffers.c cell.c circuit.c command.c connection.c \
connection_exit.c connection_op.c connection_or.c config.c \
connection_exit.c connection_ap.c connection_op.c connection_or.c config.c \
main.c onion.c routers.c
noinst_HEADERS = or.h
......
......@@ -52,9 +52,6 @@ circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) {
circ->p_conn = p_conn;
circ->state = CIRCUIT_STATE_OPEN_WAIT;
circ->onion = NULL;
circ->onionlen=0;
circ->recvlen=0;
/* ACIs */
circ->p_aci = p_aci;
......@@ -72,9 +69,19 @@ void circuit_free(circuit_t *circ) {
if(circ->onion)
free(circ->onion);
if(circ->cpath)
circuit_free_cpath(circ->cpath, circ->cpathlen);
free(circ);
}
void circuit_free_cpath(crypt_path_t **cpath, size_t cpathlen) {
int i;
for(i=0;i<cpathlen;i++)
free(cpath[i]);
free(cpath);
}
aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) {
......@@ -89,6 +96,7 @@ aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) {
test_aci &= htons(0x00FF);
if(aci_type == ACI_TYPE_HIGHER)
test_aci &= htons(0xFF00);
/* if aci_type == ACI_BOTH, don't filter any of it */
if(test_aci == 0)
return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */
......@@ -110,12 +118,10 @@ int circuit_init(circuit_t *circ, int aci_type) {
unsigned char digest1[20];
unsigned char digest2[20];
if (!circ)
return -1;
assert(circ);
ol = (onion_layer_t *)circ->onion;
if (!ol)
return -1;
assert(ol);
log(LOG_DEBUG,"circuit_init(): starting");
circ->n_addr = ol->addr;
......@@ -204,6 +210,16 @@ int circuit_init(circuit_t *circ, int aci_type) {
return 0;
}
circuit_t *circuit_get_by_naddr_nport(uint32_t naddr, uint16_t nport) {
circuit_t *circ;
for(circ=global_circuitlist;circ;circ = circ->next) {
if(circ->n_addr == naddr && circ->n_port == nport)
return circ;
}
return NULL;
}
circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) {
circuit_t *circ;
......@@ -244,24 +260,24 @@ int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn,
if(conn->type == CONN_TYPE_EXIT) { /* send payload directly */
log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to exit.");
if(connection_exit_process_data_cell(cell, conn) < 0) {
return -1;
}
} else { /* send it as a cell */
log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to connection.");
if(connection_write_cell_to_buf(cell, conn) < 0) {
return -1;
}
return connection_exit_process_data_cell(cell, conn);
}
return 0; /* success */
if(conn->type == CONN_TYPE_AP) { /* send payload directly */
log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to AP.");
return connection_ap_process_data_cell(cell, conn);
}
/* else send it as a cell */
log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to connection.");
return connection_write_cell_to_buf(cell, conn);
}
int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) {
char *out;
int outlen;
int i;
crypt_path_t *thishop;
if(!circ || !in)
return -1;
assert(circ && in);
out = malloc(inlen);
if(!out)
......@@ -269,24 +285,65 @@ int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) {
if(crypt_type == 'e') {
log(LOG_DEBUG,"circuit_crypt(): Encrypting %d bytes.",inlen);
if(!EVP_EncryptUpdate(&circ->p_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",circ->p_aci, ERR_reason_error_string(ERR_get_error()));
return -1;
if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */
/* 'e' means we're preparing to send it out. */
for (i=0; i < circ->cpathlen; i++) /* moving from last to first hop
* Remember : cpath is in reverse order, i.e. last hop first
*/
{
log(LOG_DEBUG,"circuit_crypt() : Encrypting via cpath: Processing hop %u",circ->cpathlen-i);
thishop = circ->cpath[i];
/* encrypt */
if(!EVP_EncryptUpdate(&thishop->f_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"Error performing encryption:%s",ERR_reason_error_string(ERR_get_error()));
free(out);
return -1;
}
/* copy ciphertext back to buf */
memcpy(in,out,inlen);
}
} else { /* we're in the middle. Just one crypt. */
if(!EVP_EncryptUpdate(&circ->p_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",
circ->p_aci, ERR_reason_error_string(ERR_get_error()));
free(out);
return -1;
}
memcpy(in,out,inlen);
}
} else if(crypt_type == 'd') {
log(LOG_DEBUG,"circuit_crypt(): Decrypting %d bytes.",inlen);
if(!EVP_DecryptUpdate(&circ->n_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",circ->n_aci, ERR_reason_error_string(ERR_get_error()));
return -1;
if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */
for (i=circ->cpathlen-1; i >= 0; i--) /* moving from first to last hop
* Remember : cpath is in reverse order, i.e. last hop first
*/
{
log(LOG_DEBUG,"circuit_crypt() : Decrypting via cpath: Processing hop %u",circ->cpathlen-i);
thishop = circ->cpath[i];
/* encrypt */
if(!EVP_DecryptUpdate(&thishop->b_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"Error performing decryption:%s",ERR_reason_error_string(ERR_get_error()));
free(out);
return -1;
}
/* copy ciphertext back to buf */
memcpy(in,out,inlen);
}
} else { /* we're in the middle. Just one crypt. */
if(!EVP_DecryptUpdate(&circ->n_ctx,out,&outlen,in,inlen)) {
log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",
circ->n_aci, ERR_reason_error_string(ERR_get_error()));
free(out);
return -1;
}
memcpy(in,out,inlen);
}
}
if(outlen != inlen) {
log(LOG_DEBUG,"circuit_crypt(): %d bytes crypted to %d bytes. Weird.",inlen,outlen);
return -1;
}
memcpy(in,out,inlen);
free(out);
return 0;
......@@ -294,8 +351,10 @@ int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) {
void circuit_close(circuit_t *circ) {
circuit_remove(circ);
connection_send_destroy(circ->n_aci, circ->n_conn);
connection_send_destroy(circ->p_aci, circ->p_conn);
if(circ->n_conn)
connection_send_destroy(circ->n_aci, circ->n_conn);
if(circ->p_conn)
connection_send_destroy(circ->p_aci, circ->p_conn);
circuit_free(circ);
}
......
......@@ -43,7 +43,7 @@ void command_process_create_cell(cell_t *cell, connection_t *conn) {
memcpy((void *)&circ->onionlen,(void *)cell->payload, 4);
circ->onionlen = ntohl(circ->onionlen);
log(LOG_DEBUG,"command_process_create_cell(): Onion length is %u.",circ->onionlen);
if(circ->onionlen > 50000) { /* too big */
if(circ->onionlen > 50000 || circ->onionlen < 1) { /* too big or too small */
log(LOG_DEBUG,"That's ludicrous. Closing.");
circuit_close(circ);
return;
......@@ -172,6 +172,10 @@ void command_process_data_cell(cell_t *cell, connection_t *conn) {
log(LOG_DEBUG,"command_process_data_cell(): circuit in open_wait. Dropping data cell.");
return;
}
if(circ->state == CIRCUIT_STATE_OR_WAIT) {
log(LOG_DEBUG,"command_process_data_cell(): circuit in or_wait. Dropping data cell.");
return;
}
/* at this point both circ->n_conn and circ->p_conn are guaranteed to be set */
......@@ -184,10 +188,18 @@ void command_process_data_cell(cell_t *cell, connection_t *conn) {
}
} else { /* it's an ingoing cell */
cell->aci = circ->p_aci; /* switch it */
if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'e') < 0) {
log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward) failed. Closing.");
circuit_close(circ);
return;
if(circ->p_conn->type == CONN_TYPE_AP) { /* we want to decrypt, not encrypt */
if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'd') < 0) {
log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward to AP) failed. Closing.");
circuit_close(circ);
return;
}
} else {
if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'e') < 0) {
log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward) failed. Closing.");
circuit_close(circ);
return;
}
}
}
}
......
......@@ -61,6 +61,10 @@ void connection_free(connection_t *conn) {
buf_free(conn->outbuf);
if(conn->address)
free(conn->address);
if(conn->dest_addr)
free(conn->dest_addr);
if(conn->dest_port)
free(conn->dest_port);
/* FIXME should we do these for all connections, or just ORs, or what */
if(conn->type == CONN_TYPE_OR ||
......@@ -161,18 +165,11 @@ int connection_handle_listener_read(connection_t *conn, int new_type, int new_st
return 0;
}
int retry_all_connections(routerinfo_t **router_array, int rarray_len,
RSA *prkey, uint16_t or_port, uint16_t op_port, uint16_t ap_port) {
/* start all connections that should be up but aren't */
routerinfo_t *router;
int i;
/* private function, to create the 'local' variable used below */
static int learn_local(struct sockaddr_in *local) {
/* local host information */
char localhostname[512];
struct hostent *localhost;
struct sockaddr_in local; /* local address */
/* obtain local host information */
if(gethostname(localhostname,512) < 0) {
......@@ -184,31 +181,69 @@ int retry_all_connections(routerinfo_t **router_array, int rarray_len,
log(LOG_ERR,"Error obtaining local host info.");
return -1;
}
memset((void *)&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(or_port);
memcpy((void *)&local.sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
for (i=0;i<rarray_len;i++) {
router = router_array[i];
if(!connection_get_by_addr_port(router->addr,router->port)) { /* not in the list */
connect_to_router(router, prkey, &local);
memset((void *)local,0,sizeof(struct sockaddr_in));
local->sin_family = AF_INET;
local->sin_addr.s_addr = INADDR_ANY;
memcpy((void *)&local->sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
return 0;
}
int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len,
RSA *prkey, uint16_t or_listenport, uint16_t op_listenport, uint16_t ap_listenport) {
/* start all connections that should be up but aren't */
routerinfo_t *router;
int i;
struct sockaddr_in local; /* local address */
if(learn_local(&local) < 0)
return -1;
local.sin_port = htons(or_listenport);
if(role & ROLE_OR_CONNECT_ALL) {
for (i=0;i<rarray_len;i++) {
router = router_array[i];
if(!connection_get_by_addr_port(router->addr,router->or_port)) { /* not in the list */
log(LOG_DEBUG,"retry_all_connections(): connecting to OR %s:%u.",router->address,ntohs(router->or_port));
connection_or_connect_as_or(router, prkey, &local);
}
}
}
if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) {
connection_or_create_listener(prkey, &local);
if(role & ROLE_OR_LISTEN) {
if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) {
connection_or_create_listener(prkey, &local);
}
}
local.sin_port = htons(op_port);
if(!connection_get_by_type(CONN_TYPE_OP_LISTENER)) {
connection_op_create_listener(prkey, &local);
if(role & ROLE_OP_LISTEN) {
local.sin_port = htons(op_listenport);
if(!connection_get_by_type(CONN_TYPE_OP_LISTENER)) {
connection_op_create_listener(prkey, &local);
}
}
if(role & ROLE_AP_LISTEN) {
local.sin_port = htons(ap_listenport);
if(!connection_get_by_type(CONN_TYPE_AP_LISTENER)) {
connection_ap_create_listener(NULL, &local); /* no need to tell it the private key. */
}
}
return 0;
}
connection_t *connection_connect_to_router_as_op(routerinfo_t *router, RSA *prkey, uint16_t local_or_port) {
struct sockaddr_in local; /* local address */
if(learn_local(&local) < 0)
return NULL;
local.sin_port = htons(local_or_port);
return connection_or_connect_as_or(router, prkey, &local);
}
int connection_read_to_buf(connection_t *conn) {
return read_to_buf(conn->s, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen, &conn->inbuf_reached_eof);
}
......@@ -234,6 +269,7 @@ int connection_send_destroy(aci_t aci, connection_t *conn) {
assert(conn);
if(conn->type == CONN_TYPE_OP ||
conn->type == CONN_TYPE_AP ||
conn->type == CONN_TYPE_EXIT) {
log(LOG_DEBUG,"connection_send_destroy(): At an edge. Marking connection for close.");
conn->marked_for_close = 1;
......@@ -296,12 +332,66 @@ int connection_process_inbuf(connection_t *conn) {
return connection_or_process_inbuf(conn);
case CONN_TYPE_EXIT:
return connection_exit_process_inbuf(conn);
case CONN_TYPE_AP:
return connection_ap_process_inbuf(conn);
default:
log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type.");
return -1;
}
}
int connection_package_raw_inbuf(connection_t *conn) {
int amount_to_process;
cell_t cell;
circuit_t *circ;
assert(conn);
assert(conn->type == CONN_TYPE_EXIT || conn->type == CONN_TYPE_AP);
amount_to_process = conn->inbuf_datalen;
if(!amount_to_process)
return 0;
if(amount_to_process > CELL_PAYLOAD_SIZE) {
cell.length = CELL_PAYLOAD_SIZE;
} else {
cell.length = amount_to_process;
}
if(connection_fetch_from_buf(cell.payload, cell.length, conn) < 0)
return -1;
circ = circuit_get_by_conn(conn);
if(!circ) {
log(LOG_DEBUG,"connection_raw_package_inbuf(): conn has no circuits!");
return -1;
}
log(LOG_DEBUG,"connection_raw_package_inbuf(): Packaging %d bytes.",cell.length);
if(circ->n_conn == conn) { /* send it backward. we're an exit. */
cell.aci = circ->p_aci;
cell.command = CELL_DATA;
if(circuit_deliver_data_cell(&cell, circ, circ->p_conn, 'e') < 0) {
log(LOG_DEBUG,"connection_raw_package_inbuf(): circuit_deliver_data_cell (backward) failed. Closing.");
circuit_close(circ);
return 0;
}
} else { /* send it forward. we're an AP */
cell.aci = circ->n_aci;
cell.command = CELL_DATA;
if(circuit_deliver_data_cell(&cell, circ, circ->n_conn, 'e') < 0) {
/* yes, we use 'e' here, because the AP connection must *encrypt* its input. */
log(LOG_DEBUG,"connection_raw_package_inbuf(): circuit_deliver_data_cell (forward) failed. Closing.");
circuit_close(circ);
return 0;
}
}
if(amount_to_process > CELL_PAYLOAD_SIZE)
return connection_package_raw_inbuf(conn);
return 0;
}
int connection_finished_flushing(connection_t *conn) {
assert(conn);
......@@ -309,6 +399,8 @@ int connection_finished_flushing(connection_t *conn) {
log(LOG_DEBUG,"connection_finished_flushing() entered. Socket %u.", conn->s);
switch(conn->type) {
case CONN_TYPE_AP:
return connection_ap_finished_flushing(conn);
case CONN_TYPE_OP:
return connection_op_finished_flushing(conn);
case CONN_TYPE_OR:
......
<
#include "or.h"
extern int global_role; /* from main.c */
int connection_ap_process_inbuf(connection_t *conn) {
assert(conn && conn->type == CONN_TYPE_AP);
if(conn->inbuf_reached_eof) {
/* eof reached, kill it. */
log(LOG_DEBUG,"connection_ap_process_inbuf(): conn reached eof. Closing.");
return -1;
}
log(LOG_DEBUG,"connection_ap_process_inbuf(): state %d.",conn->state);
switch(conn->state) {
case AP_CONN_STATE_SS_WAIT:
return ap_handshake_process_ss(conn);
case AP_CONN_STATE_OPEN:
return connection_package_raw_inbuf(conn);
default:
log(LOG_DEBUG,"connection_ap_process_inbuf() called in state where I'm waiting. Ignoring buf for now.");
}
return 0;
}
int ap_handshake_process_ss(connection_t *conn) {
uint16_t len;
assert(conn);
log(LOG_DEBUG,"ap_handshake_process_ss() entered.");
if(!conn->ss_received) { /* try to pull it in */
if(conn->inbuf_datalen < sizeof(ss_t)) /* entire ss available? */
return 0; /* not yet */
if(connection_fetch_from_buf((char *)&conn->ss,sizeof(ss_t),conn) < 0)
return -1;
conn->ss_received = sizeof(ss_t);
log(LOG_DEBUG,"ap_handshake_process_ss(): Successfully read ss.");
if ((conn->ss.version == 0) || (conn->ss.version != VERSION)) { /* unsupported version */
log(LOG_DEBUG,"ap_handshake_process_ss(): ss: Unsupported version.");
return -1;
}
if (conn->ss.addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) { /* unrecognized address format */
log(LOG_DEBUG,"ap_handshake_process_ss(): ss: Unrecognized address format.");
return -1;
}
}
if(!conn->dest_addr) { /* no dest_addr found yet */
if(conn->inbuf_datalen < sizeof(uint16_t))
return 0; /* not yet */
if(connection_fetch_from_buf((char *)&len,sizeof(uint16_t),conn) < 0)
return -1;
len = ntohs(len);
if(len > 512) {
log(LOG_DEBUG,"ap_handshake_process_ss(): Addr length %d too high.",len);
return -1;
}
conn->dest_addr = malloc(len+1);
if(!conn->dest_addr) {
log(LOG_DEBUG,"ap_handshake_process_ss(): Addr malloc failed");
return -1;
}
conn->dest_addr[len] = 0; /* null terminate it */
conn->dest_addr_len = len;
log(LOG_DEBUG,"Preparing a dest_addr of %d+1 bytes.",len);
}
if(conn->dest_addr_len != conn->dest_addr_received) { /* try to fetch it all in */
if(conn->inbuf_datalen < conn->dest_addr_len)
return 0; /* not yet */
if(connection_fetch_from_buf(conn->dest_addr,conn->dest_addr_len,conn) < 0)
return -1;
log(LOG_DEBUG,"ap_handshake_process_ss(): Read dest_addr '%s'.",conn->dest_addr);
conn->dest_addr_received = conn->dest_addr_len;
}
/* now do the same thing for port */
if(!conn->dest_port) { /* no dest_port found yet */
if(conn->inbuf_datalen < sizeof(uint16_t))
return 0; /* not yet */
if(connection_fetch_from_buf((char *)&len,sizeof(uint16_t),conn) < 0)
return -1;
len = ntohs(len);
if(len > 10) {
log(LOG_DEBUG,"ap_handshake_process_ss(): Port length %d too high.",len);
return -1;
}
conn->dest_port = malloc(len+1);
if(!conn->dest_port) {
log(LOG_DEBUG,"ap_handshake_process_ss(): Port malloc failed");
return -1;
}
conn->dest_port[len] = 0; /* null terminate it */
conn->dest_port_len = len;
log(LOG_DEBUG,"Preparing a dest_port of %d+1 bytes.",len);
}
if(conn->dest_port_len != conn->dest_port_received) { /* try to fetch it all in */
if(conn->inbuf_datalen < conn->dest_port_len)
return 0; /* not yet */
if(connection_fetch_from_buf(conn->dest_port,conn->dest_port_len,conn) < 0)
return -1;
log(LOG_DEBUG,"ap_handshake_process_ss(): Read dest_port (network order) '%s'.",conn->dest_port);
conn->dest_port_received = conn->dest_port_len;
}
/* now we're all ready to make an onion, etc */
return ap_handshake_create_onion(conn);
}
int ap_handshake_create_onion(connection_t *conn) {
int i;
int routelen = 0; /* length of the route */
unsigned int *route = NULL; /* hops in the route as an array of indexes into rarray */
unsigned char *onion = NULL; /* holds the onion */
int onionlen = 0; /* onion length in host order */
crypt_path_t **cpath = NULL; /* defines the crypt operations that need to be performed on incoming/outgoing data */
assert(conn);
/* choose a route */
route = (unsigned int *)router_new_route(&routelen);
if (!route) {
log(LOG_ERR,"ap_handshake_create_onion(): Error choosing a route through the OR network.");
return -1;
}
log(LOG_DEBUG,"ap_handshake_create_onion(): Chosen a route of length %u : ",routelen);
#if 0
for (i=routelen-1;i>=0;i--)
{
log(LOG_DEBUG,"ap_handshake_process_ss() : %u : %s:%u, %u",routelen-i,(routerarray[route[i]])->address,ntohs((routerarray[route[i]])->port),RSA_size((routerarray[route[i]])->pkey));
}
#endif
/* allocate memory for the crypt path */
cpath = malloc(routelen * sizeof(crypt_path_t *));
if (!cpath) {
log(LOG_ERR,"ap_handshake_create_onion(): Error allocating memory for cpath.");
free(route);
return -1;
}
/* create an onion and calculate crypto keys */
onion = router_create_onion(route,routelen,&onionlen,cpath);
if (!onion) {
log(LOG_ERR,"ap_handshake_create_onion(): Error creating an onion.");
free(route);
free(cpath); /* it's got nothing in it, since !onion */
return -1;
}
log(LOG_DEBUG,"ap_handshake_create_onion(): Created an onion of size %u bytes.",onionlen);