/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */ /* See LICENSE for licensing information */ /* $Id$ */ #include "or.h" extern or_options_t options; /* command-line and config-file options */ static int onion_process(circuit_t *circ); static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn); static int find_tracked_onion(unsigned char *onion, uint32_t onionlen); int decide_aci_type(uint32_t local_addr, uint16_t local_port, uint32_t remote_addr, uint16_t remote_port) { if(local_addr > remote_addr) return ACI_TYPE_HIGHER; if(local_addr < remote_addr) return ACI_TYPE_LOWER; if(local_port > remote_port) return ACI_TYPE_HIGHER; /* else */ return ACI_TYPE_LOWER; } /* global (within this file) variables used by the next few functions */ static struct onion_queue_t *ol_list=NULL; static struct onion_queue_t *ol_tail=NULL; static int ol_length=0; int onion_pending_add(circuit_t *circ) { struct onion_queue_t *tmp; tmp = malloc(sizeof(struct onion_queue_t)); memset(tmp, 0, sizeof(struct onion_queue_t)); tmp->circ = circ; if(!ol_tail) { assert(!ol_list); assert(!ol_length); ol_list = tmp; ol_tail = tmp; ol_length++; return 0; } assert(ol_list); assert(!ol_tail->next); if(ol_length >= options.MaxOnionsPending) { log(LOG_INFO,"onion_pending_add(): Already have %d onions queued. Closing.", ol_length); free(tmp); return -1; } ol_length++; ol_tail->next = tmp; ol_tail = tmp; return 0; } int onion_pending_check(void) { if(ol_list) return 1; else return 0; } void onion_pending_process_one(void) { struct data_queue_t *tmpd; circuit_t *circ; if(!ol_list) return; /* no onions pending, we're done */ assert(ol_list->circ && ol_list->circ->p_conn); assert(ol_length > 0); circ = ol_list->circ; if(onion_process(circ) < 0) { log(LOG_DEBUG,"onion_pending_process_one(): Failed. Closing."); onion_pending_remove(circ); circuit_close(circ); } else { log(LOG_DEBUG,"onion_pending_process_one(): Succeeded. Delivering queued data cells."); for(tmpd = ol_list->data_cells; tmpd; tmpd=tmpd->next) { command_process_data_cell(tmpd->cell, circ->p_conn); } onion_pending_remove(circ); } return; } /* go through ol_list, find the onion_queue_t element which points to * circ, remove and free that element. leave circ itself alone. */ void onion_pending_remove(circuit_t *circ) { struct onion_queue_t *tmpo, *victim; struct data_queue_t *tmpd; if(!ol_list) return; /* nothing here. */ /* first check to see if it's the first entry */ tmpo = ol_list; if(tmpo->circ == circ) { /* it's the first one. remove it from the list. */ ol_list = tmpo->next; if(!ol_list) ol_tail = NULL; ol_length--; victim = tmpo; } else { /* we need to hunt through the rest of the list */ for( ;tmpo->next && tmpo->next->circ != circ; tmpo=tmpo->next) ; if(!tmpo->next) { log(LOG_WARNING,"onion_pending_remove(): circ (p_aci %d), not in list!",circ->p_aci); return; } /* now we know tmpo->next->circ == circ */ victim = tmpo->next; tmpo->next = victim->next; if(ol_tail == victim) ol_tail = tmpo; ol_length--; } /* now victim points to the element that needs to be removed */ /* first dump the attached data cells too, if any */ while(victim->data_cells) { tmpd = victim->data_cells; victim->data_cells = tmpd->next; free(tmpd->cell); free(tmpd); } free(victim); } struct data_queue_t *data_queue_add(struct data_queue_t *list, cell_t *cell) { struct data_queue_t *tmpd, *newd; newd = malloc(sizeof(struct data_queue_t)); memset(newd, 0, sizeof(struct data_queue_t)); newd->cell = malloc(sizeof(cell_t)); memcpy(newd->cell, cell, sizeof(cell_t)); if(!list) { return newd; } for(tmpd = list; tmpd->next; tmpd=tmpd->next) ; /* now tmpd->next is null */ tmpd->next = newd; return list; } /* a data cell has arrived for a circuit which is still pending. Find * the right entry in ol_list, and add it to the end of the 'data_cells' * list. */ void onion_pending_data_add(circuit_t *circ, cell_t *cell) { struct onion_queue_t *tmpo; for(tmpo=ol_list; tmpo; tmpo=tmpo->next) { if(tmpo->circ == circ) { tmpo->data_cells = data_queue_add(tmpo->data_cells, cell); return; } } } /* helper function for onion_process */ static int onion_deliver_to_conn(aci_t aci, unsigned char *onion, uint32_t onionlen, connection_t *conn) { char *buf; int buflen, dataleft; cell_t cell; assert(aci && onion && onionlen); buflen = onionlen+4; buf = malloc(buflen); if(!buf) return -1; log(LOG_DEBUG,"onion_deliver_to_conn(): Setting onion length to %u.",onionlen); *(uint32_t*)buf = htonl(onionlen); memcpy((buf+4),onion,onionlen); dataleft = buflen; while(dataleft > 0) { memset(&cell,0,sizeof(cell_t)); cell.command = CELL_CREATE; cell.aci = aci; if(dataleft >= CELL_PAYLOAD_SIZE) cell.length = CELL_PAYLOAD_SIZE; else cell.length = dataleft; memcpy(cell.payload, buf+buflen-dataleft, cell.length); dataleft -= cell.length; log(LOG_DEBUG,"onion_deliver_to_conn(): Delivering create cell, payload %d bytes.",cell.length); if(connection_write_cell_to_buf(&cell, conn) < 0) { log(LOG_DEBUG,"onion_deliver_to_conn(): Could not buffer new create cells. Closing."); free(buf); return -1; } } free(buf); return 0; } static int onion_process(circuit_t *circ) { connection_t *n_conn; int retval; aci_t aci_type; struct sockaddr_in me; /* my router identity */ if(learn_my_address(&me) < 0) return -1; /* decrypt it in-place */ if(decrypt_onion(circ->onion,circ->onionlen,getprivatekey()) < 0) { log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit."); return -1; } log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted."); /* check freshness */ if (ntohl(*(uint32_t *)(circ->onion+8)) < (uint32_t)time(NULL)) /* expired onion */ { log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack."); return -1; } aci_type = decide_aci_type(ntohl(me.sin_addr.s_addr), ntohs(me.sin_port), ntohl(*(uint32_t *)(circ->onion+4)),ntohs(*(uint16_t *)(circ->onion+2))); if(circuit_init(circ, aci_type) < 0) { log(LOG_ERR,"process_onion(): init_circuit() failed."); return -1; } /* check for replay. at the same time, add it to the pile of tracked onions. */ if(find_tracked_onion(circ->onion, circ->onionlen)) { log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack."); return -1; } /* now we must send create cells to the next router */ if(circ->n_addr && circ->n_port) { n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); if(!n_conn || n_conn->type != CONN_TYPE_OR) { /* i've disabled making connections through OPs, but it's definitely * possible here. I'm not sure if it would be a bug or a feature. -RD */ /* note also that this will close circuits where the onion has the same * router twice in a row in the path. i think that's ok. -RD */ log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing."); return -1; } circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ circ->n_port = n_conn->port; circ->n_conn = n_conn; log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,n_conn->port); /* send the CREATE cells on to the next hop */ pad_onion(circ->onion, circ->onionlen, ONION_LAYER_SIZE); log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data."); retval = onion_deliver_to_conn(circ->n_aci, circ->onion, circ->onionlen, n_conn); free(circ->onion); circ->onion = NULL; if (retval == -1) { log(LOG_DEBUG,"command_process_create_cell(): Could not deliver the onion to next conn. Closing."); return -1; } } else { /* this is destined for an exit */ log(LOG_DEBUG,"command_process_create_cell(): create cell reached exit. Circuit established."); #if 0 log(LOG_DEBUG,"command_process_create_cell(): Creating new exit connection."); n_conn = connection_new(CONN_TYPE_EXIT); if(!n_conn) { log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing."); return -1; } n_conn->state = EXIT_CONN_STATE_CONNECTING_WAIT; n_conn->receiver_bucket = -1; /* edge connections don't do receiver buckets */ n_conn->bandwidth = -1; n_conn->s = -1; /* not yet valid */ if(connection_add(n_conn) < 0) { /* no space, forget it */ log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing."); connection_free(n_conn); return -1; } circ->n_conn = n_conn; #endif } 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 = crypto_pseudo_rand(1, &coin); if (retval) 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, int rarray_len, int *routelen) { int i, j; int num_acceptable_routers = 0; unsigned int *route = NULL; unsigned int oldchoice, choice; assert((cw >= 0) && (cw < 1) && (rarray) && (routelen) ); /* 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 %d.",*routelen); for(i=0;iaddr, rarray[i]->or_port)) { log(LOG_DEBUG,"Nope, %d is not connected.",i); goto next_i_loop; } for(j=0;jpkey, rarray[j]->pkey)) { /* these guys are twins. so we've already counted him. */ log(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j); goto next_i_loop; } } num_acceptable_routers++; log(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num_acceptable_routers); next_i_loop: ; /* our compiler may need an explicit statement after the label */ } if(num_acceptable_routers < *routelen) { log(LOG_DEBUG,"new_route(): Cutting routelen from %d to %d.",*routelen, num_acceptable_routers); *routelen = num_acceptable_routers; } if(*routelen < 1) { log(LOG_ERR,"new_route(): Didn't find any acceptable routers. Failing."); return NULL; } /* 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); if(crypto_pseudo_rand(sizeof(unsigned int),(unsigned char *)&choice)) { free((void *)route); return NULL; } choice = choice % (rarray_len); log(LOG_DEBUG,"new_route(): Contemplating router %u.",choice); if(choice == oldchoice || (oldchoice < rarray_len && !crypto_pk_cmp_keys(rarray[choice]->pkey, rarray[oldchoice]->pkey)) || (options.ORPort && !connection_twin_get_by_addr_port(rarray[choice]->addr, rarray[choice]->or_port))) { /* Same router as last choice, or router twin, * or no routers with that key are connected to us. * Try again. */ log(LOG_DEBUG,"new_route(): Picked a router %d that won't work as next hop.",choice); i--; continue; } log(LOG_DEBUG,"new_route(): Chosen router %u for hop %u.",choice,i); oldchoice = choice; route[i] = choice; } return route; } crypto_cipher_env_t * create_onion_cipher(int cipher_type, char *key, char *iv, int encrypt_mode) { switch (cipher_type) { case ONION_CIPHER_DES: cipher_type = CRYPTO_CIPHER_DES; break; case ONION_CIPHER_3DES: cipher_type = CRYPTO_CIPHER_3DES; break; case ONION_CIPHER_RC4 : cipher_type = CRYPTO_CIPHER_RC4; break; case ONION_CIPHER_IDENTITY : cipher_type = CRYPTO_CIPHER_IDENTITY; break; default: log(LOG_ERR, "Unknown cipher type %d", cipher_type); return NULL; } return crypto_create_init_cipher(cipher_type, key, iv, encrypt_mode); } /* creates a new onion from route, stores it and its length into buf and len respectively */ unsigned char *create_onion(routerinfo_t **rarray, int rarray_len, unsigned int *route, int routelen, int *len, crypt_path_t **cpath) { int i,j; char *layer; crypt_path_t *hop = NULL; unsigned char *buf; routerinfo_t *router; unsigned char iv[16]; struct in_addr netaddr; assert(rarray && route && len && routelen); /* calculate the size of the onion */ *len = routelen * ONION_LAYER_SIZE + ONION_PADDING_SIZE; /* 28 bytes per layer + 100 bytes padding for the innermost layer */ log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*len); /* allocate memory for the onion */ buf = malloc(*len); if(!buf) { log(LOG_ERR,"Error allocating memory."); return NULL; } log(LOG_DEBUG,"create_onion() : Allocated memory for the onion."); for(i=0; iaddr); log(LOG_DEBUG,"create_onion(): %u : %s:%u, %u/%u",routelen-i, inet_ntoa(netaddr), (rarray[route[i]])->or_port, (rarray[route[i]])->pkey, crypto_pk_keysize((rarray[route[i]])->pkey)); } layer = buf + *len - ONION_LAYER_SIZE - ONION_PADDING_SIZE; /* pointer to innermost layer */ /* create the onion layer by layer, starting with the innermost */ for (i=0;iaddr)),router->or_port); // log(LOG_DEBUG,"create_onion() : Key pointer = %u.",router->pkey); // log(LOG_DEBUG,"create_onion() : Key size = %u.",crypto_pk_keysize(router->pkey)); *layer = OR_VERSION; /* Back F + Forw F both use DES OFB*/ *(layer+1) = (ONION_DEFAULT_CIPHER << 4) /* for backf */ + ONION_DEFAULT_CIPHER; /* for forwf */ /* Dest Port */ if (i) /* not last hop */ *(uint16_t *)(layer+2) = htons(rarray[route[i-1]]->or_port); else *(uint16_t *)(layer+2) = htons(0); /* Dest Addr */ if (i) /* not last hop */ *(uint32_t *)(layer+4) = htonl(rarray[route[i-1]]->addr); else *(uint32_t *)(layer+4) = htonl(0); /* Expiration Time */ *(uint32_t *)(layer+8) = htonl((uint32_t)(time(NULL) + 86400)); /* NOW + 1 day */ /* Key Seed Material */ if(crypto_rand(16, layer+12)) { /* error */ log(LOG_ERR,"Error generating random data."); goto error; } // 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)),layer->port); /* build up the crypt_path */ if(cpath) { cpath[i] = (crypt_path_t *)malloc(sizeof(crypt_path_t)); if(!cpath[i]) { log(LOG_ERR,"Error allocating memory."); goto error; } log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1); hop = cpath[i]; /* set crypto functions */ hop->backf = *(layer+1) >> 4; hop->forwf = *(layer+1) & 0x0f; /* calculate keys */ crypto_SHA_digest(layer+12,16,hop->digest3); log(LOG_DEBUG,"create_onion() : First SHA pass performed."); crypto_SHA_digest(hop->digest3,20,hop->digest2); log(LOG_DEBUG,"create_onion() : Second SHA pass performed."); crypto_SHA_digest(hop->digest2,20,hop->digest3); log(LOG_DEBUG,"create_onion() : Third SHA pass performed."); log(LOG_DEBUG,"create_onion() : Keys generated."); /* set IV to zero */ memset((void *)iv,0,16); /* initialize cipher engines */ if (! (hop->f_crypto = create_onion_cipher(hop->forwf, hop->digest3, iv, 1))) { /* cipher initialization failed */ log(LOG_ERR,"Could not create a crypto environment."); goto error; } if (! (hop->b_crypto = create_onion_cipher(hop->backf, hop->digest2, iv, 0))) { /* cipher initialization failed */ log(LOG_ERR,"Could not create a crypto environment."); goto error; } log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop."); } /* padding if this is the innermost layer */ if (!i) { if (crypto_pseudo_rand(ONION_PADDING_SIZE, layer + ONION_LAYER_SIZE)) { /* error */ log(LOG_ERR,"Error generating pseudo-random data."); goto error; } log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding."); } /* encrypt */ if(encrypt_onion(layer,ONION_PADDING_SIZE+(i+1)*ONION_LAYER_SIZE,router->pkey) < 0) { log(LOG_ERR,"Error encrypting onion layer."); goto error; } log(LOG_DEBUG,"create_onion() : Encrypted layer."); /* calculate pointer to next layer */ layer = buf + (routelen-i-2)*ONION_LAYER_SIZE; } return buf; error: if (buf) free(buf); if (cpath) { for (j=0;jf_crypto) crypto_free_cipher_env(cpath[i]->f_crypto); if(cpath[i]->b_crypto) crypto_free_cipher_env(cpath[i]->b_crypto); free((void *)cpath[i]); } } 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 */ int encrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *pkey) { unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */ unsigned char digest[20]; /* stores SHA1 output - 160 bits */ unsigned char iv[8]; crypto_cipher_env_t *crypt_env = NULL; /* crypto environment */ assert(onion && pkey); assert(onionlen >= 128); memset(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)),onion->port); /* allocate space for tmpbuf */ tmpbuf = (unsigned char *)malloc(onionlen); if(!tmpbuf) { log(LOG_ERR,"Could not allocate memory."); return -1; } log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf); /* get key1 = SHA1(KeySeed) */ if (crypto_SHA_digest(onion+12,16,digest)) { log(LOG_ERR,"Error computing SHA1 digest."); goto error; } log(LOG_DEBUG,"encrypt_onion() : Computed DES key."); log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt."); /* encrypt 128 bytes with RSA *pkey */ if (crypto_pk_public_encrypt(pkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) { log(LOG_ERR,"Error RSA-encrypting data :%s",crypto_perror()); goto error; } log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion."); /* now encrypt the rest with 3DES OFB */ crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 1); if (!crypt_env) { log(LOG_ERR,"Error creating the crypto environment."); goto error; } if (crypto_cipher_encrypt(crypt_env,onion+128, onionlen-128, (unsigned char *)tmpbuf+128)) { /* error */ log(LOG_ERR,"Error performing DES encryption:%s",crypto_perror()); goto error; } log(LOG_DEBUG,"encrypt_onion() : 3DES OFB encrypted the rest of the onion."); /* now copy tmpbuf to onion */ memcpy(onion,tmpbuf,onionlen); log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer."); free(tmpbuf); crypto_free_cipher_env(crypt_env); return 0; error: if (tmpbuf) free(tmpbuf); if (crypt_env) crypto_free_cipher_env(crypt_env); return -1; } /* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */ int decrypt_onion(unsigned char *onion, uint32_t onionlen, crypto_pk_env_t *prkey) { void *tmpbuf = NULL; /* temporary buffer for crypto operations */ unsigned char digest[20]; /* stores SHA1 output - 160 bits */ unsigned char iv[8]; crypto_cipher_env_t *crypt_env =NULL; /* crypto environment */ assert(onion && prkey); memset(iv,0,8); /* allocate space for tmpbuf */ tmpbuf = malloc(onionlen); if (!tmpbuf) { log(LOG_ERR,"Could not allocate memory."); return -1; } log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer."); /* decrypt 128 bytes with RSA *prkey */ if (crypto_pk_private_decrypt(prkey, onion, 128, tmpbuf, RSA_NO_PADDING) == -1) { log(LOG_ERR,"Error RSA-decrypting data :%s",crypto_perror()); goto error; } log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete."); /* get key1 = SHA1(KeySeed) */ if (crypto_SHA_digest(tmpbuf+12,16,digest)) { log(LOG_ERR,"Error computing SHA1 digest."); goto error; } log(LOG_DEBUG,"decrypt_onion() : Computed DES key."); /* now decrypt the rest with 3DES OFB */ crypt_env = crypto_create_init_cipher(CRYPTO_CIPHER_3DES, digest, iv, 0); if (!crypt_env) { log(LOG_ERR,"Error creating crypto environment"); goto error; } if (crypto_cipher_decrypt(crypt_env,onion+128, onionlen-128,tmpbuf+128)) { log(LOG_ERR,"Error performing DES decryption:%s",crypto_perror()); goto error; } log(LOG_DEBUG,"decrypt_onion() : DES decryption complete."); /* now copy tmpbuf to onion */ memcpy(onion,tmpbuf,onionlen); free(tmpbuf); crypto_free_cipher_env(crypt_env); return 0; error: if (tmpbuf) free(tmpbuf); if (crypt_env) crypto_free_cipher_env(crypt_env); return -1; } /* 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, int n) { assert(onion); memmove(onion,onion+n,onionlen-n); crypto_pseudo_rand(n, onion+onionlen-n); } /* red black tree using Niels' tree.h. I used http://www.openbsd.org/cgi-bin/cvsweb/src/regress/sys/sys/tree/rb/ as my guide */ #include "tree.h" struct tracked_onion { RB_ENTRY(tracked_onion) node; uint32_t expire; char digest[20]; /* SHA digest of the onion */ struct tracked_onion *next; }; RB_HEAD(tracked_tree, tracked_onion) tracked_root; int compare_tracked_onions(struct tracked_onion *a, struct tracked_onion *b) { return memcmp(a->digest, b->digest, 20); } RB_PROTOTYPE(tracked_tree, tracked_onion, node, compare_tracked_onions) RB_GENERATE(tracked_tree, tracked_onion, node, compare_tracked_onions) void init_tracked_tree(void) { RB_INIT(&tracked_root); } /* see if this onion has been seen before. if so, return 1, else * return 0 and add the sha1 of this onion to the tree. */ static int find_tracked_onion(unsigned char *onion, uint32_t onionlen) { static struct tracked_onion *head_tracked_onions = NULL; /* linked list of tracked onions */ static struct tracked_onion *tail_tracked_onions = NULL; uint32_t now = time(NULL); struct tracked_onion *to; /* first take this opportunity to see if there are any expired * onions in the tree. we know this is fast because the linked list * 'tracked_onions' is ordered by when they were seen. */ while(head_tracked_onions && (head_tracked_onions->expire < now)) { to = head_tracked_onions; log(LOG_DEBUG,"find_tracked_onion(): Forgetting old onion (expires %d)", to->expire); head_tracked_onions = to->next; if(!head_tracked_onions) /* if there are no more, */ tail_tracked_onions = NULL; /* then make sure the list's tail knows that too */ RB_REMOVE(tracked_tree, &tracked_root, to); free(to); } to = malloc(sizeof(struct tracked_onion)); /* compute the SHA digest of the onion */ crypto_SHA_digest(onion, onionlen, to->digest); /* try adding it to the tree. if it's already there it will return it. */ if(RB_INSERT(tracked_tree, &tracked_root, to)) { /* yes, it's already there: this is a replay. */ free(to); return 1; } /* this is a new onion. add it to the list. */ to->expire = ntohl(*(uint32_t *)(onion+8)); /* set the expiration date */ to->next = NULL; if (!head_tracked_onions) { head_tracked_onions = to; } else { tail_tracked_onions->next = to; } tail_tracked_onions = to; log(LOG_DEBUG,"find_tracked_onion(): Remembered new onion (expires %d)", to->expire); return 0; } /* Local Variables: mode:c indent-tabs-mode:nil c-basic-offset:2 End: */