diff options
Diffstat (limited to 'src/or/circuit.c')
-rw-r--r-- | src/or/circuit.c | 154 |
1 files changed, 105 insertions, 49 deletions
diff --git a/src/or/circuit.c b/src/or/circuit.c index e2d1c0d24..81b117dc9 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -281,7 +281,8 @@ circuit_t *circuit_get_newest_ap(void) { return bestcirc; } -int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edge_type) { +int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, + char edge_type, crypt_path_t *layer_hint) { int cell_direction; static int numsent_ap=0, numsent_exit=0; @@ -293,7 +294,7 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): now sent %d relay cells from ap", numsent_ap); if(circ->p_receive_circwindow <= 0) { log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): pwindow 0, queueing for later."); - circ->relay_queue = relay_queue_add(circ->relay_queue, cell); + circ->relay_queue = relay_queue_add(circ->relay_queue, cell, layer_hint); return 0; } circ->p_receive_circwindow--; @@ -304,13 +305,13 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): now sent %d relay cells from exit", numsent_exit); if(circ->n_receive_circwindow <= 0) { log(LOG_DEBUG,"circuit_deliver_relay_cell_from_edge(): nwindow 0, queueing for later."); - circ->relay_queue = relay_queue_add(circ->relay_queue, cell); + circ->relay_queue = relay_queue_add(circ->relay_queue, cell, layer_hint); return 0; } circ->n_receive_circwindow--; } - if(circuit_deliver_relay_cell(cell, circ, cell_direction) < 0) { + if(circuit_deliver_relay_cell(cell, circ, cell_direction, layer_hint) < 0) { return -1; } @@ -318,113 +319,168 @@ int circuit_deliver_relay_cell_from_edge(cell_t *cell, circuit_t *circ, char edg return 0; } -int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction) { - connection_t *conn; +int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ, + int cell_direction, crypt_path_t *layer_hint) { + connection_t *conn=NULL; + char recognized=0; + char buf[256]; assert(cell && circ); assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN); - if(cell_direction == CELL_DIRECTION_OUT) - conn = circ->n_conn; - else - conn = circ->p_conn; - /* first crypt cell->length */ - if(circuit_crypt(circ, &(cell->length), 1, cell_direction) < 0) { - log(LOG_DEBUG,"circuit_deliver_relay_cell(): length crypt failed. Dropping connection."); - return -1; - } + buf[0] = cell->length; + memcpy(buf+1, cell->payload, CELL_PAYLOAD_SIZE); - /* then crypt the payload */ - if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, cell_direction) < 0) { - log(LOG_DEBUG,"circuit_deliver_relay_cell(): payload crypt failed. Dropping connection."); + log(LOG_DEBUG,"circuit_deliver_relay_cell(): streamid %d before crypt.", *(int*)(cell->payload+1)); + + if(relay_crypt(circ, buf, 1+CELL_PAYLOAD_SIZE, cell_direction, layer_hint, &recognized, &conn) < 0) { + log(LOG_DEBUG,"circuit_deliver_relay_cell(): relay crypt failed. Dropping connection."); return -1; } - if(cell_direction == CELL_DIRECTION_OUT && (!conn || conn->type == CONN_TYPE_EXIT)) { - log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to exit."); - return connection_edge_process_relay_cell(cell, circ, EDGE_EXIT); + cell->length = buf[0]; + memcpy(cell->payload, buf+1, CELL_PAYLOAD_SIZE); + + if(recognized) { + if(cell_direction == CELL_DIRECTION_OUT) { + log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to exit."); + return connection_edge_process_relay_cell(cell, circ, conn, EDGE_EXIT); + } + if(cell_direction == CELL_DIRECTION_IN) { + log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to AP."); + return connection_edge_process_relay_cell(cell, circ, conn, EDGE_AP); + } } - if(cell_direction == CELL_DIRECTION_IN && (!conn || conn->type == CONN_TYPE_AP)) { - log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to AP."); - return connection_edge_process_relay_cell(cell, circ, EDGE_AP); + + /* not recognized. pass it on. */ + if(cell_direction == CELL_DIRECTION_OUT) + conn = circ->n_conn; + else + conn = circ->p_conn; + + if(!conn || !connection_speaks_cells(conn)) { + log(LOG_INFO,"circuit_deliver_relay_cell(): Didn't recognize cell (%d), but circ stops here! Dropping.", *(int *)(cell->payload+1)); + return 0; } - /* else send it as a cell */ - assert(conn); - //log(LOG_DEBUG,"circuit_deliver_relay_cell(): Sending to connection."); + return connection_write_cell_to_buf(cell, conn); } -int circuit_crypt(circuit_t *circ, char *in, int inlen, char cell_direction) { - char *out; +int relay_crypt(circuit_t *circ, char *in, int inlen, char cell_direction, + crypt_path_t *layer_hint, char *recognized, connection_t **conn) { crypt_path_t *thishop; + char out[256]; - assert(circ && in); + assert(circ && in && recognized && conn); - out = (char *)malloc(inlen); - if(!out) - return -1; + assert(inlen < 256); if(cell_direction == CELL_DIRECTION_IN) { if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */ thishop = circ->cpath; - /* Remember: cpath is in forward order, that is, first hop first. */ - do { + do { /* Remember: cpath is in forward order, that is, first hop first. */ assert(thishop); + /* decrypt */ if(crypto_cipher_decrypt(thishop->b_crypto, in, inlen, out)) { log(LOG_ERR,"Error performing decryption:%s",crypto_perror()); - free(out); return -1; } - - /* copy ciphertext back to buf */ memcpy(in,out,inlen); + + if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn))) + return 0; + thishop = thishop->next; } while(thishop != circ->cpath); + log(LOG_INFO,"relay_crypt(): in-cell at OP not recognized. Killing circuit."); + return -1; } else { /* we're in the middle. Just one crypt. */ - if(crypto_cipher_encrypt(circ->p_crypto,in, inlen, out)) { + + if(crypto_cipher_encrypt(circ->p_crypto, in, inlen, out)) { log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).", circ->p_aci, crypto_perror()); - free(out); return -1; } memcpy(in,out,inlen); + + /* don't check for recognized. only the OP can recognize a stream on the way back. */ + } } else if(cell_direction == CELL_DIRECTION_OUT) { if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */ - thishop = circ->cpath->prev; + + thishop = layer_hint; /* we already know which layer, from when we package_raw_inbuf'ed */ /* moving from last to first hop */ do { assert(thishop); + /* encrypt */ - if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, (unsigned char *)out)) { + if(crypto_cipher_encrypt(thishop->f_crypto, in, inlen, out)) { log(LOG_ERR,"Error performing encryption:%s",crypto_perror()); - free(out); return -1; } - - /* copy ciphertext back to buf */ memcpy(in,out,inlen); + thishop = thishop->prev; } while(thishop != circ->cpath->prev); } else { /* we're in the middle. Just one crypt. */ + if(crypto_cipher_decrypt(circ->n_crypto,in, inlen, out)) { log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).", circ->n_aci, crypto_perror()); - free(out); return -1; } memcpy(in,out,inlen); + + if( (*recognized = relay_check_recognized(circ, cell_direction, in+2, conn))) + return 0; + } } else { log(LOG_ERR,"circuit_crypt(): unknown cell direction %d.", cell_direction); assert(0); } - free(out); return 0; } +int relay_check_recognized(circuit_t *circ, int cell_direction, char *stream, connection_t **conn) { +/* FIXME can optimize by passing thishop in */ + connection_t *tmpconn; + + log(LOG_DEBUG,"relay_check_recognized(): entering"); + if(!memcmp(stream,ZERO_STREAM,STREAM_ID_SIZE)) + return 1; /* the zero stream is always recognized */ + + if(cell_direction == CELL_DIRECTION_OUT) + tmpconn = circ->n_conn; + else + tmpconn = circ->p_conn; + + log(LOG_DEBUG,"relay_check_recognized(): not the zero stream."); + if(!tmpconn) + return 0; /* no conns? don't recognize it */ + + while(tmpconn && tmpconn->type == CONN_TYPE_OR) { + log(LOG_DEBUG,"relay_check_recognized(): skipping over an OR conn"); + tmpconn = tmpconn->next_stream; + } + + for( ; tmpconn; tmpconn=tmpconn->next_stream) { + if(!memcmp(stream,tmpconn->stream_id, STREAM_ID_SIZE)) { + log(LOG_DEBUG,"relay_check_recognized(): recognized stream %d.", *(int*)stream); + *conn = tmpconn; + return 1; + } + log(LOG_DEBUG,"relay_check_recognized(): considered stream %d, not it.",*(int*)tmpconn->stream_id); + } + + log(LOG_DEBUG,"relay_check_recognized(): Didn't recognize. Giving up."); + return 0; + +} + void circuit_resume_edge_reading(circuit_t *circ, int edge_type) { connection_t *conn; struct relay_queue_t *tmpd; @@ -438,7 +494,7 @@ void circuit_resume_edge_reading(circuit_t *circ, int edge_type) { circ->n_receive_circwindow--; assert(circ->n_receive_circwindow >= 0); - if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_IN) < 0) { + if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_IN, circ->relay_queue->layer_hint) < 0) { circuit_close(circ); return; } @@ -446,7 +502,7 @@ void circuit_resume_edge_reading(circuit_t *circ, int edge_type) { circ->p_receive_circwindow--; assert(circ->p_receive_circwindow >= 0); - if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_OUT) < 0) { + if(circuit_deliver_relay_cell(circ->relay_queue->cell, circ, CELL_DIRECTION_OUT, circ->relay_queue->layer_hint) < 0) { circuit_close(circ); return; } |