mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
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
This commit is contained in:
parent
b34fad4d38
commit
d982925593
@ -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
|
||||
|
123
src/or/circuit.c
123
src/or/circuit.c
@ -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));
|
||||
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));
|
||||
|
||||
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);
|
||||
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:
|
||||
|
381
src/or/connection_ap.c
Normal file
381
src/or/connection_ap.c
Normal file
@ -0,0 +1,381 @@
|
||||
|
||||
#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);
|
||||
log(LOG_DEBUG,"ap_handshake_create_onion(): Crypt path :");
|
||||
for (i=0;i<routelen;i++) {
|
||||
log(LOG_DEBUG,"ap_handshake_create_onion() : %u/%u",(cpath[i])->forwf, (cpath[i])->backf);
|
||||
}
|
||||
|
||||
return ap_handshake_establish_circuit(conn, route, routelen, onion, onionlen, cpath);
|
||||
}
|
||||
|
||||
int ap_handshake_establish_circuit(connection_t *conn, unsigned int *route, int routelen, char *onion,
|
||||
int onionlen, crypt_path_t **cpath) {
|
||||
routerinfo_t *firsthop;
|
||||
connection_t *n_conn;
|
||||
circuit_t *circ;
|
||||
|
||||
/* now see if we're already connected to the first OR in 'route' */
|
||||
firsthop = router_get_first_in_route(route, routelen);
|
||||
assert(firsthop); /* should always be defined */
|
||||
free(route); /* we don't need it anymore */
|
||||
|
||||
circ = circuit_new(0, conn); /* sets circ->p_aci and circ->p_conn */
|
||||
circ->n_addr = firsthop->addr;
|
||||
circ->n_port = firsthop->or_port;
|
||||
circ->state = CIRCUIT_STATE_OR_WAIT;
|
||||
circ->onion = onion;
|
||||
circ->onionlen = onionlen;
|
||||
circ->cpath = cpath;
|
||||
circ->cpathlen = routelen;
|
||||
|
||||
log(LOG_DEBUG,"ap_handshake_establish_circuit(): Looking for firsthop '%s:%u'",
|
||||
firsthop->address,ntohs(firsthop->or_port));
|
||||
n_conn = connection_get_by_addr_port(firsthop->addr,firsthop->or_port);
|
||||
if(!n_conn) { /* not currently connected */
|
||||
if(global_role & ROLE_OR_CONNECT_ALL) { /* we would be connected if he were up. but he's not. */
|
||||
log(LOG_DEBUG,"ap_handshake_establish_circuit(): Route's firsthop isn't connected.");
|
||||
circuit_close(circ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ok, launch the connection */
|
||||
n_conn = connect_to_router_as_op(firsthop);
|
||||
/* FIXME react to this somehow */
|
||||
if(!n_conn) { /* connect failed, forget the whole thing */
|
||||
log(LOG_DEBUG,"ap_handshake_establish_circuit(): connect to firsthop failed. Closing.");
|
||||
circuit_close(circ);
|
||||
return -1;
|
||||
}
|
||||
conn->state = AP_CONN_STATE_OR_WAIT;
|
||||
connection_watch_events(conn, 0); /* Stop listening for input from the AP! */
|
||||
return 0; /* return success. The onion/circuit/etc will be taken care of automatically
|
||||
* (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN.
|
||||
*/
|
||||
} else { /* it's already open. use it. */
|
||||
return ap_handshake_send_onion(conn, n_conn, circ);
|
||||
}
|
||||
}
|
||||
|
||||
/* find the circ that's waiting on me, if any, and get it to send its onion */
|
||||
int ap_handshake_n_conn_open(connection_t *or_conn) {
|
||||
circuit_t *circ;
|
||||
|
||||
log(LOG_DEBUG,"ap_handshake_n_conn_open(): Starting.");
|
||||
circ = circuit_get_by_naddr_nport(or_conn->addr, or_conn->port);
|
||||
if(!circ)
|
||||
return 0; /* i'm ok with that */
|
||||
|
||||
if(circ->p_conn->state != AP_CONN_STATE_OR_WAIT) {
|
||||
log(LOG_DEBUG,"Bug: ap_handshake_n_conn_open() got an ap_conn not in OR_WAIT state.");
|
||||
}
|
||||
connection_watch_events(or_conn, POLLIN); /* resume listening for reads */
|
||||
log(LOG_DEBUG,"ap_handshake_n_conn_open(): Found circ, sending onion.");
|
||||
return ap_handshake_send_onion(circ->p_conn, or_conn, circ);
|
||||
}
|
||||
|
||||
int ap_handshake_send_onion(connection_t *ap_conn, connection_t *n_conn, circuit_t *circ) {
|
||||
cell_t cell;
|
||||
int tmpbuflen, dataleft;
|
||||
char *tmpbuf;
|
||||
char zero=0;
|
||||
|
||||
circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, ACI_TYPE_BOTH);
|
||||
circ->n_conn = n_conn;
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): n_conn is %s:%u",n_conn->address,ntohs(n_conn->port));
|
||||
|
||||
/* deliver the onion as one or more create cells */
|
||||
cell.command = CELL_CREATE;
|
||||
cell.aci = circ->n_aci;
|
||||
|
||||
tmpbuflen = circ->onionlen+4;
|
||||
tmpbuf = malloc(tmpbuflen);
|
||||
if(!tmpbuf)
|
||||
return -1;
|
||||
circ->onionlen = htonl(circ->onionlen);
|
||||
memcpy(tmpbuf,&circ->onionlen,4);
|
||||
circ->onionlen = ntohl(circ->onionlen);
|
||||
memcpy(tmpbuf+4, circ->onion, circ->onionlen);
|
||||
|
||||
dataleft = tmpbuflen;
|
||||
while(dataleft) {
|
||||
cell.command = CELL_CREATE;
|
||||
cell.aci = circ->n_aci;
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): Sending a create cell for the onion...");
|
||||
if(dataleft >= CELL_PAYLOAD_SIZE) {
|
||||
cell.length = CELL_PAYLOAD_SIZE;
|
||||
memcpy(cell.payload, tmpbuf + tmpbuflen - dataleft, CELL_PAYLOAD_SIZE);
|
||||
connection_write_cell_to_buf(&cell, n_conn); /* clobbers cell */
|
||||
dataleft -= CELL_PAYLOAD_SIZE;
|
||||
} else { /* last cell */
|
||||
cell.length = dataleft;
|
||||
memcpy(cell.payload, tmpbuf + tmpbuflen - dataleft, dataleft);
|
||||
connection_write_cell_to_buf(&cell, n_conn); /* clobbers cell */
|
||||
dataleft = 0;
|
||||
}
|
||||
}
|
||||
free(tmpbuf);
|
||||
|
||||
/* deliver the ss in a data cell */
|
||||
cell.command = CELL_DATA;
|
||||
cell.aci = circ->n_aci;
|
||||
cell.length = sizeof(ss_t);
|
||||
memcpy(cell.payload, &ap_conn->ss, sizeof(ss_t));
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): Sending a data cell for ss...");
|
||||
if(circuit_deliver_data_cell(&cell, circ, circ->n_conn, 'e') < 0) {
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): failed to deliver ss cell. Closing.");
|
||||
circuit_close(circ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* deliver the dest_addr in a data cell */
|
||||
cell.command = CELL_DATA;
|
||||
cell.aci = circ->n_aci;
|
||||
cell.length = ap_conn->dest_addr_len+1;
|
||||
strncpy(cell.payload, ap_conn->dest_addr, ap_conn->dest_addr_len+1);
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): Sending a data cell for addr...");
|
||||
if(circuit_deliver_data_cell(&cell, circ, circ->n_conn, 'e') < 0) {
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): failed to deliver addr cell. Closing.");
|
||||
circuit_close(circ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* deliver the dest_port in a data cell */
|
||||
cell.command = CELL_DATA;
|
||||
cell.aci = circ->n_aci;
|
||||
cell.length = ap_conn->dest_port_len+1;
|
||||
strncpy(cell.payload, ap_conn->dest_port, ap_conn->dest_port_len+1);
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): Sending a data cell for port...");
|
||||
if(circuit_deliver_data_cell(&cell, circ, circ->n_conn, 'e') < 0) {
|
||||
log(LOG_DEBUG,"ap_handshake_send_onion(): failed to deliver port cell. Closing.");
|
||||
circuit_close(circ);
|
||||
return -1;
|
||||
}
|
||||
|
||||
circ->state = CIRCUIT_STATE_OPEN;
|
||||
ap_conn->state = AP_CONN_STATE_OPEN;
|
||||
|
||||
/* FIXME should set circ->expire to something here */
|
||||
|
||||
/* now we want to give the AP a "0" byte, because it wants to hear
|
||||
* back from us */
|
||||
connection_write_to_buf(&zero, 1, ap_conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_ap_process_data_cell(cell_t *cell, connection_t *conn) {
|
||||
|
||||
/* an incoming data cell has arrived */
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_AP);
|
||||
|
||||
if(conn->state == AP_CONN_STATE_OPEN) {
|
||||
log(LOG_DEBUG,"connection_ap_process_data_cell(): In state 'open', writing to buf.");
|
||||
return connection_write_to_buf(cell->payload, cell->length, conn);
|
||||
}
|
||||
|
||||
/* else we shouldn't have gotten this cell */
|
||||
log(LOG_DEBUG,"connection_ap_process_data_cell(): Got a data cell when not in 'open' state. Closing.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int connection_ap_finished_flushing(connection_t *conn) {
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_AP);
|
||||
|
||||
switch(conn->state) {
|
||||
case AP_CONN_STATE_OPEN:
|
||||
/* FIXME down the road, we'll clear out circuits that are pending to close */
|
||||
connection_watch_events(conn, POLLIN);
|
||||
return 0;
|
||||
default:
|
||||
log(LOG_DEBUG,"Bug: connection_ap_finished_flushing() called in unexpected state.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int connection_ap_create_listener(RSA *prkey, struct sockaddr_in *local) {
|
||||
log(LOG_DEBUG,"connection_create_ap_listener starting");
|
||||
return connection_create_listener(prkey, local, CONN_TYPE_AP_LISTENER);
|
||||
}
|
||||
|
||||
int connection_ap_handle_listener_read(connection_t *conn) {
|
||||
log(LOG_NOTICE,"AP: Received a connection request. Waiting for keys.");
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_AP, AP_CONN_STATE_SS_WAIT);
|
||||
}
|
||||
|
@ -18,53 +18,12 @@ int connection_exit_process_inbuf(connection_t *conn) {
|
||||
log(LOG_DEBUG,"connection_exit_process_inbuf(): text from server while in 'connecting' state. Leaving it on buffer.");
|
||||
return 0;
|
||||
case EXIT_CONN_STATE_OPEN:
|
||||
return connection_exit_package_inbuf(conn);
|
||||
return connection_package_raw_inbuf(conn);
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int connection_exit_package_inbuf(connection_t *conn) {
|
||||
int amount_to_process;
|
||||
cell_t cell;
|
||||
circuit_t *circ;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_EXIT);
|
||||
|
||||
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_exit_package_inbuf(): conn has no circuits!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"connection_exit_package_inbuf(): Packaging %d bytes.",cell.length);
|
||||
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_exit_package_inbuf(): circuit_deliver_data_cell (backward) failed. Closing.");
|
||||
circuit_close(circ);
|
||||
return 0;
|
||||
}
|
||||
if(amount_to_process > CELL_PAYLOAD_SIZE)
|
||||
return(connection_exit_package_inbuf(conn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connection_exit_finished_flushing(connection_t *conn) {
|
||||
int e, len=sizeof(e);
|
||||
|
||||
|
@ -41,6 +41,24 @@ int connection_or_finished_flushing(connection_t *conn) {
|
||||
assert(conn && conn->type == CONN_TYPE_OR);
|
||||
|
||||
switch(conn->state) {
|
||||
case OR_CONN_STATE_OP_CONNECTING:
|
||||
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */
|
||||
if(errno != EINPROGRESS){
|
||||
/* yuck. kill it. */
|
||||
log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing.");
|
||||
return -1;
|
||||
} else {
|
||||
return 0; /* no change, see if next time is better */
|
||||
}
|
||||
}
|
||||
/* the connect has finished. */
|
||||
|
||||
log(LOG_DEBUG,"connection_or_finished_flushing() : Connection to router %s:%u established.",
|
||||
conn->address,ntohs(conn->port));
|
||||
|
||||
return or_handshake_op_send_keys(conn);
|
||||
case OR_CONN_STATE_OP_SENDING_KEYS:
|
||||
return or_handshake_op_finished_sending_keys(conn);
|
||||
case OR_CONN_STATE_CLIENT_CONNECTING:
|
||||
if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */
|
||||
if(errno != EINPROGRESS){
|
||||
@ -112,28 +130,23 @@ void conn_or_init_crypto(connection_t *conn) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* auth handshake, as performed by OR *initiating* the connection
|
||||
*
|
||||
/* helper function for connection_or_connect_as_or and _as_op.
|
||||
* returns NULL if the connection fails. If it succeeds, it sets
|
||||
* *result to 1 if connect() returned before completing, or to 2
|
||||
* if it completed, and returns the new conn.
|
||||
*/
|
||||
|
||||
int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) {
|
||||
connection_t *connection_or_connect(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local,
|
||||
uint16_t port, int *result) {
|
||||
connection_t *conn;
|
||||
struct sockaddr_in router_addr;
|
||||
int s;
|
||||
|
||||
assert(router && prkey && local);
|
||||
|
||||
if(router->addr == local->sin_addr.s_addr && router->port == local->sin_port) {
|
||||
/* this is me! don't connect to me. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
conn = connection_new(CONN_TYPE_OR);
|
||||
if(!conn)
|
||||
return NULL;
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
conn->addr = router->addr, conn->port = router->port;
|
||||
conn->addr = router->addr, conn->port = router->or_port; /* NOTE we store or_port here always */
|
||||
conn->prkey = prkey;
|
||||
conn->min = router->min, conn->max = router->max;
|
||||
conn->pkey = router->pkey;
|
||||
@ -145,36 +158,37 @@ int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *loca
|
||||
{
|
||||
log(LOG_ERR,"Error creating network socket.");
|
||||
connection_free(conn);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */
|
||||
|
||||
memset((void *)&router_addr,0,sizeof(router_addr));
|
||||
router_addr.sin_family = AF_INET;
|
||||
router_addr.sin_port = router->port;
|
||||
router_addr.sin_port = port;
|
||||
memcpy((void *)&router_addr.sin_addr, &router->addr, sizeof(uint32_t));
|
||||
|
||||
log(LOG_DEBUG,"connect_to_router() : Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),ntohs(router->port));
|
||||
log(LOG_DEBUG,"connection_or_connect() : Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),ntohs(port));
|
||||
|
||||
if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){
|
||||
if(errno != EINPROGRESS){
|
||||
/* yuck. kill it. */
|
||||
connection_free(conn);
|
||||
return -1;
|
||||
return NULL;
|
||||
} else {
|
||||
/* it's in progress. set state appropriately and return. */
|
||||
conn->s = s;
|
||||
|
||||
conn->state = OR_CONN_STATE_CLIENT_CONNECTING;
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */
|
||||
log(LOG_DEBUG,"connect_to_router() : connect in progress.");
|
||||
log(LOG_DEBUG,"connection_or_connect() : connect in progress.");
|
||||
connection_watch_events(conn, POLLOUT | POLLIN);
|
||||
return 0;
|
||||
*result = 1; /* connecting */
|
||||
return conn;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,23 +197,173 @@ int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *loca
|
||||
|
||||
if(connection_add(conn) < 0) { /* no space, forget it */
|
||||
connection_free(conn);
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"connect_to_router() : Connection to router %s:%u established.",router->address,ntohs(router->port));
|
||||
log(LOG_DEBUG,"connection_or_connect() : Connection to router %s:%u established.",router->address,ntohs(port));
|
||||
|
||||
/* move to the next step in the handshake */
|
||||
if(or_handshake_client_send_auth(conn) < 0) {
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
*result = 2; /* connection finished */
|
||||
return(conn);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* handshake for connecting to the op_port of an onion router
|
||||
*
|
||||
*/
|
||||
|
||||
connection_t *connection_or_connect_as_op(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) {
|
||||
connection_t *conn;
|
||||
int result=0; /* so connection_or_connect() can tell us what happened */
|
||||
|
||||
assert(router && prkey && local);
|
||||
|
||||
if(router->addr == local->sin_addr.s_addr && router->or_port == local->sin_port) {
|
||||
/* this is me! don't connect to me. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* this function should never be called if we're already connected to router, but */
|
||||
/* FIXME we should check here if we're already connected, and return the conn */
|
||||
|
||||
conn = connection_or_connect(router, prkey, local, router->op_port, &result);
|
||||
if(!conn)
|
||||
return NULL;
|
||||
|
||||
assert(result != 0); /* if conn is defined, then it must have set result */
|
||||
|
||||
/* now we know it succeeded */
|
||||
if(result == 1) {
|
||||
conn->state = OR_CONN_STATE_OP_CONNECTING;
|
||||
return conn;
|
||||
}
|
||||
|
||||
if(result == 2) {
|
||||
/* move to the next step in the handshake */
|
||||
if(or_handshake_op_send_keys(conn) < 0) {
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
return NULL; /* shouldn't get here; to keep gcc happy */
|
||||
}
|
||||
|
||||
int or_handshake_op_send_keys(connection_t *conn) {
|
||||
int x;
|
||||
uint32_t bandwidth = DEFAULT_BANDWIDTH_OP;
|
||||
unsigned char message[20]; /* bandwidth(32bits), forward key(64bits), backward key(64bits) */
|
||||
unsigned char cipher[128];
|
||||
int retval;
|
||||
|
||||
assert(conn && conn->type == CONN_TYPE_OR);
|
||||
|
||||
/* generate random keys */
|
||||
if(!RAND_bytes(conn->f_session_key,8) ||
|
||||
!RAND_bytes(conn->b_session_key,8)) {
|
||||
log(LOG_ERR,"Cannot generate a secure DES key.");
|
||||
return -1;
|
||||
}
|
||||
log(LOG_DEBUG,"or_handshake_op_send_keys() : Generated DES keys.");
|
||||
/* compose the message */
|
||||
memcpy((void *)message, (void *)&bandwidth, 4);
|
||||
memcpy((void *)(message + 4), (void *)conn->f_session_key, 8);
|
||||
memcpy((void *)(message + 12), (void *)conn->b_session_key, 8);
|
||||
printf("f_session_key: ");
|
||||
for(x=0;x<8;x++) {
|
||||
printf("%d ",conn->f_session_key[x]);
|
||||
}
|
||||
printf("\nb_session_key: ");
|
||||
for(x=0;x<8;x++) {
|
||||
printf("%d ",conn->b_session_key[x]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* encrypt with RSA */
|
||||
if(RSA_public_encrypt(20, message, cipher, conn->pkey, RSA_PKCS1_PADDING) < 0) {
|
||||
log(LOG_ERR,"or_handshake_op_send_keys(): Public key encryption failed.");
|
||||
return -1;
|
||||
}
|
||||
log(LOG_DEBUG,"or_handshake_op_send_keys() : Encrypted authentication message.");
|
||||
|
||||
/* send message */
|
||||
|
||||
if(connection_write_to_buf(cipher, 128, conn) < 0) {
|
||||
log(LOG_DEBUG,"or_handshake_op_send_keys(): my outbuf is full. Oops.");
|
||||
return -1;
|
||||
}
|
||||
retval = connection_flush_buf(conn);
|
||||
if(retval < 0) {
|
||||
log(LOG_DEBUG,"or_handshake_op_send_keys(): bad socket while flushing.");
|
||||
return -1;
|
||||
}
|
||||
if(retval > 0) {
|
||||
/* still stuff on the buffer. */
|
||||
conn->state = OR_CONN_STATE_OP_SENDING_KEYS;
|
||||
connection_watch_events(conn, POLLOUT | POLLIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* it finished sending */
|
||||
log(LOG_DEBUG,"or_handshake_op_send_keys(): Finished sending authentication message.");
|
||||
return or_handshake_op_finished_sending_keys(conn);
|
||||
}
|
||||
|
||||
int or_handshake_op_finished_sending_keys(connection_t *conn) {
|
||||
|
||||
/* do crypto initialization, etc */
|
||||
conn_or_init_crypto(conn);
|
||||
|
||||
conn->state = OR_CONN_STATE_OPEN;
|
||||
connection_watch_events(conn, POLLIN); /* give it a default, tho the ap_handshake call may change it */
|
||||
ap_handshake_n_conn_open(conn); /* send the pending onion */
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* auth handshake, as performed by OR *initiating* the connection
|
||||
*
|
||||
*/
|
||||
|
||||
connection_t *connection_or_connect_as_or(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) {
|
||||
connection_t *conn;
|
||||
int result=0; /* so connection_or_connect() can tell us what happened */
|
||||
|
||||
assert(router && prkey && local);
|
||||
|
||||
if(router->addr == local->sin_addr.s_addr && router->or_port == local->sin_port) {
|
||||
/* this is me! don't connect to me. */
|
||||
log(LOG_DEBUG,"connection_or_connect_as_or(): This is me. Skipping.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn = connection_or_connect(router, prkey, local, router->or_port, &result);
|
||||
if(!conn)
|
||||
return NULL;
|
||||
|
||||
/* now we know it succeeded */
|
||||
if(result == 1) {
|
||||
conn->state = OR_CONN_STATE_CLIENT_CONNECTING;
|
||||
return conn;
|
||||
}
|
||||
|
||||
if(result == 2) {
|
||||
/* move to the next step in the handshake */
|
||||
if(or_handshake_client_send_auth(conn) < 0) {
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
return NULL; /* shouldn't get here; to keep gcc happy */
|
||||
}
|
||||
|
||||
int or_handshake_client_send_auth(connection_t *conn) {
|
||||
int retval;
|
||||
char keys[16];
|
||||
char buf[44];
|
||||
char cipher[128];
|
||||
|
||||
@ -207,28 +371,25 @@ int or_handshake_client_send_auth(connection_t *conn) {
|
||||
return -1;
|
||||
|
||||
/* generate random keys */
|
||||
retval = RAND_bytes(keys,16);
|
||||
if (retval != 1) /* error */
|
||||
{
|
||||
if(!RAND_bytes(conn->f_session_key,8) ||
|
||||
!RAND_bytes(conn->b_session_key,8)) {
|
||||
log(LOG_ERR,"Cannot generate a secure DES key.");
|
||||
return -1;
|
||||
}
|
||||
log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated DES keys.");
|
||||
|
||||
/* save keys */
|
||||
memcpy(conn->f_session_key,keys,8);
|
||||
memcpy(conn->b_session_key,keys+8,8);
|
||||
|
||||
/* generate first message */
|
||||
memcpy(buf,&conn->local.sin_addr,4); /* local address */
|
||||
memcpy(buf+4,(void *)&conn->local.sin_port,2); /* local port */
|
||||
memcpy(buf+6, (void *)&conn->addr, 4); /* remote address */
|
||||
memcpy(buf+10, (void *)&conn->port, 2); /* remote port */
|
||||
memcpy(buf+12,keys,16); /* keys */
|
||||
memcpy(buf+12,conn->f_session_key,8); /* keys */
|
||||
memcpy(buf+20,conn->b_session_key,8);
|
||||
*((uint32_t *)(buf+28)) = htonl(conn->min); /* min link utilisation */
|
||||
*((uint32_t *)(buf+32)) = htonl(conn->max); /* maximum link utilisation */
|
||||
log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated first authentication message.");
|
||||
|
||||
|
||||
/* encrypt message */
|
||||
retval = RSA_public_encrypt(36,buf,cipher,conn->pkey,RSA_PKCS1_PADDING);
|
||||
if (retval == -1) /* error */
|
||||
@ -397,8 +558,7 @@ int or_handshake_server_process_auth(connection_t *conn) {
|
||||
retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING);
|
||||
if (retval == -1)
|
||||
{
|
||||
log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.",
|
||||
conn->address,ntohs(conn->port));
|
||||
log(LOG_ERR,"Public-key decryption failed processing auth message from new client.");
|
||||
log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",
|
||||
ERR_reason_error_string(ERR_get_error()));
|
||||
return -1;
|
||||
@ -421,7 +581,7 @@ int or_handshake_server_process_auth(connection_t *conn) {
|
||||
return -1;
|
||||
}
|
||||
log(LOG_DEBUG,"or_handshake_server_process_auth() : Router identified as %s:%u.",
|
||||
router->address,ntohs(router->port));
|
||||
router->address,ntohs(router->or_port));
|
||||
|
||||
if(connection_get_by_addr_port(addr,port)) {
|
||||
log(LOG_DEBUG,"or_handshake_server_process_auth(): That router is already connected. Dropping.");
|
||||
@ -447,7 +607,7 @@ int or_handshake_server_process_auth(connection_t *conn) {
|
||||
conn->max = max;
|
||||
|
||||
/* copy all relevant info to conn */
|
||||
conn->addr = router->addr, conn->port = router->port;
|
||||
conn->addr = router->addr, conn->port = router->or_port;
|
||||
conn->pkey = router->pkey;
|
||||
conn->address = strdup(router->address);
|
||||
|
||||
|
@ -8,20 +8,24 @@ static char *args = "hf:e:n:l:";
|
||||
|
||||
int loglevel = LOG_DEBUG;
|
||||
|
||||
int global_role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN;
|
||||
/* FIXME defaulting to all roles for now. should make it a config option though */
|
||||
|
||||
/* valid config file options */
|
||||
config_opt_t options[] =
|
||||
{
|
||||
{"RouterFile", CONFIG_TYPE_STRING, {0}, 0},
|
||||
{"PrivateKeyFile", CONFIG_TYPE_STRING, {0}, 0},
|
||||
{"EntryPort", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"NetworkPort", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"APPort", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"OPPort", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"ORPort", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"CoinWeight", CONFIG_TYPE_DOUBLE, {0}, 0},
|
||||
{"MaxConn", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"MaxConnTimeout", CONFIG_TYPE_INT, {0}, 0},
|
||||
{"TrafficShaping", CONFIG_TYPE_INT, {0}, 0},
|
||||
{0}
|
||||
};
|
||||
enum opts {
|
||||
RouterFile=0, PrivateKeyFile, EntryPort, NetworkPort, MaxConn, MaxConnTimeout, TrafficShaping
|
||||
RouterFile=0, PrivateKeyFile, APPort, OPPort, ORPort, CoinWeight, MaxConn, TrafficShaping
|
||||
};
|
||||
|
||||
connection_t *connection_array[MAXCONNECTIONS] =
|
||||
@ -138,7 +142,7 @@ routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
|
||||
for(i=0;i<rarray_len;i++)
|
||||
{
|
||||
router = router_array[i];
|
||||
if ((router->addr == addr) && (router->port == port))
|
||||
if ((router->addr == addr) && (router->or_port == port))
|
||||
return router;
|
||||
}
|
||||
|
||||
@ -146,6 +150,24 @@ routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
|
||||
|
||||
}
|
||||
|
||||
routerinfo_t *router_get_first_in_route(unsigned int *route, size_t routelen) {
|
||||
return router_array[route[routelen-1]];
|
||||
}
|
||||
|
||||
/* a wrapper around new_route. put all these in routers.c perhaps? */
|
||||
unsigned int *router_new_route(size_t *rlen) {
|
||||
return new_route(options[CoinWeight].r.d, router_array,rarray_len, rlen);
|
||||
}
|
||||
|
||||
/* a wrapper around create_onion */
|
||||
unsigned char *router_create_onion(unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp) {
|
||||
return create_onion(router_array,rarray_len,route,routelen,lenp,cpathp);
|
||||
}
|
||||
|
||||
connection_t *connect_to_router_as_op(routerinfo_t *router) {
|
||||
return connection_connect_to_router_as_op(router, prkey, options[ORPort].r.i);
|
||||
}
|
||||
|
||||
void connection_watch_events(connection_t *conn, short events) {
|
||||
|
||||
assert(conn && conn->poll_index < nfds);
|
||||
@ -167,6 +189,8 @@ void check_conn_read(int i) {
|
||||
retval = connection_op_handle_listener_read(conn);
|
||||
} else if (conn->type == CONN_TYPE_OR_LISTENER) {
|
||||
retval = connection_or_handle_listener_read(conn);
|
||||
} else if (conn->type == CONN_TYPE_AP_LISTENER) {
|
||||
retval = connection_ap_handle_listener_read(conn);
|
||||
} else {
|
||||
/* else it's an OP, OR, or exit */
|
||||
retval = connection_read_to_buf(conn);
|
||||
@ -228,7 +252,10 @@ void check_conn_marked(int i) {
|
||||
assert(conn);
|
||||
if(conn->marked_for_close) {
|
||||
log(LOG_DEBUG,"check_conn_marked(): Cleaning up connection.");
|
||||
connection_flush_buf(conn); /* flush it first */
|
||||
if(conn->s >= 0) { /* might be an incomplete exit connection */
|
||||
/* FIXME there's got to be a better way to check for this -- and make other checks? */
|
||||
connection_flush_buf(conn); /* flush it first */
|
||||
}
|
||||
connection_remove(conn);
|
||||
connection_free(conn);
|
||||
if(i<nfds) { /* we just replaced the one at i with a new one.
|
||||
@ -250,7 +277,6 @@ int do_main_loop(void) {
|
||||
}
|
||||
|
||||
/* load the private key */
|
||||
ERR_load_crypto_strings();
|
||||
prkey = load_prkey(options[PrivateKeyFile].r.str);
|
||||
if (!prkey)
|
||||
{
|
||||
@ -258,11 +284,12 @@ int do_main_loop(void) {
|
||||
exit(1);
|
||||
}
|
||||
log(LOG_DEBUG,"core : Loaded private key of size %u bytes.",RSA_size(prkey));
|
||||
ERR_free_strings();
|
||||
|
||||
/* try to connect to all the other ORs, and start the listeners */
|
||||
retry_all_connections(router_array, rarray_len, prkey,
|
||||
options[NetworkPort].r.i,options[EntryPort].r.i, 0);
|
||||
/* start-up the necessary connections based on global_role. This is where we
|
||||
* try to connect to all the other ORs, and start the listeners */
|
||||
retry_all_connections(global_role, router_array, rarray_len, prkey,
|
||||
options[ORPort].r.i,options[OPPort].r.i,
|
||||
options[APPort].r.i);
|
||||
|
||||
for(;;) {
|
||||
poll(poll_array, nfds, -1); /* poll until we have an event */
|
||||
@ -320,14 +347,24 @@ int main(int argc, char *argv[]) {
|
||||
log(LOG_ERR,"PrivateKeyFile option required but not found.");
|
||||
exit(1);
|
||||
}
|
||||
else if (options[EntryPort].err != 1)
|
||||
else if (options[CoinWeight].err != 1)
|
||||
{
|
||||
log(LOG_ERR,"EntryPort option required but not found.");
|
||||
log(LOG_ERR,"Error reading the CoinWeight option.");
|
||||
exit(1);
|
||||
}
|
||||
else if (options[NetworkPort].err != 1)
|
||||
else if (options[APPort].err != 1)
|
||||
{
|
||||
log(LOG_ERR,"NetworkPort option required but not found.");
|
||||
log(LOG_ERR,"APPort option required but not found.");
|
||||
exit(1);
|
||||
}
|
||||
else if (options[OPPort].err != 1)
|
||||
{
|
||||
log(LOG_ERR,"OPPort option required but not found.");
|
||||
exit(1);
|
||||
}
|
||||
else if (options[ORPort].err != 1)
|
||||
{
|
||||
log(LOG_ERR,"ORPort option required but not found.");
|
||||
exit(1);
|
||||
}
|
||||
else if (options[MaxConn].err != 1)
|
||||
@ -336,19 +373,6 @@ int main(int argc, char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
#if 0
|
||||
else if (options[MaxConnTimeout].err != 1)
|
||||
{
|
||||
conn_tout.tv_sec = OR_DEFAULT_CONN_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!options[MaxConnTimeout].r.i)
|
||||
conn_toutp = NULL;
|
||||
else
|
||||
conn_tout.tv_sec = options[MaxConnTimeout].r.i;
|
||||
}
|
||||
conn_tout.tv_usec = 0;
|
||||
|
||||
if (!options[TrafficShaping].err)
|
||||
{
|
||||
options[TrafficShaping].r.i = DEFAULT_POLICY;
|
||||
|
528
src/or/onion.c
528
src/or/onion.c
@ -60,3 +60,531 @@ int process_onion(circuit_t *circ, connection_t *conn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* uses a weighted coin with weight cw to choose a route length */
|
||||
int chooselen(double cw)
|
||||
{
|
||||
int len = 2;
|
||||
int retval = 0;
|
||||
unsigned char coin;
|
||||
|
||||
if ((cw < 0) || (cw >= 1)) /* invalid parameter */
|
||||
return -1;
|
||||
|
||||
while(1)
|
||||
{
|
||||
retval = RAND_pseudo_bytes(&coin,1);
|
||||
if (retval == -1)
|
||||
return -1;
|
||||
|
||||
if (coin > cw*255) /* don't extend */
|
||||
break;
|
||||
else
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* returns an array of pointers to routent that define a new route through the OR network
|
||||
* int cw is the coin weight to use when choosing the route
|
||||
* order of routers is from last to first
|
||||
*/
|
||||
unsigned int *new_route(double cw, routerinfo_t **rarray, size_t rarray_len, size_t *rlen)
|
||||
{
|
||||
int routelen = 0;
|
||||
int i = 0;
|
||||
int retval = 0;
|
||||
unsigned int *route = NULL;
|
||||
unsigned int oldchoice, choice;
|
||||
|
||||
if ( (cw >= 0) && (cw < 1) && (rarray) && (rlen) ) /* valid parameters */
|
||||
{
|
||||
routelen = chooselen(cw);
|
||||
if (routelen == -1)
|
||||
{
|
||||
log(LOG_ERR,"Choosing route length failed.");
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"new_route(): Chosen route length %u.",routelen);
|
||||
|
||||
/* allocate memory for the new route */
|
||||
route = (unsigned int *)malloc(routelen * sizeof(unsigned int));
|
||||
if (!route)
|
||||
{
|
||||
log(LOG_ERR,"Memory allocation failed.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
oldchoice = rarray_len;
|
||||
for(i=0;i<routelen;i++)
|
||||
{
|
||||
log(LOG_DEBUG,"new_route() : Choosing hop %u.",i);
|
||||
retval = RAND_pseudo_bytes((unsigned char *)&choice,sizeof(unsigned int));
|
||||
if (retval == -1)
|
||||
{
|
||||
free((void *)route);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
choice = choice % (rarray_len);
|
||||
log(LOG_DEBUG,"new_route() : Chosen router %u.",choice);
|
||||
if (choice == oldchoice) /* same router */
|
||||
{
|
||||
/* try again */
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
oldchoice = choice;
|
||||
route[i] = choice;
|
||||
}
|
||||
|
||||
*rlen = routelen;
|
||||
return route;
|
||||
} /* valid parameters */
|
||||
else /* invalid parameters */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* creates a new onion from route, stores it and its length into bufp and lenp respectively */
|
||||
unsigned char *create_onion(routerinfo_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp)
|
||||
{
|
||||
int i,j;
|
||||
int retval = 0;
|
||||
onion_layer_t *layer = NULL;
|
||||
crypt_path_t *hop = NULL;
|
||||
unsigned char *retbuf = NULL;
|
||||
unsigned char *bufp;
|
||||
routerinfo_t *router;
|
||||
|
||||
if ( (rarray) && (route) && (lenp) ) /* valid parameters */
|
||||
{
|
||||
/* calculate the size of the onion */
|
||||
*lenp = routelen * 28 + 100; /* 28 bytes per layer + 100 bytes padding for the innermost layer */
|
||||
log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*lenp);
|
||||
|
||||
/* allocate memory for the onion */
|
||||
bufp = (unsigned char *)malloc(*lenp);
|
||||
if (!bufp)
|
||||
{
|
||||
log(LOG_ERR,"Error allocating memory.");
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"create_onion() : Allocated memory for the onion.");
|
||||
|
||||
for (retval=0; retval<routelen;retval++)
|
||||
{
|
||||
log(LOG_DEBUG,"create_onion() : %u : %s:%u, %u/%u",routelen-retval,inet_ntoa(*((struct in_addr *)&((rarray[route[retval]])->addr))),ntohs((rarray[route[retval]])->or_port),(rarray[route[retval]])->pkey,RSA_size((rarray[route[retval]])->pkey));
|
||||
}
|
||||
|
||||
layer = (onion_layer_t *)(bufp + *lenp - 128); /* pointer to innermost layer */
|
||||
/* create the onion layer by layer, starting with the innermost */
|
||||
for (i=0;i<routelen;i++)
|
||||
{
|
||||
router = rarray[route[i]];
|
||||
|
||||
log(LOG_DEBUG,"create_onion() : %u",router);
|
||||
log(LOG_DEBUG,"create_onion() : This router is %s:%u",inet_ntoa(*((struct in_addr *)&router->addr)),ntohs(router->or_port));
|
||||
log(LOG_DEBUG,"create_onion() : Key pointer = %u.",router->pkey);
|
||||
log(LOG_DEBUG,"create_onion() : Key size = %u.",RSA_size(router->pkey));
|
||||
|
||||
/* 0 bit */
|
||||
layer->zero = 0;
|
||||
/* version */
|
||||
layer->version = VERSION;
|
||||
/* Back F + Forw F both use DES OFB*/
|
||||
layer->backf = ONION_DEFAULT_CIPHER;
|
||||
layer->forwf = ONION_DEFAULT_CIPHER;
|
||||
/* Dest Port */
|
||||
if (i) /* not last hop */
|
||||
layer->port = rarray[route[i-1]]->or_port;
|
||||
else
|
||||
layer->port = 0;
|
||||
/* Dest Addr */
|
||||
if (i) /* not last hop */
|
||||
layer->addr = rarray[route[i-1]]->addr;
|
||||
else
|
||||
layer->addr = 0;
|
||||
/* Expiration Time */
|
||||
layer->expire = time(NULL) + 3600; /* NOW + 1 hour */
|
||||
/* Key Seed Material */
|
||||
retval = RAND_bytes(layer->keyseed,16);
|
||||
if (retval < 1) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error generating random data.");
|
||||
free((void *)bufp);
|
||||
if (cpathp)
|
||||
{
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"create_onion() : Onion layer %u built : %u, %u, %u, %s, %u.",i+1,layer->zero,layer->backf,layer->forwf,inet_ntoa(*((struct in_addr *)&layer->addr)),ntohs(layer->port));
|
||||
|
||||
/* build up the crypt_path */
|
||||
if (cpathp)
|
||||
{
|
||||
cpathp[i] = (crypt_path_t *)malloc(sizeof(crypt_path_t));
|
||||
if (!cpathp[i])
|
||||
{
|
||||
log(LOG_ERR,"Error allocating memory.");
|
||||
free((void *)bufp);
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1);
|
||||
hop = cpathp[i];
|
||||
/* set crypto functions */
|
||||
hop->backf = layer->backf;
|
||||
hop->forwf = layer->forwf;
|
||||
|
||||
/* calculate keys */
|
||||
SHA1(layer->keyseed,16,hop->digest3);
|
||||
log(LOG_DEBUG,"create_onion() : First SHA pass performed.");
|
||||
SHA1(hop->digest3,20,hop->digest2);
|
||||
log(LOG_DEBUG,"create_onion() : Second SHA pass performed.");
|
||||
SHA1(hop->digest2,20,hop->digest3);
|
||||
log(LOG_DEBUG,"create_onion() : Third SHA pass performed.");
|
||||
log(LOG_DEBUG,"create_onion() : Keys generated.");
|
||||
/* set IVs */
|
||||
memset((void *)hop->f_iv,0,16);
|
||||
memset((void *)hop->b_iv,0,16);
|
||||
|
||||
/* initialize cipher contexts */
|
||||
EVP_CIPHER_CTX_init(&hop->f_ctx);
|
||||
EVP_CIPHER_CTX_init(&hop->b_ctx);
|
||||
|
||||
/* initialize cipher engines */
|
||||
switch(layer->forwf)
|
||||
{
|
||||
case ONION_CIPHER_DES :
|
||||
retval = EVP_EncryptInit(&hop->f_ctx, EVP_des_ofb(), hop->digest3, hop->f_iv);
|
||||
break;
|
||||
case ONION_CIPHER_RC4 :
|
||||
retval = EVP_EncryptInit(&hop->f_ctx, EVP_rc4(), hop->digest3, hop->f_iv);
|
||||
break;
|
||||
case ONION_CIPHER_IDENTITY :
|
||||
retval = EVP_EncryptInit(&hop->f_ctx, EVP_enc_null(), hop->digest3, hop->f_iv);
|
||||
break;
|
||||
}
|
||||
if (!retval) /* cipher initialization failed */
|
||||
{
|
||||
log(LOG_ERR,"Could not initialize crypto engines.");
|
||||
free((void *)bufp);
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
return NULL;
|
||||
}
|
||||
switch(layer->backf)
|
||||
{
|
||||
case ONION_CIPHER_DES :
|
||||
retval = EVP_DecryptInit(&hop->b_ctx, EVP_des_ofb(), hop->digest2, hop->b_iv);
|
||||
break;
|
||||
case ONION_CIPHER_RC4 :
|
||||
retval = EVP_DecryptInit(&hop->b_ctx, EVP_rc4(), hop->digest2, hop->b_iv);
|
||||
break;
|
||||
case ONION_CIPHER_IDENTITY :
|
||||
retval = EVP_DecryptInit(&hop->b_ctx, EVP_enc_null(), hop->digest2, hop->b_iv);
|
||||
break;
|
||||
}
|
||||
if (!retval) /* cipher initialization failed */
|
||||
{
|
||||
log(LOG_ERR,"Could not initialize crypto engines.");
|
||||
free((void *)bufp);
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop.");
|
||||
}
|
||||
|
||||
/* padding if this is the innermost layer */
|
||||
if (!i)
|
||||
{
|
||||
retval=RAND_pseudo_bytes((unsigned char *)layer + 28,100);
|
||||
if (retval == -1) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error generating pseudo-random data.");
|
||||
free((void *)bufp);
|
||||
if (cpathp)
|
||||
{
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding.");
|
||||
}
|
||||
|
||||
/* encrypt */
|
||||
retbuf = encrypt_onion(layer,128+(i*28),router->pkey);
|
||||
if (!retbuf)
|
||||
{
|
||||
log(LOG_ERR,"Error encrypting onion layer.");
|
||||
free((void *)bufp);
|
||||
if (cpathp)
|
||||
{
|
||||
for (j=0;j<i;j++)
|
||||
free((void *)cpathp[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"create_onion() : Encrypted layer.");
|
||||
|
||||
/* calculate pointer to next layer */
|
||||
layer = (onion_layer_t *)bufp + (routelen-i-2)*sizeof(onion_layer_t);
|
||||
}
|
||||
|
||||
return bufp;
|
||||
} /* valid parameters */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* encrypts 128 bytes of the onion with the specified public key, the rest with
|
||||
* DES OFB with the key as defined in the outter layer */
|
||||
unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey)
|
||||
{
|
||||
unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */
|
||||
unsigned char digest[20]; /* stores SHA1 output - 160 bits */
|
||||
unsigned char *retbuf = NULL;
|
||||
unsigned char iv[8];
|
||||
int retval = 0;
|
||||
int outlen = 0;
|
||||
|
||||
EVP_CIPHER_CTX ctx; /* cipher context */
|
||||
|
||||
if ( (onion) && (pkey) ) /* valid parameters */
|
||||
{
|
||||
memset((void *)iv,0,8);
|
||||
|
||||
log(LOG_DEBUG,"Onion layer : %u, %u, %u, %s, %u.",onion->zero,onion->backf,onion->forwf,inet_ntoa(*((struct in_addr *)&onion->addr)),ntohs(onion->port));
|
||||
/* allocate space for tmpbuf */
|
||||
tmpbuf = (unsigned char *)malloc(onionlen);
|
||||
if (!tmpbuf)
|
||||
{
|
||||
log(LOG_ERR,"Could not allocate memory.");
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf);
|
||||
|
||||
/* get key1 = SHA1(KeySeed) */
|
||||
retbuf = SHA1(((onion_layer_t *)onion)->keyseed,16,digest);
|
||||
if (!retbuf)
|
||||
{
|
||||
log(LOG_ERR,"Error computing SHA1 digest.");
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"encrypt_onion() : Computed DES key.");
|
||||
|
||||
log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt.");
|
||||
/* encrypt 128 bytes with RSA *pkey */
|
||||
retval = RSA_public_encrypt(128, (unsigned char *)onion, tmpbuf, pkey, RSA_NO_PADDING);
|
||||
if (retval == -1)
|
||||
{
|
||||
log(LOG_ERR,"Error RSA-encrypting data :%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion.");
|
||||
|
||||
/* now encrypt the rest with DES OFB */
|
||||
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
retval = EVP_EncryptInit(&ctx,EVP_des_ofb(),digest,iv);
|
||||
if (!retval) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retval = EVP_EncryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128);
|
||||
if (!retval) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error performing DES encryption:%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"encrypt_onion() : DES OFB encrypted the rest of the onion.");
|
||||
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
|
||||
/* now copy tmpbuf to onion */
|
||||
memcpy((void *)onion,(void *)tmpbuf,onionlen);
|
||||
log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer.");
|
||||
free((void *)tmpbuf);
|
||||
return (unsigned char *)onion;
|
||||
} /* valid parameters */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */
|
||||
unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey)
|
||||
{
|
||||
void *tmpbuf = NULL; /* temporary buffer for crypto operations */
|
||||
unsigned char digest[20]; /* stores SHA1 output - 160 bits */
|
||||
unsigned char *retbuf = NULL;
|
||||
unsigned char iv[8];
|
||||
int retval = 0;
|
||||
int outlen = 0;
|
||||
|
||||
EVP_CIPHER_CTX ctx; /* cipher context */
|
||||
|
||||
if ( (onion) && (prkey) ) /* valid parameters */
|
||||
{
|
||||
memset((void *)iv,0,8);
|
||||
|
||||
/* allocate space for tmpbuf */
|
||||
tmpbuf = malloc(onionlen);
|
||||
if (!tmpbuf)
|
||||
{
|
||||
log(LOG_ERR,"Could not allocate memory.");
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer.");
|
||||
|
||||
/* decrypt 128 bytes with RSA *prkey */
|
||||
retval = RSA_private_decrypt(128, (unsigned char*)onion, (unsigned char *)tmpbuf, prkey, RSA_NO_PADDING);
|
||||
if (retval == -1)
|
||||
{
|
||||
log(LOG_ERR,"Error RSA-decrypting data :%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete.");
|
||||
|
||||
/* get key1 = SHA1(KeySeed) */
|
||||
retbuf = SHA1(((onion_layer_t *)tmpbuf)->keyseed,16,digest);
|
||||
if (!retbuf)
|
||||
{
|
||||
log(LOG_ERR,"Error computing SHA1 digest.");
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
log(LOG_DEBUG,"decrypt_onion() : Computed DES key.");
|
||||
|
||||
/* now decrypt the rest with DES OFB */
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
retval = EVP_DecryptInit(&ctx,EVP_des_ofb(),digest,iv);
|
||||
if (!retval) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
retval = EVP_DecryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128);
|
||||
if (!retval) /* error */
|
||||
{
|
||||
log(LOG_ERR,"Error performing DES decryption:%s",ERR_reason_error_string(ERR_get_error()));
|
||||
free((void *)tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
log(LOG_DEBUG,"decrypt_onion() : DES decryption complete.");
|
||||
|
||||
/* now copy tmpbuf to onion */
|
||||
memcpy((void *)onion,(void *)tmpbuf,onionlen);
|
||||
free((void *)tmpbuf);
|
||||
return (unsigned char *)onion;
|
||||
} /* valid parameters */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* delete first n bytes of the onion and pads the end with n bytes of random data */
|
||||
void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n)
|
||||
{
|
||||
if (onion) /* valid parameter */
|
||||
{
|
||||
memmove((void *)onion,(void *)(onion+n),onionlen-n);
|
||||
RAND_pseudo_bytes(onion+onionlen-n,n);
|
||||
}
|
||||
}
|
||||
|
||||
/* create a new tracked_onion entry */
|
||||
tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion)
|
||||
{
|
||||
tracked_onion_t *to = NULL;
|
||||
|
||||
if (!onion || !tracked_onions || !last_tracked_onion) /* invalid parameters */
|
||||
return NULL;
|
||||
|
||||
to = (tracked_onion_t *)malloc(sizeof(tracked_onion_t));
|
||||
if (!to)
|
||||
return NULL;
|
||||
|
||||
to->expire = ((onion_layer_t *)onion)->expire; /* set the expiration date */
|
||||
/* compute the SHA digest */
|
||||
SHA1(onion, onionlen, to->digest);
|
||||
if (!to->digest)
|
||||
{
|
||||
log(LOG_DEBUG,"new_tracked_onion() : Failed to compute a SHA1 digest of the onion.");
|
||||
free((void *)to);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
to->next = NULL;
|
||||
|
||||
if (!*tracked_onions)
|
||||
{
|
||||
to->prev = NULL;
|
||||
*tracked_onions = to;
|
||||
}
|
||||
else
|
||||
{
|
||||
to->prev = (void *)*last_tracked_onion;
|
||||
(*last_tracked_onion)->next = (void *)to;
|
||||
}
|
||||
*last_tracked_onion = to;
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
/* delete a tracked onion entry */
|
||||
void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion)
|
||||
{
|
||||
if (!*tracked_onions || !*last_tracked_onion || !to)
|
||||
return;
|
||||
|
||||
if (to->prev)
|
||||
((tracked_onion_t *)to->prev)->next = to->next;
|
||||
if (to->next)
|
||||
((tracked_onion_t *)to->next)->prev = to->prev;
|
||||
|
||||
if (to == *tracked_onions)
|
||||
*tracked_onions = (tracked_onion_t *)to->next;
|
||||
|
||||
if (to == *last_tracked_onion)
|
||||
*last_tracked_onion = (tracked_onion_t *)to->prev;
|
||||
|
||||
free((void *)to);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* find a tracked onion in the linked list of tracked onions */
|
||||
tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions)
|
||||
{
|
||||
tracked_onion_t *to = tracked_onions;
|
||||
unsigned char digest[20];
|
||||
|
||||
/* compute the SHA digest of the onion */
|
||||
SHA1(onion,onionlen, digest);
|
||||
|
||||
while(to)
|
||||
{
|
||||
if (!memcmp((void *)digest, (void *)to->digest, 20))
|
||||
return to;
|
||||
to = (tracked_onion_t *)to->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
193
src/or/or.h
193
src/or/or.h
@ -16,8 +16,6 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
@ -34,16 +32,24 @@
|
||||
#include "../common/config.h"
|
||||
#include "../common/key.h"
|
||||
#include "../common/log.h"
|
||||
#include "../common/onion.h"
|
||||
#include "../common/ss.h"
|
||||
#include "../common/version.h"
|
||||
|
||||
#define MAXCONNECTIONS 200 /* upper bound on max connections.
|
||||
can be overridden by config file */
|
||||
|
||||
#define MAX_BUF_SIZE (64*1024)
|
||||
#define DEFAULT_BANDWIDTH_OP 1
|
||||
|
||||
#define ACI_TYPE_LOWER 0
|
||||
#define ACI_TYPE_HIGHER 1
|
||||
#define ACI_TYPE_BOTH 2
|
||||
|
||||
/* bitvector of the roles that we might want to play. You can or (|) them together */
|
||||
#define ROLE_OR_LISTEN 1
|
||||
#define ROLE_OR_CONNECT_ALL 2
|
||||
#define ROLE_OP_LISTEN 4
|
||||
#define ROLE_AP_LISTEN 8
|
||||
|
||||
#define CONN_TYPE_OP_LISTENER 1
|
||||
#define CONN_TYPE_OP 2
|
||||
@ -66,9 +72,9 @@
|
||||
* foo_CONN_STATE_bar_baz:
|
||||
* "I am acting as a bar, currently in stage baz of talking with a foo."
|
||||
*/
|
||||
#define OR_CONN_STATE_OP_CONNECTING 0
|
||||
#define OR_CONN_STATE_OP_CONNECTING 0 /* an application proxy wants me to connect to this OR */
|
||||
#define OR_CONN_STATE_OP_SENDING_KEYS 1
|
||||
#define OR_CONN_STATE_CLIENT_CONNECTING 2
|
||||
#define OR_CONN_STATE_CLIENT_CONNECTING 2 /* I'm connecting to this OR as an OR */
|
||||
#define OR_CONN_STATE_CLIENT_SENDING_AUTH 3 /* sending address and info */
|
||||
#define OR_CONN_STATE_CLIENT_AUTH_WAIT 4 /* have sent address and info, waiting */
|
||||
#define OR_CONN_STATE_CLIENT_SENDING_NONCE 5 /* sending nonce, last piece of handshake */
|
||||
@ -90,12 +96,21 @@
|
||||
#define AP_CONN_STATE_OPEN 2
|
||||
|
||||
#define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */
|
||||
#define CIRCUIT_STATE_OPEN 1 /* onion processed, ready to send data along the connection */
|
||||
#define CIRCUIT_STATE_CLOSE_WAIT1 2 /* sent two "destroy" signals, waiting for acks */
|
||||
#define CIRCUIT_STATE_CLOSE_WAIT2 3 /* received one ack, waiting for one more
|
||||
#define CIRCUIT_STATE_OR_WAIT 1 /* I'm at the beginning of the path, my firsthop is still connecting */
|
||||
#define CIRCUIT_STATE_OPEN 2 /* onion processed, ready to send data along the connection */
|
||||
#define CIRCUIT_STATE_CLOSE_WAIT1 3 /* sent two "destroy" signals, waiting for acks */
|
||||
#define CIRCUIT_STATE_CLOSE_WAIT2 4 /* received one ack, waiting for one more
|
||||
(or if just one was sent, waiting for that one */
|
||||
//#define CIRCUIT_STATE_CLOSE 4 /* both acks received, connection is dead */ /* NOT USED */
|
||||
|
||||
/* available cipher functions */
|
||||
#define ONION_CIPHER_IDENTITY 0
|
||||
#define ONION_CIPHER_DES 1
|
||||
#define ONION_CIPHER_RC4 2
|
||||
|
||||
/* default cipher function */
|
||||
#define ONION_DEFAULT_CIPHER ONION_CIPHER_DES
|
||||
|
||||
typedef uint16_t aci_t;
|
||||
|
||||
typedef struct
|
||||
@ -118,9 +133,7 @@ typedef struct
|
||||
size_t outbuflen;
|
||||
size_t outbuf_datalen;
|
||||
|
||||
/* used by OP and App: */
|
||||
|
||||
uint16_t aci; /* anonymous connection identifier */
|
||||
// uint16_t aci; /* anonymous connection identifier */
|
||||
|
||||
/* used by OR and OP: */
|
||||
|
||||
@ -142,11 +155,15 @@ typedef struct
|
||||
uint32_t addr; /* these two uniquely identify a router */
|
||||
uint16_t port;
|
||||
|
||||
/* used by exit: */
|
||||
/* used by exit and ap: */
|
||||
|
||||
ss_t ss; /* standard structure */
|
||||
int ss_received; /* size of ss, received so far */
|
||||
|
||||
char *dest_addr, *dest_port;
|
||||
uint16_t dest_addr_len, dest_port_len;
|
||||
uint16_t dest_addr_received, dest_port_received;
|
||||
|
||||
/* used by OR, to keep state while connect()ing: Kludge. */
|
||||
|
||||
RSA *prkey;
|
||||
@ -156,7 +173,7 @@ typedef struct
|
||||
uint32_t min;
|
||||
uint32_t max;
|
||||
|
||||
char *address; /* strdup into this, gets free_connection frees it */
|
||||
char *address; /* strdup into this, because free_connection frees it */
|
||||
RSA *pkey; /* public RSA key for the other side */
|
||||
|
||||
char nonce[8];
|
||||
@ -169,7 +186,9 @@ typedef struct
|
||||
char *address;
|
||||
|
||||
uint32_t addr;
|
||||
uint16_t port;
|
||||
uint16_t or_port;
|
||||
uint16_t op_port;
|
||||
uint16_t ap_port;
|
||||
|
||||
RSA *pkey; /* public RSA key */
|
||||
|
||||
@ -187,6 +206,23 @@ typedef struct
|
||||
void *next;
|
||||
} routerinfo_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int forwf;
|
||||
unsigned int backf;
|
||||
char digest2[20]; /* second SHA output for onion_layer_t.keyseed */
|
||||
char digest3[20]; /* third SHA output for onion_layer_t.keyseed */
|
||||
|
||||
/* IVs */
|
||||
char f_iv[16];
|
||||
char b_iv[16];
|
||||
|
||||
/* cipher contexts */
|
||||
EVP_CIPHER_CTX f_ctx;
|
||||
EVP_CIPHER_CTX b_ctx;
|
||||
|
||||
} crypt_path_t;
|
||||
|
||||
/* per-anonymous-connection struct */
|
||||
typedef struct
|
||||
{
|
||||
@ -205,8 +241,8 @@ typedef struct
|
||||
unsigned char p_f; /* crypto functions */
|
||||
unsigned char n_f;
|
||||
|
||||
unsigned char p_key[128]; /* crypto keys */
|
||||
unsigned char n_key[128];
|
||||
unsigned char p_key[16]; /* crypto keys */
|
||||
unsigned char n_key[16];
|
||||
|
||||
unsigned char p_iv[16]; /* initialization vectors */
|
||||
unsigned char n_iv[16];
|
||||
@ -214,6 +250,9 @@ typedef struct
|
||||
EVP_CIPHER_CTX p_ctx; /* cipher context */
|
||||
EVP_CIPHER_CTX n_ctx;
|
||||
|
||||
crypt_path_t **cpath;
|
||||
size_t cpathlen;
|
||||
|
||||
uint32_t expire; /* expiration time for the corresponding onion */
|
||||
|
||||
int state;
|
||||
@ -225,6 +264,26 @@ typedef struct
|
||||
void *next;
|
||||
} circuit_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int zero:1;
|
||||
int version:7;
|
||||
int backf:4;
|
||||
int forwf:4;
|
||||
uint16_t port;
|
||||
uint32_t addr;
|
||||
time_t expire;
|
||||
unsigned char keyseed[16];
|
||||
} onion_layer_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
time_t expire;
|
||||
char digest[20]; /* SHA digest of the onion */
|
||||
void *prev;
|
||||
void *next;
|
||||
} tracked_onion_t;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -280,12 +339,14 @@ aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type);
|
||||
|
||||
circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn);
|
||||
circuit_t *circuit_get_by_conn(connection_t *conn);
|
||||
circuit_t *circuit_get_by_naddr_nport(uint32_t naddr, uint16_t nport);
|
||||
|
||||
int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type);
|
||||
int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type);
|
||||
|
||||
int circuit_init(circuit_t *circ, int aci_type);
|
||||
void circuit_free(circuit_t *circ);
|
||||
void circuit_free_cpath(crypt_path_t **cpath, size_t cpathlen);
|
||||
|
||||
void circuit_close(circuit_t *circ);
|
||||
|
||||
@ -316,8 +377,9 @@ int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type);
|
||||
int connection_handle_listener_read(connection_t *conn, int new_type, int new_state);
|
||||
|
||||
/* start all connections that should be up but aren't */
|
||||
int retry_all_connections(routerinfo_t **router_array, int rarray_len,
|
||||
int retry_all_connections(int role, routerinfo_t **router_array, int rarray_len,
|
||||
RSA *prkey, uint16_t or_port, uint16_t op_port, uint16_t ap_port);
|
||||
connection_t *connection_connect_to_router_as_op(routerinfo_t *router, RSA *prkey, uint16_t local_or_port);
|
||||
|
||||
int connection_read_to_buf(connection_t *conn);
|
||||
|
||||
@ -331,27 +393,43 @@ int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn);
|
||||
int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn);
|
||||
|
||||
int connection_process_inbuf(connection_t *conn);
|
||||
int connection_package_raw_inbuf(connection_t *conn);
|
||||
int connection_process_cell_from_inbuf(connection_t *conn);
|
||||
|
||||
int connection_finished_flushing(connection_t *conn);
|
||||
|
||||
/********************************* connection_or.c ***************************/
|
||||
/********************************* connection_ap.c ****************************/
|
||||
|
||||
int connection_or_process_inbuf(connection_t *conn);
|
||||
int connection_or_finished_flushing(connection_t *conn);
|
||||
int connection_ap_process_inbuf(connection_t *conn);
|
||||
|
||||
void conn_or_init_crypto(connection_t *conn);
|
||||
int ap_handshake_process_ss(connection_t *conn);
|
||||
|
||||
int or_handshake_client_process_auth(connection_t *conn);
|
||||
int or_handshake_client_send_auth(connection_t *conn);
|
||||
int ap_handshake_create_onion(connection_t *conn);
|
||||
|
||||
int or_handshake_server_process_auth(connection_t *conn);
|
||||
int or_handshake_server_process_nonce(connection_t *conn);
|
||||
int ap_handshake_establish_circuit(connection_t *conn, unsigned int *route, int routelen, char *onion,
|
||||
int onionlen, crypt_path_t **cpath);
|
||||
|
||||
int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local);
|
||||
/* find the circ that's waiting on me, if any, and get it to send its onion */
|
||||
int ap_handshake_n_conn_open(connection_t *or_conn);
|
||||
|
||||
int ap_handshake_send_onion(connection_t *ap_conn, connection_t *or_conn, circuit_t *circ);
|
||||
|
||||
int connection_ap_process_data_cell(cell_t *cell, connection_t *conn);
|
||||
|
||||
int connection_ap_finished_flushing(connection_t *conn);
|
||||
|
||||
int connection_ap_create_listener(RSA *prkey, struct sockaddr_in *local);
|
||||
|
||||
int connection_ap_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* connection_exit.c ***************************/
|
||||
|
||||
int connection_exit_process_inbuf(connection_t *conn);
|
||||
int connection_exit_package_inbuf(connection_t *conn);
|
||||
int connection_exit_process_data_cell(cell_t *cell, connection_t *conn);
|
||||
|
||||
int connection_exit_finished_flushing(connection_t *conn);
|
||||
|
||||
int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local);
|
||||
int connection_or_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* connection_op.c ***************************/
|
||||
|
||||
@ -365,13 +443,27 @@ int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local);
|
||||
|
||||
int connection_op_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* connection_exit.c ***************************/
|
||||
/********************************* connection_or.c ***************************/
|
||||
|
||||
int connection_exit_process_inbuf(connection_t *conn);
|
||||
int connection_exit_package_inbuf(connection_t *conn);
|
||||
int connection_exit_process_data_cell(cell_t *cell, connection_t *conn);
|
||||
int connection_or_process_inbuf(connection_t *conn);
|
||||
int connection_or_finished_flushing(connection_t *conn);
|
||||
|
||||
int connection_exit_finished_flushing(connection_t *conn);
|
||||
void conn_or_init_crypto(connection_t *conn);
|
||||
|
||||
int or_handshake_op_send_keys(connection_t *conn);
|
||||
int or_handshake_op_finished_sending_keys(connection_t *conn);
|
||||
|
||||
int or_handshake_client_process_auth(connection_t *conn);
|
||||
int or_handshake_client_send_auth(connection_t *conn);
|
||||
|
||||
int or_handshake_server_process_auth(connection_t *conn);
|
||||
int or_handshake_server_process_nonce(connection_t *conn);
|
||||
|
||||
connection_t *connect_to_router_as_or(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local);
|
||||
connection_t *connection_or_connect_as_or(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local);
|
||||
|
||||
int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local);
|
||||
int connection_or_handle_listener_read(connection_t *conn);
|
||||
|
||||
/********************************* main.c ***************************/
|
||||
|
||||
@ -384,6 +476,10 @@ connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
connection_t *connection_get_by_type(int type);
|
||||
|
||||
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port);
|
||||
unsigned int *router_new_route(size_t *rlen);
|
||||
unsigned char *router_create_onion(unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp);
|
||||
routerinfo_t *router_get_first_in_route(unsigned int *route, size_t routelen);
|
||||
connection_t *connect_to_router_as_op(routerinfo_t *router);
|
||||
|
||||
void connection_watch_events(connection_t *conn, short events);
|
||||
|
||||
@ -404,6 +500,37 @@ int decide_aci_type(uint32_t local_addr, uint16_t local_port,
|
||||
|
||||
int process_onion(circuit_t *circ, connection_t *conn);
|
||||
|
||||
/* uses a weighted coin with weight cw to choose a route length */
|
||||
int chooselen(double cw);
|
||||
|
||||
/* returns an array of pointers to routent that define a new route through the OR network
|
||||
* int cw is the coin weight to use when choosing the route
|
||||
* order of routers is from last to first
|
||||
*/
|
||||
unsigned int *new_route(double cw, routerinfo_t **rarray, size_t rarray_len, size_t *rlen);
|
||||
|
||||
/* creates a new onion from route, stores it and its length into bufp and lenp respectively */
|
||||
unsigned char *create_onion(routerinfo_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp);
|
||||
|
||||
/* encrypts 128 bytes of the onion with the specified public key, the rest with
|
||||
* DES OFB with the key as defined in the outter layer */
|
||||
unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey);
|
||||
|
||||
/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */
|
||||
unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey);
|
||||
|
||||
/* delete first n bytes of the onion and pads the end with n bytes of random data */
|
||||
void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n);
|
||||
|
||||
/* create a new tracked_onion entry */
|
||||
tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion);
|
||||
|
||||
/* delete a tracked onion entry */
|
||||
void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion);
|
||||
|
||||
/* find a tracked onion in the linked list of tracked onions */
|
||||
tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions);
|
||||
|
||||
/********************************* routers.c ***************************/
|
||||
|
||||
routerinfo_t **getrouters(char *routerfile, size_t *listlenp);
|
||||
|
@ -163,11 +163,14 @@ routerinfo_t **getrouters(char *routerfile, size_t *lenp)
|
||||
if (token)
|
||||
{
|
||||
log(LOG_DEBUG,"getrouters():Token :%s",token);
|
||||
router->port = (uint16_t)strtoul(token,&errtest,0);
|
||||
router->or_port = (uint16_t)strtoul(token,&errtest,0);
|
||||
if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */
|
||||
{
|
||||
/* FIXME patch from RD. We should make it actually read these. */
|
||||
router->op_port = htons(router->or_port + 10);
|
||||
router->ap_port = htons(router->or_port + 20);
|
||||
/* convert port to network format */
|
||||
router->port = htons(router->port);
|
||||
router->or_port = htons(router->or_port);
|
||||
|
||||
/* read min bandwidth */
|
||||
token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS);
|
||||
@ -204,7 +207,8 @@ routerinfo_t **getrouters(char *routerfile, size_t *lenp)
|
||||
retp=fgets(line,512,rf);
|
||||
if (!retp)
|
||||
{
|
||||
log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
|
||||
log(LOG_ERR,"Could not find a public key entry for router %s:%u.",
|
||||
router->address,router->or_port);
|
||||
free((void *)router->address);
|
||||
free((void *)router);
|
||||
fclose(rf);
|
||||
@ -233,7 +237,8 @@ routerinfo_t **getrouters(char *routerfile, size_t *lenp)
|
||||
}
|
||||
else /* we found something else; this isn't right */
|
||||
{
|
||||
log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port);
|
||||
log(LOG_ERR,"Could not find a public key entry for router %s:%u.",
|
||||
router->address,router->or_port);
|
||||
free((void *)router->address);
|
||||
free((void *)router);
|
||||
fclose(rf);
|
||||
@ -247,7 +252,8 @@ routerinfo_t **getrouters(char *routerfile, size_t *lenp)
|
||||
router->pkey = PEM_read_RSAPublicKey(rf,&router->pkey,NULL,NULL);
|
||||
if (!router->pkey) /* something went wrong */
|
||||
{
|
||||
log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port);
|
||||
log(LOG_ERR,"Could not read public key for router %s:%u.",
|
||||
router->address,router->or_port);
|
||||
free((void *)router->address);
|
||||
free((void *)router);
|
||||
fclose(rf);
|
||||
@ -259,7 +265,7 @@ routerinfo_t **getrouters(char *routerfile, size_t *lenp)
|
||||
log(LOG_DEBUG,"getrouters():Public key size = %u.", RSA_size(router->pkey));
|
||||
if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */
|
||||
{
|
||||
log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port);
|
||||
log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->or_port);
|
||||
free((void *)router->address);
|
||||
RSA_free(router->pkey);
|
||||
free((void *)router);
|
||||
|
Loading…
Reference in New Issue
Block a user