2003-10-08 04:04:08 +02:00
|
|
|
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
|
Implemented link padding and receiver token buckets
Each socket reads at most 'bandwidth' bytes per second sustained, but
can handle bursts of up to 10*bandwidth bytes.
Cells are now sent out at evenly-spaced intervals, with padding sent
out otherwise. Set Linkpadding=0 in the rc file to send cells as soon
as they're available (and to never send padding cells).
Added license/copyrights statements at the top of most files.
router->min and router->max have been merged into a single 'bandwidth'
value. We should make the routerinfo_t reflect this (want to do that,
Mat?)
As the bandwidth increases, and we want to stop sleeping more and more
frequently to send a single cell, cpu usage goes up. At 128kB/s we're
pretty much calling poll with a timeout of 1ms or even 0ms. The current
code takes a timeout of 0-9ms and makes it 10ms. prepare_for_poll()
handles everything that should have happened in the past, so as long as
our buffers don't get too full in that 10ms, we're ok.
Speaking of too full, if you run three servers at 100kB/s with -l debug,
it spends too much time printing debugging messages to be able to keep
up with the cells. The outbuf ultimately fills up and it kills that
connection. If you run with -l err, it works fine up through 500kB/s and
probably beyond. Down the road we'll want to teach it to recognize when
an outbuf is getting full, and back off.
svn:r50
2002-07-16 03:12:15 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
/* $Id$ */
|
2002-06-27 00:45:49 +02:00
|
|
|
|
|
|
|
#include "or.h"
|
|
|
|
|
2004-04-01 22:33:29 +02:00
|
|
|
/* prototypes for smartlist operations from routerlist.h
|
|
|
|
* they're here to prevent precedence issues with the .h files
|
|
|
|
*/
|
|
|
|
void router_add_running_routers_to_smartlist(smartlist_t *sl);
|
|
|
|
|
2002-11-27 05:08:20 +01:00
|
|
|
extern or_options_t options; /* command-line and config-file options */
|
2002-09-04 08:29:28 +02:00
|
|
|
|
2003-04-08 08:44:38 +02:00
|
|
|
static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len);
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2003-11-11 04:01:48 +01:00
|
|
|
int decide_circ_id_type(char *local_nick, char *remote_nick) {
|
2003-10-01 03:49:53 +02:00
|
|
|
int result;
|
2003-11-16 18:00:02 +01:00
|
|
|
|
2003-10-01 03:49:53 +02:00
|
|
|
assert(remote_nick);
|
|
|
|
if(!local_nick)
|
2003-11-11 04:01:48 +01:00
|
|
|
return CIRC_ID_TYPE_LOWER;
|
2003-10-01 03:49:53 +02:00
|
|
|
result = strcmp(local_nick, remote_nick);
|
|
|
|
assert(result);
|
|
|
|
if(result < 0)
|
2003-11-11 04:01:48 +01:00
|
|
|
return CIRC_ID_TYPE_LOWER;
|
|
|
|
return CIRC_ID_TYPE_HIGHER;
|
2002-06-27 00:45:49 +02:00
|
|
|
}
|
|
|
|
|
2003-09-16 22:57:09 +02:00
|
|
|
struct onion_queue_t {
|
|
|
|
circuit_t *circ;
|
|
|
|
struct onion_queue_t *next;
|
|
|
|
};
|
|
|
|
|
2002-11-27 05:08:20 +01:00
|
|
|
/* 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;
|
|
|
|
|
2003-12-07 23:03:47 +01:00
|
|
|
tmp = tor_malloc(sizeof(struct onion_queue_t));
|
2002-11-27 05:08:20 +01:00
|
|
|
tmp->circ = circ;
|
2003-12-07 23:03:47 +01:00
|
|
|
tmp->next = NULL;
|
2002-11-27 05:08:20 +01:00
|
|
|
|
|
|
|
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) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"Already have %d onions queued. Closing.", ol_length);
|
2002-11-27 05:08:20 +01:00
|
|
|
free(tmp);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ol_length++;
|
|
|
|
ol_tail->next = tmp;
|
|
|
|
ol_tail = tmp;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2003-08-21 01:05:22 +02:00
|
|
|
circuit_t *onion_next_task(void) {
|
2003-09-14 10:17:14 +02:00
|
|
|
circuit_t *circ;
|
2002-11-27 05:08:20 +01:00
|
|
|
|
|
|
|
if(!ol_list)
|
2003-08-21 01:05:22 +02:00
|
|
|
return NULL; /* no onions pending, we're done */
|
2002-11-27 05:08:20 +01:00
|
|
|
|
2003-05-06 01:24:46 +02:00
|
|
|
assert(ol_list->circ);
|
2003-11-18 22:12:17 +01:00
|
|
|
assert(ol_list->circ->p_conn); /* make sure it's still valid */
|
2002-11-27 05:08:20 +01:00
|
|
|
assert(ol_length > 0);
|
2003-09-14 10:17:14 +02:00
|
|
|
circ = ol_list->circ;
|
|
|
|
onion_pending_remove(ol_list->circ);
|
|
|
|
return circ;
|
2002-11-27 05:08:20 +01:00
|
|
|
}
|
|
|
|
|
2002-12-03 23:18:23 +01:00
|
|
|
/* go through ol_list, find the onion_queue_t element which points to
|
|
|
|
* circ, remove and free that element. leave circ itself alone.
|
2002-11-27 05:08:20 +01:00
|
|
|
*/
|
|
|
|
void onion_pending_remove(circuit_t *circ) {
|
|
|
|
struct onion_queue_t *tmpo, *victim;
|
|
|
|
|
|
|
|
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) {
|
2003-11-11 04:01:48 +01:00
|
|
|
log_fn(LOG_DEBUG,"circ (p_circ_id %d) not in list, probably at cpuworker.",circ->p_circ_id);
|
2002-11-27 05:08:20 +01:00
|
|
|
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 */
|
|
|
|
|
2003-12-17 22:09:31 +01:00
|
|
|
free(victim);
|
2002-11-27 05:08:20 +01:00
|
|
|
}
|
|
|
|
|
2003-08-21 01:05:22 +02:00
|
|
|
/* given a response payload and keys, initialize, then send a created cell back */
|
2003-09-14 04:58:50 +02:00
|
|
|
int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) {
|
2003-05-06 01:24:46 +02:00
|
|
|
unsigned char iv[16];
|
2002-11-27 05:08:20 +01:00
|
|
|
cell_t cell;
|
|
|
|
|
2003-05-06 01:24:46 +02:00
|
|
|
memset(iv, 0, 16);
|
2002-10-03 00:54:20 +02:00
|
|
|
|
2003-05-06 01:24:46 +02:00
|
|
|
memset(&cell, 0, sizeof(cell_t));
|
|
|
|
cell.command = CELL_CREATED;
|
2003-11-11 04:01:48 +01:00
|
|
|
cell.circ_id = circ->p_circ_id;
|
2003-05-06 01:24:46 +02:00
|
|
|
|
|
|
|
circ->state = CIRCUIT_STATE_OPEN;
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2003-08-21 01:05:22 +02:00
|
|
|
log_fn(LOG_DEBUG,"Entering.");
|
2003-05-06 01:24:46 +02:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
memcpy(cell.payload, payload, ONIONSKIN_REPLY_LEN);
|
2002-06-27 00:45:49 +02:00
|
|
|
|
2004-03-18 20:22:56 +01:00
|
|
|
log_fn(LOG_INFO,"init digest forward 0x%.8x, backward 0x%.8x.",
|
|
|
|
(unsigned int)*(uint32_t*)(keys), (unsigned int)*(uint32_t*)(keys+20));
|
2003-12-17 06:58:30 +01:00
|
|
|
circ->n_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
|
|
|
|
crypto_digest_add_bytes(circ->n_digest, keys, 20);
|
|
|
|
circ->p_digest = crypto_new_digest_env(CRYPTO_SHA1_DIGEST);
|
|
|
|
crypto_digest_add_bytes(circ->p_digest, keys+20, 20);
|
|
|
|
|
2004-03-18 20:22:56 +01:00
|
|
|
log_fn(LOG_DEBUG,"init cipher forward 0x%.8x, backward 0x%.8x.",
|
|
|
|
(unsigned int)*(uint32_t*)(keys+40), (unsigned int)*(uint32_t*)(keys+40+16));
|
2003-05-06 01:24:46 +02:00
|
|
|
if (!(circ->n_crypto =
|
2003-12-17 06:58:30 +01:00
|
|
|
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40,iv,0))) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"Cipher initialization failed (n).");
|
2002-06-27 00:45:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2003-05-06 01:24:46 +02:00
|
|
|
if (!(circ->p_crypto =
|
2003-12-17 06:58:30 +01:00
|
|
|
crypto_create_init_cipher(CIRCUIT_CIPHER,keys+40+16,iv,1))) {
|
2003-10-10 03:48:32 +02:00
|
|
|
log_fn(LOG_WARN,"Cipher initialization failed (p).");
|
2002-06-27 00:45:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-10-09 20:45:14 +02:00
|
|
|
connection_or_write_cell_to_buf(&cell, circ->p_conn);
|
2003-08-21 01:05:22 +02:00
|
|
|
log_fn(LOG_DEBUG,"Finished sending 'created' cell.");
|
2002-06-27 00:45:49 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-02-29 02:31:33 +01:00
|
|
|
extern int has_fetched_directory;
|
|
|
|
|
2003-12-13 08:01:46 +01:00
|
|
|
static void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) {
|
|
|
|
char *start,*end;
|
|
|
|
char nick[MAX_NICKNAME_LEN];
|
|
|
|
routerinfo_t *router;
|
|
|
|
|
2004-03-19 21:50:12 +01:00
|
|
|
while(isspace((int)*list) || *list==',') list++;
|
2003-12-13 08:01:46 +01:00
|
|
|
|
|
|
|
start = list;
|
|
|
|
while(*start) {
|
2004-03-19 21:50:12 +01:00
|
|
|
end=start; while(*end && !isspace((int)*end) && *end != ',') end++;
|
2003-12-13 08:01:46 +01:00
|
|
|
memcpy(nick,start,end-start);
|
|
|
|
nick[end-start] = 0; /* null terminate it */
|
|
|
|
router = router_get_by_nickname(nick);
|
2004-02-29 02:31:33 +01:00
|
|
|
if (router) {
|
|
|
|
if (router->is_running)
|
|
|
|
smartlist_add(sl,router);
|
|
|
|
else
|
2004-03-03 04:02:06 +01:00
|
|
|
log_fn(LOG_INFO,"Nickname list includes '%s' which is known but down.",nick);
|
2004-02-29 02:31:33 +01:00
|
|
|
} else
|
|
|
|
log_fn(has_fetched_directory ? LOG_WARN : LOG_INFO,
|
|
|
|
"Nickname list includes '%s' which isn't a known router.",nick);
|
2004-03-19 21:50:12 +01:00
|
|
|
while(isspace((int)*end) || *end==',') end++;
|
2003-12-13 08:01:46 +01:00
|
|
|
start = end;
|
|
|
|
}
|
2003-11-12 20:34:34 +01:00
|
|
|
}
|
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
static int new_route_len(double cw, routerinfo_t **rarray, int rarray_len) {
|
2003-04-08 08:44:38 +02:00
|
|
|
int num_acceptable_routers;
|
2003-11-12 03:32:20 +01:00
|
|
|
int routelen;
|
2003-04-16 08:18:31 +02:00
|
|
|
|
2003-11-14 00:01:56 +01:00
|
|
|
assert((cw >= 0) && (cw < 1) && rarray); /* valid parameters */
|
2002-07-22 06:38:36 +02:00
|
|
|
|
2004-01-20 04:12:46 +01:00
|
|
|
#ifdef TOR_PERF
|
|
|
|
routelen = 2;
|
|
|
|
#else
|
2004-01-31 00:43:17 +01:00
|
|
|
routelen = 3;
|
|
|
|
#endif
|
|
|
|
#if 0
|
2004-01-20 04:12:46 +01:00
|
|
|
for(routelen = 3; ; routelen++) { /* 3, increment until coinflip says we're done */
|
2003-11-14 00:01:56 +01:00
|
|
|
if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */
|
|
|
|
break;
|
2002-08-23 05:35:44 +02:00
|
|
|
}
|
2004-01-20 04:12:46 +01:00
|
|
|
#endif
|
2003-11-12 03:32:20 +01:00
|
|
|
log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, rarray_len);
|
2002-08-23 05:35:44 +02:00
|
|
|
|
2003-04-08 08:44:38 +02:00
|
|
|
num_acceptable_routers = count_acceptable_routers(rarray, rarray_len);
|
|
|
|
|
2003-04-16 08:18:31 +02:00
|
|
|
if(num_acceptable_routers < 2) {
|
2004-03-26 23:28:16 +01:00
|
|
|
log_fn(LOG_INFO,"Not enough acceptable routers. Discarding this circuit.");
|
2003-11-12 03:32:20 +01:00
|
|
|
return -1;
|
2003-04-16 08:18:31 +02:00
|
|
|
}
|
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
if(num_acceptable_routers < routelen) {
|
2003-12-07 23:03:47 +01:00
|
|
|
log_fn(LOG_INFO,"Not enough routers: cutting routelen from %d to %d.",
|
|
|
|
routelen, num_acceptable_routers);
|
2003-11-12 03:32:20 +01:00
|
|
|
routelen = num_acceptable_routers;
|
2002-08-23 05:35:44 +02:00
|
|
|
}
|
2002-07-22 06:38:36 +02:00
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
return routelen;
|
Integrated onion proxy into or/
The 'or' process can now be told (by the global_role variable) what
roles this server should play -- connect to all ORs, listen for ORs,
listen for OPs, listen for APs, or any combination.
* everything in /src/op/ is now obsolete.
* connection_ap.c now handles all interactions with application proxies
* "port" is now or_port, op_port, ap_port. But routers are still always
referenced (say, in conn_get_by_addr_port()) by addr / or_port. We
should make routers.c actually read these new ports (currently I've
kludged it so op_port = or_port+10, ap_port=or_port+20)
* circuits currently know if they're at the beginning of the path because
circ->cpath is set. They use this instead for crypts (both ways),
if it's set.
* I still obey the "send a 0 back to the AP when you're ready" protocol,
but I think we should phase it out. I can simply not read from the AP
socket until I'm ready.
I need to do a lot of cleanup work here, but the code appears to work, so
now's a good time for a checkin.
svn:r22
2002-07-02 11:36:58 +02:00
|
|
|
}
|
|
|
|
|
2004-04-01 22:33:29 +02:00
|
|
|
static routerinfo_t *choose_good_exit_server_rend(routerlist_t *dir)
|
|
|
|
{
|
|
|
|
smartlist_t *sl, *excludednodes;
|
|
|
|
routerinfo_t *choice;
|
|
|
|
|
|
|
|
excludednodes = smartlist_create();
|
|
|
|
add_nickname_list_to_smartlist(excludednodes,options.RendExcludeNodes);
|
|
|
|
|
|
|
|
/* try the nodes in RendNodes first */
|
|
|
|
sl = smartlist_create();
|
|
|
|
add_nickname_list_to_smartlist(sl,options.RendNodes);
|
|
|
|
smartlist_subtract(sl,excludednodes);
|
|
|
|
choice = smartlist_choose(sl);
|
|
|
|
smartlist_free(sl);
|
|
|
|
if(!choice) {
|
|
|
|
sl = smartlist_create();
|
|
|
|
router_add_running_routers_to_smartlist(sl);
|
|
|
|
smartlist_subtract(sl,excludednodes);
|
|
|
|
choice = smartlist_choose(sl);
|
|
|
|
smartlist_free(sl);
|
|
|
|
}
|
|
|
|
smartlist_free(excludednodes);
|
|
|
|
if(!choice)
|
|
|
|
log_fn(LOG_WARN,"No available nodes when trying to choose rendezvous point. Failing.");
|
|
|
|
return choice;
|
|
|
|
}
|
|
|
|
|
|
|
|
static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir)
|
2003-11-16 06:33:45 +01:00
|
|
|
{
|
|
|
|
int *n_supported;
|
|
|
|
int i, j;
|
|
|
|
int n_pending_connections = 0;
|
|
|
|
connection_t **carray;
|
|
|
|
int n_connections;
|
|
|
|
int best_support = -1;
|
2004-02-17 08:45:44 +01:00
|
|
|
int n_best_support=0;
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_t *sl, *preferredexits, *excludedexits;
|
2003-12-13 02:43:21 +01:00
|
|
|
routerinfo_t *router;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-11-16 06:33:45 +01:00
|
|
|
get_connection_array(&carray, &n_connections);
|
|
|
|
|
2003-11-17 07:02:41 +01:00
|
|
|
/* Count how many connections are waiting for a circuit to be built.
|
|
|
|
* We use this for log messages now, but in the future we may depend on it.
|
|
|
|
*/
|
2003-11-16 06:33:45 +01:00
|
|
|
for (i = 0; i < n_connections; ++i) {
|
2003-12-03 10:50:02 +01:00
|
|
|
if (carray[i]->type == CONN_TYPE_AP &&
|
|
|
|
carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT &&
|
2003-12-13 00:03:25 +01:00
|
|
|
!carray[i]->marked_for_close &&
|
|
|
|
!circuit_stream_is_being_handled(carray[i]))
|
2003-11-16 06:33:45 +01:00
|
|
|
++n_pending_connections;
|
|
|
|
}
|
|
|
|
log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending",
|
|
|
|
n_pending_connections);
|
2004-02-17 08:45:44 +01:00
|
|
|
/* Now we count, for each of the routers in the directory, how many
|
|
|
|
* of the pending connections could possibly exit from that
|
|
|
|
* router (n_supported[i]). (We can't be sure about cases where we
|
|
|
|
* don't know the IP address of the pending connection.)
|
2003-11-17 07:02:41 +01:00
|
|
|
*/
|
2003-11-16 06:33:45 +01:00
|
|
|
n_supported = tor_malloc(sizeof(int)*dir->n_routers);
|
2003-12-03 09:06:55 +01:00
|
|
|
for (i = 0; i < dir->n_routers; ++i) { /* iterate over routers */
|
2003-11-19 02:24:19 +01:00
|
|
|
if(!dir->routers[i]->is_running) {
|
2004-02-17 08:45:44 +01:00
|
|
|
n_supported[i] = -1;
|
2003-12-03 10:50:02 +01:00
|
|
|
log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.",
|
|
|
|
dir->routers[i]->nickname, i);
|
2003-12-03 09:06:55 +01:00
|
|
|
continue; /* skip routers that are known to be down */
|
|
|
|
}
|
|
|
|
if(router_exit_policy_rejects_all(dir->routers[i])) {
|
2004-02-17 08:45:44 +01:00
|
|
|
n_supported[i] = -1;
|
2003-12-03 10:50:02 +01:00
|
|
|
log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.",
|
|
|
|
dir->routers[i]->nickname, i);
|
2003-12-03 09:06:55 +01:00
|
|
|
continue; /* skip routers that reject all */
|
2003-11-19 02:24:19 +01:00
|
|
|
}
|
2004-02-17 08:45:44 +01:00
|
|
|
n_supported[i] = 0;
|
2003-11-17 07:02:41 +01:00
|
|
|
for (j = 0; j < n_connections; ++j) { /* iterate over connections */
|
2003-12-03 09:06:55 +01:00
|
|
|
if (carray[j]->type != CONN_TYPE_AP ||
|
2003-12-03 10:50:02 +01:00
|
|
|
carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT ||
|
2003-12-13 00:03:25 +01:00
|
|
|
carray[j]->marked_for_close ||
|
|
|
|
circuit_stream_is_being_handled(carray[j]))
|
2003-11-18 10:53:03 +01:00
|
|
|
continue; /* Skip everything but APs in CIRCUIT_WAIT */
|
2003-12-17 22:09:31 +01:00
|
|
|
switch (connection_ap_can_use_exit(carray[j], dir->routers[i]))
|
2003-11-16 06:33:45 +01:00
|
|
|
{
|
2004-02-17 09:29:22 +01:00
|
|
|
case ADDR_POLICY_REJECTED:
|
2003-12-03 10:50:02 +01:00
|
|
|
log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.",
|
|
|
|
dir->routers[i]->nickname, i);
|
2003-11-18 10:53:03 +01:00
|
|
|
break; /* would be rejected; try next connection */
|
2004-02-17 09:29:22 +01:00
|
|
|
case ADDR_POLICY_ACCEPTED:
|
|
|
|
case ADDR_POLICY_UNKNOWN:
|
2003-11-16 06:33:45 +01:00
|
|
|
++n_supported[i];
|
2003-12-03 10:50:02 +01:00
|
|
|
log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
|
|
|
|
dir->routers[i]->nickname, i, n_supported[i]);
|
2003-11-16 06:33:45 +01:00
|
|
|
}
|
2003-11-17 07:02:41 +01:00
|
|
|
} /* End looping over connections. */
|
2003-12-17 22:09:31 +01:00
|
|
|
if (n_supported[i] > best_support) {
|
2003-11-17 07:02:41 +01:00
|
|
|
/* If this router is better than previous ones, remember its index
|
|
|
|
* and goodness, and start counting how many routers are this good. */
|
2004-02-17 09:42:25 +01:00
|
|
|
best_support = n_supported[i]; n_best_support=1;
|
2003-12-03 10:50:02 +01:00
|
|
|
log_fn(LOG_DEBUG,"%s is new best supported option so far.",
|
|
|
|
dir->routers[i]->nickname);
|
2003-11-16 06:33:45 +01:00
|
|
|
} else if (n_supported[i] == best_support) {
|
2003-11-17 07:02:41 +01:00
|
|
|
/* If this router is _as good_ as the best one, just increment the
|
|
|
|
* count of equally good routers.*/
|
2003-11-16 06:33:45 +01:00
|
|
|
++n_best_support;
|
|
|
|
}
|
|
|
|
}
|
2004-02-17 08:45:44 +01:00
|
|
|
log_fn(LOG_INFO, "Found %d servers that might support %d/%d pending connections.",
|
|
|
|
n_best_support, best_support, n_pending_connections);
|
2003-12-14 00:32:57 +01:00
|
|
|
|
2004-03-31 00:59:00 +02:00
|
|
|
preferredexits = smartlist_create();
|
2003-12-14 00:32:57 +01:00
|
|
|
add_nickname_list_to_smartlist(preferredexits,options.ExitNodes);
|
|
|
|
|
2004-03-31 00:59:00 +02:00
|
|
|
excludedexits = smartlist_create();
|
2004-02-28 06:09:37 +01:00
|
|
|
add_nickname_list_to_smartlist(excludedexits,options.ExcludeNodes);
|
2003-12-14 06:08:28 +01:00
|
|
|
|
2004-03-31 00:59:00 +02:00
|
|
|
sl = smartlist_create();
|
2003-12-14 06:08:28 +01:00
|
|
|
|
2003-11-17 07:02:41 +01:00
|
|
|
/* If any routers definitely support any pending connections, choose one
|
|
|
|
* at random. */
|
2003-11-19 02:24:19 +01:00
|
|
|
if (best_support > 0) {
|
2004-02-17 09:42:25 +01:00
|
|
|
for (i = 0; i < dir->n_routers; i++)
|
2003-12-14 00:32:57 +01:00
|
|
|
if (n_supported[i] == best_support)
|
2003-12-13 02:43:21 +01:00
|
|
|
smartlist_add(sl, dir->routers[i]);
|
|
|
|
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_subtract(sl,excludedexits);
|
2003-12-14 00:32:57 +01:00
|
|
|
if (smartlist_overlap(sl,preferredexits))
|
|
|
|
smartlist_intersect(sl,preferredexits);
|
2003-12-13 02:43:21 +01:00
|
|
|
router = smartlist_choose(sl);
|
2003-12-14 06:08:28 +01:00
|
|
|
} else {
|
|
|
|
/* Either there are no pending connections, or no routers even seem to
|
|
|
|
* possibly support any of them. Choose a router at random. */
|
2004-02-17 08:45:44 +01:00
|
|
|
if (best_support == -1) {
|
2004-02-04 23:35:58 +01:00
|
|
|
log(LOG_WARN, "All routers are down or middleman -- choosing a doomed exit at random.");
|
|
|
|
}
|
2004-02-17 09:42:25 +01:00
|
|
|
for(i = 0; i < dir->n_routers; i++)
|
2003-12-14 06:08:28 +01:00
|
|
|
if(n_supported[i] != -1)
|
|
|
|
smartlist_add(sl, dir->routers[i]);
|
2003-12-13 02:43:21 +01:00
|
|
|
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_subtract(sl,excludedexits);
|
|
|
|
if (smartlist_overlap(sl,preferredexits))
|
|
|
|
smartlist_intersect(sl,preferredexits);
|
|
|
|
router = smartlist_choose(sl);
|
|
|
|
}
|
2003-12-13 02:43:21 +01:00
|
|
|
|
2003-12-14 00:32:57 +01:00
|
|
|
smartlist_free(preferredexits);
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_free(excludedexits);
|
2003-12-13 02:43:21 +01:00
|
|
|
smartlist_free(sl);
|
2004-02-17 08:45:44 +01:00
|
|
|
tor_free(n_supported);
|
2003-12-13 02:43:21 +01:00
|
|
|
if(router) {
|
2004-02-28 08:48:28 +01:00
|
|
|
log_fn(LOG_INFO, "Chose exit server '%s'", router->nickname);
|
2003-12-13 02:43:21 +01:00
|
|
|
return router;
|
2003-11-19 02:24:19 +01:00
|
|
|
}
|
2003-12-13 02:43:21 +01:00
|
|
|
log_fn(LOG_WARN, "No exit routers seem to be running; can't choose an exit.");
|
2003-11-19 02:28:57 +01:00
|
|
|
return NULL;
|
2003-11-16 06:33:45 +01:00
|
|
|
}
|
|
|
|
|
2004-04-01 22:33:29 +02:00
|
|
|
static routerinfo_t *choose_good_exit_server(uint8_t purpose, routerlist_t *dir)
|
|
|
|
{
|
|
|
|
if(purpose == CIRCUIT_PURPOSE_C_GENERAL)
|
|
|
|
return choose_good_exit_server_general(dir);
|
|
|
|
else
|
|
|
|
return choose_good_exit_server_rend(dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
cpath_build_state_t *onion_new_cpath_build_state(uint8_t purpose,
|
|
|
|
const char *exit_nickname) {
|
2003-12-06 06:54:04 +01:00
|
|
|
routerlist_t *rl;
|
2003-11-14 21:45:47 +01:00
|
|
|
int r;
|
|
|
|
cpath_build_state_t *info;
|
2003-12-03 11:04:44 +01:00
|
|
|
routerinfo_t *exit;
|
|
|
|
|
2003-12-06 06:54:04 +01:00
|
|
|
router_get_routerlist(&rl);
|
|
|
|
r = new_route_len(options.PathlenCoinWeight, rl->routers, rl->n_routers);
|
2003-12-17 22:09:31 +01:00
|
|
|
if (r < 0)
|
2003-11-14 21:45:47 +01:00
|
|
|
return NULL;
|
2004-04-01 22:33:29 +02:00
|
|
|
info = tor_malloc_zero(sizeof(cpath_build_state_t));
|
2003-11-14 21:45:47 +01:00
|
|
|
info->desired_path_len = r;
|
2004-04-01 05:44:49 +02:00
|
|
|
if(exit_nickname) { /* the circuit-builder pre-requested one */
|
|
|
|
log_fn(LOG_INFO,"Using requested exit node '%s'", exit_nickname);
|
|
|
|
info->chosen_exit = tor_strdup(exit_nickname);
|
|
|
|
} else { /* we have to decide one */
|
2004-04-01 22:33:29 +02:00
|
|
|
exit = choose_good_exit_server(purpose, rl);
|
2004-04-01 05:44:49 +02:00
|
|
|
if(!exit) {
|
|
|
|
tor_free(info);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
info->chosen_exit = tor_strdup(exit->nickname);
|
|
|
|
}
|
2003-11-14 21:45:47 +01:00
|
|
|
return info;
|
2003-11-12 03:55:38 +01:00
|
|
|
}
|
|
|
|
|
2003-04-08 08:44:38 +02:00
|
|
|
static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) {
|
|
|
|
int i, j;
|
|
|
|
int num=0;
|
2003-04-16 08:18:31 +02:00
|
|
|
connection_t *conn;
|
2003-04-08 08:44:38 +02:00
|
|
|
|
|
|
|
for(i=0;i<rarray_len;i++) {
|
2003-09-26 12:03:50 +02:00
|
|
|
log_fn(LOG_DEBUG,"Contemplating whether router %d is a new option...",i);
|
2003-11-18 10:53:03 +01:00
|
|
|
if(rarray[i]->is_running == 0) {
|
|
|
|
log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i);
|
|
|
|
goto next_i_loop;
|
|
|
|
}
|
2003-11-20 18:49:45 +01:00
|
|
|
if(options.ORPort) {
|
2003-04-16 08:18:31 +02:00
|
|
|
conn = connection_exact_get_by_addr_port(rarray[i]->addr, rarray[i]->or_port);
|
|
|
|
if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) {
|
2003-09-26 12:03:50 +02:00
|
|
|
log_fn(LOG_DEBUG,"Nope, %d is not connected.",i);
|
2003-04-16 08:18:31 +02:00
|
|
|
goto next_i_loop;
|
|
|
|
}
|
2003-04-08 08:44:38 +02:00
|
|
|
}
|
|
|
|
for(j=0;j<i;j++) {
|
2003-09-25 07:17:11 +02:00
|
|
|
if(!crypto_pk_cmp_keys(rarray[i]->onion_pkey, rarray[j]->onion_pkey)) {
|
2003-04-08 08:44:38 +02:00
|
|
|
/* these guys are twins. so we've already counted him. */
|
2003-09-26 12:03:50 +02:00
|
|
|
log_fn(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j);
|
2003-04-08 08:44:38 +02:00
|
|
|
goto next_i_loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
num++;
|
2003-09-26 12:03:50 +02:00
|
|
|
log_fn(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num);
|
2003-04-08 08:44:38 +02:00
|
|
|
next_i_loop:
|
|
|
|
; /* our compiler may need an explicit statement after the label */
|
|
|
|
}
|
|
|
|
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
2003-12-13 08:01:46 +01:00
|
|
|
static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) {
|
|
|
|
int i;
|
|
|
|
routerinfo_t *r;
|
|
|
|
|
|
|
|
if(twin == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* XXX abstraction violation: this function reaches inside smartlist :( */
|
|
|
|
for(i=0; i < sl->num_used; i++) {
|
|
|
|
r = sl->list[i];
|
|
|
|
if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) {
|
|
|
|
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
|
|
|
|
i--; /* so we process the new i'th element */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-14 21:45:47 +01:00
|
|
|
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, routerinfo_t **router_out)
|
2003-11-12 03:32:20 +01:00
|
|
|
{
|
|
|
|
int cur_len;
|
|
|
|
crypt_path_t *cpath, *hop;
|
2003-12-03 11:28:51 +01:00
|
|
|
routerinfo_t *r;
|
2003-11-16 06:33:45 +01:00
|
|
|
routerinfo_t *choice;
|
2003-11-12 03:32:20 +01:00
|
|
|
int i;
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_t *sl, *excludednodes;
|
2003-05-02 00:55:51 +02:00
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
assert(head_ptr);
|
2003-11-12 20:34:34 +01:00
|
|
|
assert(router_out);
|
2002-11-23 07:49:01 +01:00
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
if (!*head_ptr) {
|
|
|
|
cur_len = 0;
|
|
|
|
} else {
|
|
|
|
cur_len = 1;
|
|
|
|
for (cpath = *head_ptr; cpath->next != *head_ptr; cpath = cpath->next) {
|
|
|
|
++cur_len;
|
|
|
|
}
|
2002-11-23 07:49:01 +01:00
|
|
|
}
|
2003-12-17 22:09:31 +01:00
|
|
|
if (cur_len >= state->desired_path_len) {
|
|
|
|
log_fn(LOG_DEBUG, "Path is complete: %d steps long",
|
2003-11-14 21:45:47 +01:00
|
|
|
state->desired_path_len);
|
2003-12-17 22:09:31 +01:00
|
|
|
return 1;
|
2003-11-14 21:45:47 +01:00
|
|
|
}
|
2003-12-13 08:01:46 +01:00
|
|
|
log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len,
|
2003-11-14 21:45:47 +01:00
|
|
|
state->desired_path_len);
|
2003-05-02 00:55:51 +02:00
|
|
|
|
2004-03-31 00:59:00 +02:00
|
|
|
excludednodes = smartlist_create();
|
2004-02-28 06:09:37 +01:00
|
|
|
add_nickname_list_to_smartlist(excludednodes,options.ExcludeNodes);
|
2003-12-14 06:08:28 +01:00
|
|
|
|
2003-12-13 08:01:46 +01:00
|
|
|
if(cur_len == state->desired_path_len - 1) { /* Picking last node */
|
2004-01-30 20:31:39 +01:00
|
|
|
log_fn(LOG_DEBUG, "Contemplating last hop: choice already made: %s",
|
|
|
|
state->chosen_exit);
|
2003-11-16 06:33:45 +01:00
|
|
|
choice = router_get_by_nickname(state->chosen_exit);
|
2004-01-30 22:32:40 +01:00
|
|
|
smartlist_free(excludednodes);
|
2003-12-03 11:28:51 +01:00
|
|
|
if(!choice) {
|
2004-03-26 23:28:16 +01:00
|
|
|
log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Discarding this circuit.",
|
2003-12-03 11:28:51 +01:00
|
|
|
state->chosen_exit);
|
|
|
|
return -1;
|
|
|
|
}
|
2003-12-13 08:01:46 +01:00
|
|
|
} else if(cur_len == 0) { /* picking first node */
|
|
|
|
/* try the nodes in EntryNodes first */
|
2004-03-31 00:59:00 +02:00
|
|
|
sl = smartlist_create();
|
2003-12-13 08:01:46 +01:00
|
|
|
add_nickname_list_to_smartlist(sl,options.EntryNodes);
|
2003-12-14 07:50:44 +01:00
|
|
|
/* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */
|
2003-12-13 08:01:46 +01:00
|
|
|
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_subtract(sl,excludednodes);
|
2003-12-13 08:01:46 +01:00
|
|
|
choice = smartlist_choose(sl);
|
|
|
|
smartlist_free(sl);
|
|
|
|
if(!choice) {
|
2004-03-31 00:59:00 +02:00
|
|
|
sl = smartlist_create();
|
2003-12-13 08:01:46 +01:00
|
|
|
router_add_running_routers_to_smartlist(sl);
|
|
|
|
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_subtract(sl,excludednodes);
|
2003-12-13 08:01:46 +01:00
|
|
|
choice = smartlist_choose(sl);
|
|
|
|
smartlist_free(sl);
|
|
|
|
}
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_free(excludednodes);
|
2003-12-13 08:01:46 +01:00
|
|
|
if(!choice) {
|
2004-03-26 23:28:16 +01:00
|
|
|
log_fn(LOG_WARN,"No acceptable routers while picking entry node. Discarding this circuit.");
|
2003-12-13 08:01:46 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2003-11-16 06:33:45 +01:00
|
|
|
} else {
|
|
|
|
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
|
2004-03-31 00:59:00 +02:00
|
|
|
sl = smartlist_create();
|
2003-12-13 08:01:46 +01:00
|
|
|
router_add_running_routers_to_smartlist(sl);
|
|
|
|
remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
|
|
|
|
for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) {
|
|
|
|
r = router_get_by_addr_port(cpath->addr, cpath->port);
|
|
|
|
assert(r);
|
|
|
|
remove_twins_from_smartlist(sl,r);
|
|
|
|
}
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_subtract(sl,excludednodes);
|
2003-12-13 08:01:46 +01:00
|
|
|
choice = smartlist_choose(sl);
|
|
|
|
smartlist_free(sl);
|
2003-12-14 06:08:28 +01:00
|
|
|
smartlist_free(excludednodes);
|
2003-12-03 11:28:51 +01:00
|
|
|
if(!choice) {
|
2004-03-26 23:28:16 +01:00
|
|
|
log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Discarding this circuit.");
|
2003-12-03 11:28:51 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2003-11-12 20:34:34 +01:00
|
|
|
}
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-12-13 08:01:46 +01:00
|
|
|
log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
|
|
|
|
choice->nickname, cur_len, state->chosen_exit);
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-11-18 09:20:19 +01:00
|
|
|
hop = (crypt_path_t *)tor_malloc_zero(sizeof(crypt_path_t));
|
2003-11-12 03:32:20 +01:00
|
|
|
|
|
|
|
/* link hop into the cpath, at the end. */
|
|
|
|
if (*head_ptr) {
|
|
|
|
hop->next = (*head_ptr);
|
|
|
|
hop->prev = (*head_ptr)->prev;
|
|
|
|
(*head_ptr)->prev->next = hop;
|
|
|
|
(*head_ptr)->prev = hop;
|
|
|
|
} else {
|
|
|
|
*head_ptr = hop;
|
|
|
|
hop->prev = hop->next = hop;
|
2002-11-23 07:49:01 +01:00
|
|
|
}
|
Integrated onion proxy into or/
The 'or' process can now be told (by the global_role variable) what
roles this server should play -- connect to all ORs, listen for ORs,
listen for OPs, listen for APs, or any combination.
* everything in /src/op/ is now obsolete.
* connection_ap.c now handles all interactions with application proxies
* "port" is now or_port, op_port, ap_port. But routers are still always
referenced (say, in conn_get_by_addr_port()) by addr / or_port. We
should make routers.c actually read these new ports (currently I've
kludged it so op_port = or_port+10, ap_port=or_port+20)
* circuits currently know if they're at the beginning of the path because
circ->cpath is set. They use this instead for crypts (both ways),
if it's set.
* I still obey the "send a 0 back to the AP when you're ready" protocol,
but I think we should phase it out. I can simply not read from the AP
socket until I'm ready.
I need to do a lot of cleanup work here, but the code appears to work, so
now's a good time for a checkin.
svn:r22
2002-07-02 11:36:58 +02:00
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
hop->state = CPATH_STATE_CLOSED;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-11-16 06:33:45 +01:00
|
|
|
hop->port = choice->or_port;
|
|
|
|
hop->addr = choice->addr;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-11-12 03:32:20 +01:00
|
|
|
hop->package_window = CIRCWINDOW_START;
|
|
|
|
hop->deliver_window = CIRCWINDOW_START;
|
2002-11-23 07:49:01 +01:00
|
|
|
|
2003-12-17 22:09:31 +01:00
|
|
|
log_fn(LOG_DEBUG, "Extended circuit path with %s for hop %d",
|
2003-11-16 06:33:45 +01:00
|
|
|
choice->nickname, cur_len);
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-11-16 06:33:45 +01:00
|
|
|
*router_out = choice;
|
2003-11-12 03:32:20 +01:00
|
|
|
return 0;
|
2003-04-16 18:19:27 +02:00
|
|
|
}
|
|
|
|
|
2003-05-01 21:42:51 +02:00
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
/* Given a router's 128 byte public key,
|
|
|
|
stores the following in onion_skin_out:
|
|
|
|
[16 bytes] Symmetric key for encrypting blob past RSA
|
|
|
|
[112 bytes] g^x part 1 (inside the RSA)
|
|
|
|
[16 bytes] g^x part 2 (symmetrically encrypted)
|
|
|
|
[ 6 bytes] Meeting point (IP/port)
|
|
|
|
[ 8 bytes] Meeting cookie
|
|
|
|
[16 bytes] End-to-end authentication [optional]
|
|
|
|
|
|
|
|
* Stores the DH private key into handshake_state_out for later completion
|
|
|
|
* of the handshake.
|
2003-05-01 21:42:51 +02:00
|
|
|
*
|
2003-12-16 09:21:58 +01:00
|
|
|
* The meeting point/cookies and auth are zeroed out for now.
|
2003-05-01 21:42:51 +02:00
|
|
|
*/
|
|
|
|
int
|
2003-05-06 01:24:46 +02:00
|
|
|
onion_skin_create(crypto_pk_env_t *dest_router_key,
|
2003-05-01 21:42:51 +02:00
|
|
|
crypto_dh_env_t **handshake_state_out,
|
2003-12-16 09:21:58 +01:00
|
|
|
char *onion_skin_out) /* Must be ONIONSKIN_CHALLENGE_LEN bytes */
|
2003-05-01 21:42:51 +02:00
|
|
|
{
|
|
|
|
char iv[16];
|
2003-12-16 09:21:58 +01:00
|
|
|
char *challenge = NULL;
|
2003-05-01 21:42:51 +02:00
|
|
|
crypto_dh_env_t *dh = NULL;
|
|
|
|
crypto_cipher_env_t *cipher = NULL;
|
|
|
|
int dhbytes, pkbytes;
|
|
|
|
|
|
|
|
*handshake_state_out = NULL;
|
2003-12-16 09:21:58 +01:00
|
|
|
memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
|
2003-05-05 06:27:00 +02:00
|
|
|
memset(iv, 0, 16);
|
2003-05-01 21:42:51 +02:00
|
|
|
|
|
|
|
if (!(dh = crypto_dh_new()))
|
|
|
|
goto err;
|
2003-12-16 09:21:58 +01:00
|
|
|
|
2003-05-01 21:42:51 +02:00
|
|
|
dhbytes = crypto_dh_get_bytes(dh);
|
2003-05-06 01:24:46 +02:00
|
|
|
pkbytes = crypto_pk_keysize(dest_router_key);
|
2003-12-16 09:21:58 +01:00
|
|
|
assert(dhbytes == 128);
|
|
|
|
assert(pkbytes == 128);
|
|
|
|
challenge = (char *)tor_malloc_zero(ONIONSKIN_CHALLENGE_LEN);
|
2003-05-01 21:42:51 +02:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if (crypto_rand(16, challenge))
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
2003-12-09 02:04:40 +01:00
|
|
|
|
|
|
|
/* You can't just run around RSA-encrypting any bitstream: if it's
|
|
|
|
* greater than the RSA key, then OpenSSL will happily encrypt,
|
|
|
|
* and later decrypt to the wrong value. So we set the first bit
|
2003-12-16 09:21:58 +01:00
|
|
|
* of 'challenge' to 0. This means that our symmetric key is really
|
|
|
|
* only 127 bits.
|
2003-05-05 06:27:00 +02:00
|
|
|
*/
|
2003-12-17 22:09:31 +01:00
|
|
|
challenge[0] &= 0x7f;
|
2003-05-01 21:42:51 +02:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if (crypto_dh_get_public(dh, challenge+16, dhbytes))
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
#define PA(a,n) \
|
|
|
|
{ int _i; for (_i = 0; _i<n; ++_i) printf("%02x ",((int)(a)[_i])&0xFF); }
|
|
|
|
|
|
|
|
printf("Client: client g^x:");
|
2003-12-16 09:21:58 +01:00
|
|
|
PA(challenge+16,3);
|
2003-06-13 23:23:14 +02:00
|
|
|
printf("...");
|
2003-12-16 09:21:58 +01:00
|
|
|
PA(challenge+141,3);
|
2003-06-13 23:23:14 +02:00
|
|
|
puts("");
|
|
|
|
|
|
|
|
printf("Client: client symkey:");
|
2003-12-16 09:21:58 +01:00
|
|
|
PA(challenge+0,16);
|
2003-06-13 23:23:14 +02:00
|
|
|
puts("");
|
2003-05-05 06:27:00 +02:00
|
|
|
#endif
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
/* set meeting point, meeting cookie, etc here. Leave zero for now. */
|
|
|
|
|
|
|
|
cipher = crypto_create_init_cipher(ONION_CIPHER, challenge, iv, 1);
|
2003-05-05 06:27:00 +02:00
|
|
|
|
|
|
|
if (!cipher)
|
|
|
|
goto err;
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if (crypto_pk_public_encrypt(dest_router_key, challenge, pkbytes,
|
2003-05-05 06:27:00 +02:00
|
|
|
onion_skin_out, RSA_NO_PADDING)==-1)
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if (crypto_cipher_encrypt(cipher, challenge+pkbytes, ONIONSKIN_CHALLENGE_LEN-pkbytes,
|
2003-05-01 21:42:51 +02:00
|
|
|
onion_skin_out+pkbytes))
|
|
|
|
goto err;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
tor_free(challenge);
|
2003-05-01 21:42:51 +02:00
|
|
|
crypto_free_cipher_env(cipher);
|
|
|
|
*handshake_state_out = dh;
|
2003-05-05 06:27:00 +02:00
|
|
|
|
2003-05-01 21:42:51 +02:00
|
|
|
return 0;
|
|
|
|
err:
|
2003-12-16 09:21:58 +01:00
|
|
|
tor_free(challenge);
|
2003-05-01 21:42:51 +02:00
|
|
|
if (dh) crypto_dh_free(dh);
|
|
|
|
if (cipher) crypto_free_cipher_env(cipher);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given an encrypted DH public key as generated by onion_skin_create,
|
2003-12-16 09:21:58 +01:00
|
|
|
* and the private key for this onion router, generate the reply (128-byte
|
|
|
|
* DH plus the first 20 bytes of shared key material), and store the
|
|
|
|
* next key_out_len bytes of key material in key_out.
|
2003-05-01 21:42:51 +02:00
|
|
|
*/
|
|
|
|
int
|
2003-12-16 09:21:58 +01:00
|
|
|
onion_skin_server_handshake(char *onion_skin, /* ONIONSKIN_CHALLENGE_LEN bytes */
|
2003-05-01 21:42:51 +02:00
|
|
|
crypto_pk_env_t *private_key,
|
2003-12-16 09:21:58 +01:00
|
|
|
char *handshake_reply_out, /* ONIONSKIN_REPLY_LEN bytes */
|
2003-05-01 21:42:51 +02:00
|
|
|
char *key_out,
|
|
|
|
int key_out_len)
|
|
|
|
{
|
2003-12-16 09:21:58 +01:00
|
|
|
char challenge[ONIONSKIN_CHALLENGE_LEN];
|
2003-05-01 21:42:51 +02:00
|
|
|
char iv[16];
|
|
|
|
crypto_dh_env_t *dh = NULL;
|
|
|
|
crypto_cipher_env_t *cipher = NULL;
|
|
|
|
int pkbytes;
|
2003-06-14 03:30:53 +02:00
|
|
|
int len;
|
2003-12-16 09:21:58 +01:00
|
|
|
char *key_material=NULL;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-05-01 21:42:51 +02:00
|
|
|
memset(iv, 0, 16);
|
|
|
|
pkbytes = crypto_pk_keysize(private_key);
|
|
|
|
|
|
|
|
if (crypto_pk_private_decrypt(private_key,
|
|
|
|
onion_skin, pkbytes,
|
2003-12-16 09:21:58 +01:00
|
|
|
challenge, RSA_NO_PADDING) == -1)
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
printf("Server: client symkey:");
|
|
|
|
PA(buf+0,16);
|
|
|
|
puts("");
|
2003-05-05 06:27:00 +02:00
|
|
|
#endif
|
2003-05-01 21:42:51 +02:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
cipher = crypto_create_init_cipher(ONION_CIPHER, challenge, iv, 0);
|
2003-05-01 21:42:51 +02:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if (crypto_cipher_decrypt(cipher, onion_skin+pkbytes, ONIONSKIN_CHALLENGE_LEN-pkbytes,
|
|
|
|
challenge+pkbytes))
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
2003-05-05 06:27:00 +02:00
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
printf("Server: client g^x:");
|
|
|
|
PA(buf+16,3);
|
|
|
|
printf("...");
|
|
|
|
PA(buf+141,3);
|
|
|
|
puts("");
|
2003-05-05 06:27:00 +02:00
|
|
|
#endif
|
2003-12-16 09:21:58 +01:00
|
|
|
|
2003-05-01 21:42:51 +02:00
|
|
|
dh = crypto_dh_new();
|
2003-05-06 07:54:42 +02:00
|
|
|
if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN))
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
printf("Server: server g^y:");
|
|
|
|
PA(handshake_reply_out+0,3);
|
|
|
|
printf("...");
|
|
|
|
PA(handshake_reply_out+125,3);
|
|
|
|
puts("");
|
|
|
|
#endif
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
key_material = tor_malloc(20+key_out_len);
|
|
|
|
len = crypto_dh_compute_secret(dh, challenge+16, DH_KEY_LEN,
|
|
|
|
key_material, 20+key_out_len);
|
2003-06-14 03:30:53 +02:00
|
|
|
if (len < 0)
|
2003-05-01 21:42:51 +02:00
|
|
|
goto err;
|
|
|
|
|
2004-01-05 06:23:03 +01:00
|
|
|
/* send back H(K|0) as proof that we learned K. */
|
2003-12-16 09:21:58 +01:00
|
|
|
memcpy(handshake_reply_out+DH_KEY_LEN, key_material, 20);
|
|
|
|
|
|
|
|
/* use the rest of the key material for our shared keys, digests, etc */
|
|
|
|
memcpy(key_out, key_material+20, key_out_len);
|
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
2003-06-14 03:34:39 +02:00
|
|
|
printf("Server: key material:");
|
|
|
|
PA(buf, DH_KEY_LEN);
|
|
|
|
puts("");
|
2003-06-13 23:23:14 +02:00
|
|
|
printf("Server: keys out:");
|
|
|
|
PA(key_out, key_out_len);
|
|
|
|
puts("");
|
|
|
|
#endif
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
tor_free(key_material);
|
2003-05-01 21:42:51 +02:00
|
|
|
crypto_free_cipher_env(cipher);
|
|
|
|
crypto_dh_free(dh);
|
|
|
|
return 0;
|
|
|
|
err:
|
2003-12-16 09:21:58 +01:00
|
|
|
tor_free(key_material);
|
2003-05-01 21:42:51 +02:00
|
|
|
if (cipher) crypto_free_cipher_env(cipher);
|
|
|
|
if (dh) crypto_dh_free(dh);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finish the client side of the DH handshake.
|
2003-12-16 09:21:58 +01:00
|
|
|
* Given the 128 byte DH reply + 20 byte hash as generated by
|
|
|
|
* onion_skin_server_handshake and the handshake state generated by
|
|
|
|
* onion_skin_create, verify H(K) with the first 20 bytes of shared
|
|
|
|
* key material, then generate key_out_len more bytes of shared key
|
|
|
|
* material and store them in key_out.
|
2003-05-01 21:42:51 +02:00
|
|
|
*
|
|
|
|
* After the invocation, call crypto_dh_free on handshake_state.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
|
2003-12-16 09:21:58 +01:00
|
|
|
char *handshake_reply, /* Must be ONIONSKIN_REPLY_LEN bytes */
|
2003-05-01 21:42:51 +02:00
|
|
|
char *key_out,
|
2003-12-16 09:21:58 +01:00
|
|
|
int key_out_len)
|
2003-05-01 21:42:51 +02:00
|
|
|
{
|
2003-06-14 03:30:53 +02:00
|
|
|
int len;
|
2003-12-16 09:21:58 +01:00
|
|
|
char *key_material=NULL;
|
2003-05-06 07:54:42 +02:00
|
|
|
assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
printf("Client: server g^y:");
|
|
|
|
PA(handshake_reply+0,3);
|
|
|
|
printf("...");
|
|
|
|
PA(handshake_reply+125,3);
|
|
|
|
puts("");
|
|
|
|
#endif
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
key_material = tor_malloc(20+key_out_len);
|
2003-06-14 03:30:53 +02:00
|
|
|
len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN,
|
2003-12-16 09:21:58 +01:00
|
|
|
key_material, 20+key_out_len);
|
2003-06-14 03:30:53 +02:00
|
|
|
if (len < 0)
|
2003-05-01 21:42:51 +02:00
|
|
|
return -1;
|
2003-12-07 23:03:47 +01:00
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
if(memcmp(key_material, handshake_reply+DH_KEY_LEN, 20)) {
|
|
|
|
/* H(K) does *not* match. Something fishy. */
|
|
|
|
tor_free(key_material);
|
|
|
|
log_fn(LOG_WARN,"Digest DOES NOT MATCH on onion handshake. Bug or attack.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use the rest of the key material for our shared keys, digests, etc */
|
|
|
|
memcpy(key_out, key_material+20, key_out_len);
|
|
|
|
|
2003-06-13 23:23:14 +02:00
|
|
|
#ifdef DEBUG_ONION_SKINS
|
|
|
|
printf("Client: keys out:");
|
|
|
|
PA(key_out, key_out_len);
|
|
|
|
puts("");
|
|
|
|
#endif
|
|
|
|
|
2003-12-16 09:21:58 +01:00
|
|
|
tor_free(key_material);
|
2003-05-01 21:42:51 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-04-07 04:12:02 +02:00
|
|
|
/*
|
|
|
|
Local Variables:
|
|
|
|
mode:c
|
|
|
|
indent-tabs-mode:nil
|
|
|
|
c-basic-offset:2
|
|
|
|
End:
|
|
|
|
*/
|