diff options
author | Roger Dingledine <arma@torproject.org> | 2002-07-02 09:36:58 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2002-07-02 09:36:58 +0000 |
commit | d982925593dbb724e4c8ebeb4f945c2184f11831 (patch) | |
tree | c3f22938ad261f3c45ed2744ae0950388c1c04e8 /src/or | |
parent | b34fad4d38ac9c45eb2112fa9dde26e499ccdcc5 (diff) | |
download | tor-d982925593dbb724e4c8ebeb4f945c2184f11831.tar tor-d982925593dbb724e4c8ebeb4f945c2184f11831.tar.gz |
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
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/Makefile.am | 2 | ||||
-rw-r--r-- | src/or/circuit.c | 123 | ||||
-rw-r--r-- | src/or/command.c | 22 | ||||
-rw-r--r-- | src/or/connection.c | 140 | ||||
-rw-r--r-- | src/or/connection_ap.c | 381 | ||||
-rw-r--r-- | src/or/connection_exit.c | 43 | ||||
-rw-r--r-- | src/or/connection_or.c | 244 | ||||
-rw-r--r-- | src/or/main.c | 80 | ||||
-rw-r--r-- | src/or/onion.c | 528 | ||||
-rw-r--r-- | src/or/or.h | 193 | ||||
-rw-r--r-- | src/or/routers.c | 18 |
11 files changed, 1561 insertions, 213 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index c401ef79a..d0e78a552 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -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 diff --git a/src/or/circuit.c b/src/or/circuit.c index 39ca49c86..5f323dd50 100644 --- a/src/or/circuit.c +++ b/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); } diff --git a/src/or/command.c b/src/or/command.c index fddf5cfea..621809677 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -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; + } } } } diff --git a/src/or/connection.c b/src/or/connection.c index 643b1ebb8..b44d72c68 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -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: diff --git a/src/or/connection_ap.c b/src/or/connection_ap.c new file mode 100644 index 000000000..cd57d90ed --- /dev/null +++ b/src/or/connection_ap.c @@ -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); +} + diff --git a/src/or/connection_exit.c b/src/or/connection_exit.c index 99a6760bc..eec917a8e 100644 --- a/src/or/connection_exit.c +++ b/src/or/connection_exit.c @@ -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); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 37abc8b7a..5e9cae84d 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -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 NULL; + } + + log(LOG_DEBUG,"connection_or_connect() : Connection to router %s:%u established.",router->address,ntohs(port)); + + *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."); - log(LOG_DEBUG,"connect_to_router() : Connection to router %s:%u established.",router->address,ntohs(router->port)); + /* send message */ - /* move to the next step in the handshake */ - if(or_handshake_client_send_auth(conn) < 0) { - connection_remove(conn); - connection_free(conn); + if(connection_write_to_buf(cipher, 128, conn) < 0) { + log(LOG_DEBUG,"or_handshake_op_send_keys(): my outbuf is full. Oops."); return -1; } - return 0; + 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); diff --git a/src/or/main.c b/src/or/main.c index 2cac0819c..543b1ea3f 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -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,35 +347,32 @@ int main(int argc, char *argv[]) { log(LOG_ERR,"PrivateKeyFile option required but not found."); exit(1); } - else if (options[EntryPort].err != 1) - { - log(LOG_ERR,"EntryPort option required but not found."); + else if (options[CoinWeight].err != 1) + { + 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[MaxConn].err != 1) + else if (options[OPPort].err != 1) { - log(LOG_ERR,"MaxConn option required but not found."); + log(LOG_ERR,"OPPort option required but not found."); exit(1); } -#if 0 - else if (options[MaxConnTimeout].err != 1) + else if (options[ORPort].err != 1) { - conn_tout.tv_sec = OR_DEFAULT_CONN_TIMEOUT; + log(LOG_ERR,"ORPort option required but not found."); + exit(1); } - else + else if (options[MaxConn].err != 1) { - if (!options[MaxConnTimeout].r.i) - conn_toutp = NULL; - else - conn_tout.tv_sec = options[MaxConnTimeout].r.i; + log(LOG_ERR,"MaxConn option required but not found."); + exit(1); } - conn_tout.tv_usec = 0; - +#if 0 if (!options[TrafficShaping].err) { options[TrafficShaping].r.i = DEFAULT_POLICY; diff --git a/src/or/onion.c b/src/or/onion.c index d9c8a9c5d..d659272f2 100644 --- a/src/or/onion.c +++ b/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; +} + diff --git a/src/or/or.h b/src/or/or.h index 6ab44f29f..0f4647fb2 100644 --- a/src/or/or.h +++ b/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,10 +155,14 @@ 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. */ @@ -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); diff --git a/src/or/routers.c b/src/or/routers.c index d24d4d1bd..9a673aeef 100644 --- a/src/or/routers.c +++ b/src/or/routers.c @@ -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); |