aboutsummaryrefslogtreecommitdiff
path: root/src/or/circuit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/circuit.c')
-rw-r--r--src/or/circuit.c319
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);
+ }
+}
+