/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* See LICENSE for licensing information */
/* $Id$ */

#include "or.h"

/********* START VARIABLES **********/

tracked_onion_t *tracked_onions = NULL; /* linked list of tracked onions */
tracked_onion_t *last_tracked_onion = NULL;

/********* END VARIABLES ************/


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; 
}

int process_onion(circuit_t *circ, connection_t *conn) {
  aci_t aci_type;

  if(!decrypt_onion((onion_layer_t *)circ->onion,circ->onionlen,conn->prkey)) {
    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 (((onion_layer_t *)circ->onion)->expire < 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(conn->local.sin_addr.s_addr, ntohs(conn->local.sin_port),
             ((onion_layer_t *)circ->onion)->addr,((onion_layer_t *)circ->onion)->port);
      
  if(circuit_init(circ, aci_type) < 0) { 
    log(LOG_ERR,"process_onion(): init_circuit() failed.");
    return -1;
  }

  /* check for replay */
  if(id_tracked_onion(circ->onion, circ->onionlen, tracked_onions)) {
    log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack.");
    return -1;
  }

  /* track the new onion */
  if(!new_tracked_onion(circ->onion,circ->onionlen, &tracked_onions, &last_tracked_onion)) {
    log(LOG_DEBUG,"process_onion(): Onion tracking failed. Will ignore.");
  }

  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;i<rarray_len;i++) {
    log(LOG_DEBUG,"Contemplating whether router %d is any good...",i);
    if(!connection_exact_get_by_addr_port(rarray[i]->addr, rarray[i]->or_port)) {
      log(LOG_DEBUG,"Nope, %d is not connected.",i);
      goto next_i_loop;
    }
    for(j=0;j<i;j++) {
      if(!pkey_cmp(rarray[i]->pkey, 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 && !pkey_cmp(rarray[choice]->pkey, rarray[oldchoice]->pkey)) ||
      !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;
}

/* 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;
  int retval = 0;
  onion_layer_t *layer = NULL;
  crypt_path_t *hop = NULL;
  unsigned char *retbuf = NULL;
  unsigned char *buf;
  routerinfo_t *router;
  unsigned char iv[16];

  assert(rarray && route && len && routelen);

    /* calculate the size of the onion */
    *len = 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.",*len);
    
    /* allocate memory for the onion */
    buf = (unsigned char *)malloc(*len);
    if (!buf)
    {
      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))),(rarray[route[retval]])->or_port,(rarray[route[retval]])->pkey,crypto_pk_keysize((rarray[route[retval]])->pkey));
    }
    
    layer = (onion_layer_t *)(buf + *len - 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)),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)); 
      
      /* 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 = crypto_rand(16, layer->keyseed);
      if (retval) /* error */
      {
	log(LOG_ERR,"Error generating random data.");
	free((void *)buf);
	if (cpath)
	{
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
      }
      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.");
	  free((void *)buf);
          for (j=0;j<i;j++) {
	    if (cpath[i]->f_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]);
	  }
	}
      
	log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1);
	hop = cpath[i];
	/* set crypto functions */
	hop->backf = layer->backf;
	hop->forwf = layer->forwf;
	
	/* calculate keys */
	crypto_SHA_digest(layer->keyseed,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 */
	switch(layer->forwf)
	{
	 case ONION_CIPHER_DES :
	  hop->f_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_DES);
	  break;
	 case ONION_CIPHER_RC4 :
	  hop->f_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_RC4);
	  break;
	 case ONION_CIPHER_IDENTITY :
	  hop->f_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_IDENTITY);
	  break;
	}
	if (!hop->f_crypto) /* cipher initialization failed */
	{
	  log(LOG_ERR,"Could not create a crypto environment.");
	  free((void *)buf);
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
	}
	/* set the key and IV */
	if (crypto_cipher_set_key(hop->f_crypto, hop->digest3) || 
	    crypto_cipher_set_iv(hop->f_crypto, iv)) {
	  log(LOG_ERR,"Could not initialize the crypto engine.");
          free((void *)buf);
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
	}
	
	switch(layer->backf)
	{
	 case ONION_CIPHER_DES :
	  hop->b_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_DES);
	  break;
	 case ONION_CIPHER_RC4 :
	  hop->b_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_RC4);
	  break;
	 case ONION_CIPHER_IDENTITY :
	  hop->b_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_IDENTITY);
	  break;
	}
	if (!hop->b_crypto) /* cipher initialization failed */
	{
	  log(LOG_ERR,"Could not create a crypto environment.");
	  free((void *)buf);
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
	}
	/* set the key and IV */
	if (crypto_cipher_set_key(hop->b_crypto, hop->digest2) || 
	    crypto_cipher_set_iv(hop->b_crypto, iv)) {
	  log(LOG_ERR,"Could not initialize the crypto engine.");
          free((void *)buf);
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
	}

        /* initialize */
	if (crypto_cipher_encrypt_init_cipher(hop->f_crypto) || crypto_cipher_decrypt_init_cipher(hop->b_crypto)) {
	  log(LOG_ERR,"Could not initialize the crypto engine.");
	  free((void *)buf);
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
	}
	    
	log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop.");
      }
      
      /* padding if this is the innermost layer */
      if (!i)
      {
	retval=crypto_pseudo_rand(100, (unsigned char *)layer + 28);
	if (retval) /* error */
	{
	  log(LOG_ERR,"Error generating pseudo-random data.");
	  free((void *)buf);
	  if (cpath)
	  {
	    for (j=0;j<i;j++) {
	      if (cpath[i]->f_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;
	}
	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 *)buf);
	if (cpath)
	{
	  for (j=0;j<i;j++) {
	    if (cpath[i]->f_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;
      }
      log(LOG_DEBUG,"create_onion() : Encrypted layer.");
      
      /* calculate pointer to next layer */
      layer = (onion_layer_t *)(buf + (routelen-i-2)*sizeof(onion_layer_t));
    }

    return buf;
}

/* 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, 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];
  int retval = 0;
  
  crypto_cipher_env_t *crypt_env; /* crypto environment */
 
  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)),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) */
    if (crypto_SHA_digest(((onion_layer_t *)onion)->keyseed,16,digest))
    {
      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 = crypto_pk_public_encrypt(pkey, (unsigned char *)onion, 128, tmpbuf, RSA_NO_PADDING); 
    if (retval == -1) 
    { 
      log(LOG_ERR,"Error RSA-encrypting data :%s",crypto_perror());
      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 */
    crypt_env = crypto_new_cipher_env(CRYPTO_CIPHER_DES);
    if (!crypt_env)
    {
      log(LOG_ERR,"Error creating the crypto environment.");
      free((void *)tmpbuf);
      return NULL;
    }
    if (crypto_cipher_set_key(crypt_env, digest)) /* error */ 
    { 
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror()); 
      free((void *)tmpbuf); 
      return NULL; 
    } 
    if (crypto_cipher_set_iv(crypt_env, iv))
    {    
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    if (crypto_cipher_encrypt_init_cipher(crypt_env)) {
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    
    retval = crypto_cipher_encrypt(crypt_env,(unsigned char *)onion+128, onionlen-128, (unsigned char *)tmpbuf+128);
    if (retval) /* error */
    { 
      log(LOG_ERR,"Error performing DES encryption:%s",crypto_perror()); 
      free((void *)tmpbuf); 
      return NULL; 
    }
    log(LOG_DEBUG,"encrypt_onion() : DES OFB encrypted the rest of the onion.");
  
    crypto_free_cipher_env(crypt_env);
  
    /* 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, 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];
  int retval = 0;
  
  crypto_cipher_env_t *crypt_env; /* crypto environment */
  
  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 = crypto_pk_private_decrypt(prkey, (unsigned char*)onion, 128, (unsigned char *)tmpbuf, RSA_NO_PADDING);
    if (retval == -1)
    {
      log(LOG_ERR,"Error RSA-decrypting data :%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete.");
    
    /* get key1 = SHA1(KeySeed) */
    retval = crypto_SHA_digest(((onion_layer_t *)tmpbuf)->keyseed,16,digest);
    if (retval)
    {
      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 */
    crypt_env = crypto_new_cipher_env(CRYPTO_CIPHER_DES);
    if (!crypt_env)
    {
      log(LOG_ERR,"Error creating the crypto environment.");
      free((void *)tmpbuf);
      return NULL;
    }
    if (crypto_cipher_set_key(crypt_env, digest)) /* error */
    {
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    if (crypto_cipher_set_iv(crypt_env, iv))
    {
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    if (crypto_cipher_decrypt_init_cipher(crypt_env)) {
      log(LOG_ERR,"Error initializing DES engine:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    
    retval = crypto_cipher_decrypt(crypt_env,(unsigned char *)onion+128, onionlen-128,(unsigned char *)tmpbuf+128);
    if (retval) /* error */
    {
      log(LOG_ERR,"Error performing DES decryption:%s",crypto_perror());
      free((void *)tmpbuf);
      return NULL;
    }
    
    crypto_free_cipher_env(crypt_env);
    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, int n)
{
  if (onion) /* valid parameter */
  {
    memmove((void *)onion,(void *)(onion+n),onionlen-n);
    crypto_pseudo_rand(n, onion+onionlen-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 */
  if (crypto_SHA_digest(onion, onionlen, 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 */
  crypto_SHA_digest(onion,onionlen, digest);
  
  while(to)
  {
    if (!memcmp((void *)digest, (void *)to->digest, 20))
      return to;
    to = (tracked_onion_t *)to->next;
  }
  
  return NULL;
}