diff options
Diffstat (limited to 'src/or/circuit.c')
-rw-r--r-- | src/or/circuit.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/src/or/circuit.c b/src/or/circuit.c new file mode 100644 index 000000000..b910fd351 --- /dev/null +++ b/src/or/circuit.c @@ -0,0 +1,319 @@ + +#include "or.h" + +/********* START VARIABLES **********/ + +circuit_t *global_circuitlist=NULL; + +/********* END VARIABLES ************/ + +void circuit_add(circuit_t *circ) { + + if(!global_circuitlist) { /* first one */ + global_circuitlist = circ; + circ->next = NULL; + } else { + circ->next = global_circuitlist; + global_circuitlist = circ; + } + +} + +void circuit_remove(circuit_t *circ) { + circuit_t *tmpcirc; + + if(!circ || !global_circuitlist) + return; + + if(global_circuitlist == circ) { + global_circuitlist = global_circuitlist->next; + return; + } + + for(tmpcirc = global_circuitlist;tmpcirc->next;tmpcirc = tmpcirc->next) { + if(tmpcirc->next == circ) { + tmpcirc->next = circ->next; + return; + } + } + +} + +circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) { + + circuit_t *circ; + + circ = (circuit_t *)malloc(sizeof(circuit_t)); + if(!circ) + return NULL; + memset(circ,0,sizeof(circuit_t)); /* zero it out */ + + circ->p_aci = p_aci; + 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; + circ->n_aci = 0; /* we need to have identified the next hop to choose a correct ACI */ + + circuit_add(circ); + + return circ; +} + +void circuit_free(circuit_t *circ) { + + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + + if(circ->onion) + free(circ->onion); + + free(circ); + +} + +aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) { + aci_t test_aci; + connection_t *conn; + + log(LOG_DEBUG,"get_unique_aci_by_addr_port() trying to get a unique aci"); + + RAND_pseudo_bytes((unsigned char *)&test_aci, 2); + + if(aci_type == ACI_TYPE_LOWER) + test_aci &= htons(0x00FF); + if(aci_type == ACI_TYPE_HIGHER) + test_aci &= htons(0xFF00); + + if(test_aci == 0) + return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */ + + conn = connection_get_by_addr_port(addr,port); + if(!conn) /* there can't be a conflict -- no connection of that sort yet */ + return test_aci; + + if(circuit_get_by_aci_conn(test_aci, conn)) + return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */ + + return test_aci; + +} + +int circuit_init(circuit_t *circ, int aci_type) { + onion_layer_t *ol; + int retval = 0; + unsigned char digest1[20]; + unsigned char digest2[20]; + + if (!circ) + return -1; + + ol = (onion_layer_t *)circ->onion; + if (!ol) + return -1; + + log(LOG_DEBUG,"circuit_init(): starting"); + circ->n_addr = ol->addr; + circ->n_port = ol->port; + log(LOG_DEBUG,"circuit_init(): Set port to %u.",ntohs(ol->port)); + circ->p_f = ol->backf; + log(LOG_DEBUG,"circuit_init(): Set BACKF to %u.",ol->backf); + circ->n_f = ol->forwf; + log(LOG_DEBUG,"circuit_init(): Set FORWF to %u.",ol->forwf); + circ->state = CIRCUIT_STATE_OPEN; + + log(LOG_DEBUG,"circuit_init(): aci_type = %u.",aci_type); + + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type); + + log(LOG_DEBUG,"circuit_init(): Chosen ACI %u.",circ->n_aci); + + /* keys */ + SHA1(ol->keyseed,16,digest1); + SHA1(digest1,20,digest2); + SHA1(digest2,20,digest1); + memcpy(circ->p_key,digest2,16); + memcpy(circ->n_key,digest1,16); + log(LOG_DEBUG,"circuit_init(): Computed keys."); + + /* set IVs to zero */ + memset(circ->n_iv,0,16); + memset(circ->p_iv,0,16); + + /* initialize cipher context */ + EVP_CIPHER_CTX_init(&circ->n_ctx); + EVP_CIPHER_CTX_init(&circ->p_ctx); + + /* initialize crypto engines */ + switch(circ->p_f) + { + case ONION_CIPHER_DES : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_des_ofb(), circ->p_key, circ->p_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_rc4(), circ->p_key,circ->p_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_enc_null(), circ->p_key, circ->p_iv); + break; + default : + log(LOG_ERR,"Onion contains unrecognized cipher(%u) for ACI : %u.",circ->p_f,circ->n_aci); + return -1; + break; + } + + if (!retval) /* EVP_EncryptInit() error */ + { + log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + return -1; + } + switch(circ->n_f) + { + case ONION_CIPHER_DES : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_des_ofb(), circ->n_key, circ->n_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_rc4(), circ->n_key,circ->n_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_enc_null(), circ->n_key, circ->n_iv); + break; + default : + log(LOG_ERR,"Onion contains unrecognized cipher for ACI : %u.",circ->n_aci); + return -1; + break; + } + if (!retval) /* EVP_EncryptInit() error */ + { + log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + return -1; + } + log(LOG_DEBUG,"circuit_init(): Cipher initialization complete."); + + circ->expire = ol->expire; + + return 0; +} + +circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn && circ->p_aci == aci) + return circ; + if(circ->n_conn == conn && circ->n_aci == aci) + return circ; + } + return NULL; +} + +circuit_t *circuit_get_by_conn(connection_t *conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn) + return circ; + if(circ->n_conn == conn) + return circ; + } + return NULL; +} + +int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type) { + + /* first decrypt cell->length */ + if(circuit_crypt(circ, &(cell->length), 1, crypt_type) < 0) { + log(LOG_DEBUG,"circuit_deliver_data_cell(): length decryption failed. Dropping connection."); + return -1; + } + + /* then decrypt the payload */ + if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, crypt_type) < 0) { + log(LOG_DEBUG,"circuit_deliver_data_cell(): payload decryption failed. Dropping connection."); + return -1; + } + + if(conn->type == CONN_TYPE_APP) { /* send payload directly */ + log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to application."); + if(connection_app_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 0; /* success */ +} + +int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) { + char *out; + int outlen; + + if(!circ || !in) + return -1; + + out = malloc(inlen); + if(!out) + return -1; + + 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; + } + } 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(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; +} + +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); + circuit_free(circ); +} + +void circuit_about_to_close_connection(connection_t *conn) { + /* send destroys for all circuits using conn */ + /* currently, we assume it's too late to flush conn's buf here. + * down the road, maybe we'll consider that eof doesn't mean can't-write + */ + + circuit_t *circ; + + while((circ = circuit_get_by_conn(conn))) { + circuit_remove(circ); + if(circ->n_conn == conn) /* it's closing in front of us */ + connection_send_destroy(circ->p_aci, circ->p_conn); + if(circ->p_conn == conn) /* it's closing behind us */ + connection_send_destroy(circ->n_aci, circ->n_conn); + circuit_free(circ); + } +} + |