routers.c 38.5 KB
Newer Older
1
/* Copyright 2001-2003 Roger Dingledine, Matej Pfajfar. */
2
3
4
/* See LICENSE for licensing information */
/* $Id$ */

5
#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n"
6
#define OR_PUBLICKEY_END_TAG "-----END RSA PUBLIC KEY-----\n"
7
8
#define OR_SIGNATURE_BEGIN_TAG "-----BEGIN SIGNATURE-----\n"
#define OR_SIGNATURE_END_TAG "-----END SIGNATURE-----\n"
Roger Dingledine's avatar
Roger Dingledine committed
9

10
11
12
13
14
#define _GNU_SOURCE
/* XXX this is required on rh7 to make strptime not complain. how bad
 * is this for portability?
 */

Roger Dingledine's avatar
Roger Dingledine committed
15
16
#include "or.h"

17
18
/****************************************************************************/

Roger Dingledine's avatar
Roger Dingledine committed
19
static routerlist_t *routerlist = NULL; /* router array */
20
21
static routerinfo_t *desc_routerinfo = NULL; /* my descriptor */
static char descriptor[8192]; /* string representation of my descriptor */
22
23
24
25

extern or_options_t options; /* command-line and config-file options */

/****************************************************************************/
26

27
28
29
struct directory_token;
typedef struct directory_token directory_token_t;

30
/* static function prototypes */
31
static int router_add_exit_policy_from_string(routerinfo_t *router, char *s);
32
33
static int router_add_exit_policy(routerinfo_t *router, 
                                  directory_token_t *tok);
Roger Dingledine's avatar
Roger Dingledine committed
34
static int router_resolve_routerlist(routerlist_t *dir);
35

36
37
/****************************************************************************/

38
void router_retry_connections(void) {
39
40
41
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
42
43
44
45
  for (i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
    if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) { 
      /* not in the list */
46
      log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port);
47
      connection_or_connect(router);
48
49
50
51
52
    }
  }
}

routerinfo_t *router_pick_directory_server(void) {
53
54
  /* pick a random running router with a positive dir_port */
  int i,j;
55
  routerinfo_t *router, *dirserver=NULL;
56
57
  int num_dirservers=0;

Roger Dingledine's avatar
Roger Dingledine committed
58
  if(!routerlist)
59
60
    return NULL;

Roger Dingledine's avatar
Roger Dingledine committed
61
62
  for(i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
63
    if(router->dir_port > 0 && router->is_running)
64
      num_dirservers++;
65
66
  }

67
68
69
70
  if(!num_dirservers) {
    log_fn(LOG_INFO,"No dirservers are reachable. Trying them all again.");
    /* no running dir servers found? go through and mark them all as up,
     * and we'll cycle through the list again. */
Roger Dingledine's avatar
Roger Dingledine committed
71
72
    for(i=0;i<routerlist->n_routers;i++) {
      router = routerlist->routers[i];
73
74
75
76
      if(router->dir_port > 0) {
        router->is_running = 1;
        dirserver = router;
      }
77
    }
78
    return dirserver;
79
80
  }

81
  j = crypto_pseudo_rand_int(num_dirservers);
Roger Dingledine's avatar
Roger Dingledine committed
82
83
  for (i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
84
85
86
87
88
89
90
91
92
93
94
    if (router->dir_port > 0 && router->is_running) {
      if (j)
        --j;
      else {
        log_fn(LOG_DEBUG, "Chose server '%s'", router->nickname);
        return router;
      }
    }
  }
  assert(0);
  return NULL;
95
96
}

97
98
99
100
routerinfo_t *router_pick_randomly_from_running(void) {
  int i,j;
  int num_running=0;

Roger Dingledine's avatar
Roger Dingledine committed
101
  if(!routerlist)
102
103
    return NULL;

Roger Dingledine's avatar
Roger Dingledine committed
104
105
  for(i=0;i<routerlist->n_routers;i++) {
    if(routerlist->routers[i]->is_running)
106
107
108
109
110
111
112
113
      num_running++;
  }

  if(!num_running) {
    log_fn(LOG_INFO,"No routers are running. Returning NULL.");
    return NULL;
  }
  j = crypto_pseudo_rand_int(num_running);
Roger Dingledine's avatar
Roger Dingledine committed
114
115
  for (i=0;i<routerlist->n_routers;i++) {
    if (routerlist->routers[i]->is_running) {
116
117
118
      if (j)
        --j;
      else {
Roger Dingledine's avatar
Roger Dingledine committed
119
120
        log_fn(LOG_DEBUG, "Chose server '%s'", routerlist->routers[i]->nickname);
        return routerlist->routers[i];
121
122
123
124
125
126
127
      }
    }
  }
  assert(0);
  return NULL;
}

128
129
130
131
void router_upload_desc_to_dirservers(void) {
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
132
  if(!routerlist)
133
134
    return;

135
  if (!router_get_my_descriptor()) {
Roger Dingledine's avatar
Roger Dingledine committed
136
    log_fn(LOG_WARN, "No descriptor; skipping upload");
137
138
139
    return;
  }

Roger Dingledine's avatar
Roger Dingledine committed
140
141
  for(i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
142
143
144
145
146
    if(router->dir_port > 0)
      directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD);
  }
}

147
148
149
150
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
151
  assert(routerlist);
152

Roger Dingledine's avatar
Roger Dingledine committed
153
154
  for(i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
155
156
157
158
159
160
    if ((router->addr == addr) && (router->or_port == port))
      return router;
  }
  return NULL;
}

161
routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk) 
162
163
164
165
{
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
166
  assert(routerlist);
167

Roger Dingledine's avatar
Roger Dingledine committed
168
169
  for(i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
170
    if (0 == crypto_pk_cmp_keys(router->link_pkey, pk))
171
172
173
174
      return router;
  }
  return NULL;
}
175

176
routerinfo_t *router_get_by_nickname(char *nickname)
177
178
179
180
{
  int i;
  routerinfo_t *router;

Roger Dingledine's avatar
Roger Dingledine committed
181
  assert(routerlist);
182

Roger Dingledine's avatar
Roger Dingledine committed
183
184
  for(i=0;i<routerlist->n_routers;i++) {
    router = routerlist->routers[i];
185
    if (0 == strcmp(router->nickname, nickname))
186
187
188
189
      return router;
  }
  return NULL;
}
190

Roger Dingledine's avatar
Roger Dingledine committed
191
192
193
/* a way to access routerlist outside this file */
void router_get_routerlist(routerlist_t **prouterlist) {
  *prouterlist = routerlist;
194
195
}

196
/* delete a router from memory */
197
void routerinfo_free(routerinfo_t *router)
Roger Dingledine's avatar
Roger Dingledine committed
198
{
199
  struct exit_policy_t *e;
Roger Dingledine's avatar
Roger Dingledine committed
200

201
  if (!router)
Roger Dingledine's avatar
Roger Dingledine committed
202
203
    return;

204
205
  tor_free(router->address);
  tor_free(router->nickname);
206
207
208
209
210
211
  if (router->onion_pkey)
    crypto_free_pk_env(router->onion_pkey);
  if (router->link_pkey)
    crypto_free_pk_env(router->link_pkey);
  if (router->identity_pkey)
    crypto_free_pk_env(router->identity_pkey);
212
213
214
  while (router->exit_policy) {
    e = router->exit_policy;
    router->exit_policy = e->next;
215
    tor_free(e->string);
216
217
218
    free(e);
  }
  free(router);
219
220
}

Roger Dingledine's avatar
Roger Dingledine committed
221
void routerlist_free(routerlist_t *rl)
222
{
223
  int i;
Roger Dingledine's avatar
Roger Dingledine committed
224
225
226
227
228
  for (i = 0; i < rl->n_routers; ++i)
    routerinfo_free(rl->routers[i]);
  tor_free(rl->routers);
  tor_free(rl->software_versions);
  free(rl);
229
230
}

231
232
void router_mark_as_down(char *nickname) {
  routerinfo_t *router = router_get_by_nickname(nickname);
233
234
  if(!router) /* we don't seem to know about him in the first place */
    return;
235
236
  log_fn(LOG_DEBUG,"Marking %s as down.",router->nickname);
  router->is_running = 0;
237
238
}

Roger Dingledine's avatar
Roger Dingledine committed
239
/* load the router list */
Roger Dingledine's avatar
Roger Dingledine committed
240
int router_set_routerlist_from_file(char *routerfile)
Roger Dingledine's avatar
Roger Dingledine committed
241
{
242
  char *string;
Roger Dingledine's avatar
Roger Dingledine committed
243

244
245
  string = read_file_to_str(routerfile);
  if(!string) {
Roger Dingledine's avatar
Roger Dingledine committed
246
    log_fn(LOG_WARN,"Failed to load routerfile %s.",routerfile);
247
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
248
  }
249

Roger Dingledine's avatar
Roger Dingledine committed
250
  if(router_set_routerlist_from_string(string) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
251
    log_fn(LOG_WARN,"The routerfile itself was corrupt.");
252
253
254
255
256
257
    free(string);
    return -1;
  }

  free(string);
  return 0;
258
}
259

260
261
262
typedef enum {
  K_ACCEPT,
  K_DIRECTORY_SIGNATURE,
263
  K_RECOMMENDED_SOFTWARE,
Roger Dingledine's avatar
Roger Dingledine committed
264
265
  K_REJECT,
  K_ROUTER,
266
267
  K_SIGNED_DIRECTORY,
  K_SIGNING_KEY,
268
269
270
  K_ONION_KEY,
  K_LINK_KEY,
  K_ROUTER_SIGNATURE,
271
  K_PUBLISHED,
272
  K_RUNNING_ROUTERS,
273
  K_PLATFORM,
Roger Dingledine's avatar
Roger Dingledine committed
274
275
276
277
  _SIGNATURE,
  _PUBLIC_KEY,
  _ERR,
  _EOF
278
279
280
281
282
283
284
285
286
} directory_keyword;

struct token_table_ent { char *t; int v; };

static struct token_table_ent token_table[] = {
  { "accept", K_ACCEPT },
  { "directory-signature", K_DIRECTORY_SIGNATURE },
  { "reject", K_REJECT },
  { "router", K_ROUTER },
287
  { "recommended-software", K_RECOMMENDED_SOFTWARE },
288
289
  { "signed-directory", K_SIGNED_DIRECTORY },
  { "signing-key", K_SIGNING_KEY },
290
291
292
  { "onion-key", K_ONION_KEY },
  { "link-key", K_LINK_KEY },
  { "router-signature", K_ROUTER_SIGNATURE },
293
  { "published", K_PUBLISHED },
294
  { "running-routers", K_RUNNING_ROUTERS },
295
  { "platform", K_PLATFORM },
296
297
298
  { NULL, -1 }
};

299
#define MAX_ARGS 1024
300
301
302
303
struct directory_token {
  directory_keyword tp;
  union {
    struct {
Roger Dingledine's avatar
Roger Dingledine committed
304
      char *args[MAX_ARGS+1];
305
306
307
308
309
310
311
312
      int n_args;
    } cmd;
    char *signature;
    char *error;
    crypto_pk_env_t *public_key;
  } val;
};

313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* Free any malloced resources allocated for a token.  Don't call this if
   you inherit the reference to those resources.
 */
static void
router_release_token(directory_token_t *tok)
{
  switch (tok->tp) 
    {
    case _SIGNATURE:
      free(tok->val.signature);
      break;
    case _PUBLIC_KEY:
      crypto_free_pk_env(tok->val.public_key);
      break;
    default:
      break;
    }
}

332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
static int
_router_get_next_token(char **s, directory_token_t *tok) {
  char *next;
  crypto_pk_env_t *pkey = NULL;
  char *signature = NULL;
  int i, done;

  tok->tp = _ERR;
  tok->val.error = "";

  *s = eat_whitespace(*s);
  if (!**s) {
    tok->tp = _EOF;
    return 0;
  } else if (**s == '-') {
    next = strchr(*s, '\n');
    if (! next) { tok->val.error = "No newline at EOF"; return -1; }
    ++next;
    if (! strncmp(*s, OR_PUBLICKEY_BEGIN_TAG, next-*s)) {
      next = strstr(*s, OR_PUBLICKEY_END_TAG);
      if (!next) { tok->val.error = "No public key end tag found"; return -1; }
      next = strchr(next, '\n'); /* Part of OR_PUBLICKEY_END_TAG; can't fail.*/
      ++next;
      if (!(pkey = crypto_new_pk_env(CRYPTO_PK_RSA))) 
        return -1;
      if (crypto_pk_read_public_key_from_string(pkey, *s, next-*s)) {
        crypto_free_pk_env(pkey);
        tok->val.error = "Couldn't parse public key.";
        return -1;
      }
      tok->tp = _PUBLIC_KEY;
      tok->val.public_key = pkey;
      *s = next;
      return 0;
    } else if (! strncmp(*s, OR_SIGNATURE_BEGIN_TAG, next-*s)) {
      /* Advance past newline; can't fail. */
      *s = strchr(*s, '\n'); 
      ++*s;
      /* Find end of base64'd data */
      next = strstr(*s, OR_SIGNATURE_END_TAG);
      if (!next) { tok->val.error = "No signature end tag found"; return -1; }
373

374
      signature = tor_malloc(256);
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
      i = base64_decode(signature, 256, *s, next-*s);
      if (i<0) {
        free(signature);
        tok->val.error = "Error decoding signature."; return -1;
      } else if (i != 128) {
        free(signature);
        tok->val.error = "Bad length on decoded signature."; return -1;
      }
      tok->tp = _SIGNATURE;
      tok->val.signature = signature;

      next = strchr(next, '\n'); /* Part of OR_SIGNATURE_END_TAG; can't fail.*/
      *s = next+1;
      return 0;
    } else {
      tok->val.error = "Unrecognized begin line"; return -1;
    }
  } else {
    next = find_whitespace(*s);
    if (!next) {
      tok->val.error = "Unexpected EOF"; return -1;
    }
    for (i = 0 ; token_table[i].t ; ++i) {
      if (!strncmp(token_table[i].t, *s, next-*s)) {
        tok->tp = token_table[i].v;
        i = 0;
        done = (*next == '\n');
        *s = eat_whitespace_no_nl(next);
        while (**s != '\n' && i <= MAX_ARGS && !done) {
          next = find_whitespace(*s);
          if (*next == '\n')
            done = 1;
          *next = 0;
          tok->val.cmd.args[i++] = *s;
          *s = eat_whitespace_no_nl(next+1);
        };
        tok->val.cmd.n_args = i;
        if (i > MAX_ARGS) {
          tok->tp = _ERR;
          tok->val.error = "Too many arguments"; return -1;
        }
        return 0;
      }
    }
    tok->val.error = "Unrecognized command"; return -1;
  }
}

423
#ifdef DEBUG_ROUTER_TOKENS
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
static void 
router_dump_token(directory_token_t *tok) {
  int i;
  switch(tok->tp) 
    {
    case _SIGNATURE:
      puts("(signature)");
      return;
    case _PUBLIC_KEY:
      puts("(public key)");
      return;
    case _ERR:
      printf("(Error: %s\n)", tok->val.error);
      return;
    case _EOF:
      puts("EOF");
      return;
    case K_ACCEPT: printf("Accept"); break;
    case K_DIRECTORY_SIGNATURE: printf("Directory-Signature"); break;
    case K_REJECT: printf("Reject"); break;
444
    case K_RECOMMENDED_SOFTWARE: printf("Server-Software"); break;
445
446
447
    case K_ROUTER: printf("Router"); break;
    case K_SIGNED_DIRECTORY: printf("Signed-Directory"); break;
    case K_SIGNING_KEY: printf("Signing-Key"); break;
448
449
450
    case K_ONION_KEY: printf("Onion-key"); break;
    case K_LINK_KEY: printf("Link-key"); break;
    case K_ROUTER_SIGNATURE: printf("Router-signature"); break;
451
    case K_PUBLISHED: printf("Published"); break;
452
    case K_RUNNING_ROUTERS: printf("Running-routers"); break;
453
    case K_PLATFORM: printf("Platform"); break;
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    default:
      printf("?????? %d\n", tok->tp); return;
    }
  for (i = 0; i < tok->val.cmd.n_args; ++i) {
    printf(" \"%s\"", tok->val.cmd.args[i]);
  }
  printf("\n");
  return;
}
static int
router_get_next_token(char **s, directory_token_t *tok) {
  int i;
  i = _router_get_next_token(s, tok);
  router_dump_token(tok);
  return i;
}
#else
#define router_get_next_token _router_get_next_token
#endif

Roger Dingledine's avatar
Roger Dingledine committed
474
475
476
/* read routerinfo elements from s, and throw out the ones that
 * don't parse and resolve. */
int router_set_routerlist_from_string(char *s)
477
{
Roger Dingledine's avatar
Roger Dingledine committed
478
  if (router_get_list_from_string_impl(&s, &routerlist, -1, NULL)) {
Roger Dingledine's avatar
Roger Dingledine committed
479
    log(LOG_WARN, "Error parsing router file");
Nick Mathewson's avatar
Nick Mathewson committed
480
481
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
482
483
  if (router_resolve_routerlist(routerlist)) {
    log(LOG_WARN, "Error resolving routerlist");
Nick Mathewson's avatar
Nick Mathewson committed
484
485
486
    return -1;
  }
  return 0;
487
488
}

489
static int router_get_hash_impl(char *s, char *digest, const char *start_str,
Roger Dingledine's avatar
Roger Dingledine committed
490
                                const char *end_str)
491
492
{
  char *start, *end;
493
  start = strstr(s, start_str);
Nick Mathewson's avatar
Nick Mathewson committed
494
  if (!start) {
Roger Dingledine's avatar
Roger Dingledine committed
495
    log_fn(LOG_WARN,"couldn't find \"%s\"",start_str);
Nick Mathewson's avatar
Nick Mathewson committed
496
497
    return -1;
  }
498
  end = strstr(start+strlen(start_str), end_str);
Nick Mathewson's avatar
Nick Mathewson committed
499
  if (!end) {
Roger Dingledine's avatar
Roger Dingledine committed
500
    log_fn(LOG_WARN,"couldn't find \"%s\"",end_str);
Nick Mathewson's avatar
Nick Mathewson committed
501
502
    return -1;
  }
503
  end = strchr(end, '\n');
Nick Mathewson's avatar
Nick Mathewson committed
504
  if (!end) {
Roger Dingledine's avatar
Roger Dingledine committed
505
    log_fn(LOG_WARN,"couldn't find EOL");
Nick Mathewson's avatar
Nick Mathewson committed
506
507
    return -1;
  }
508
  ++end;
Roger Dingledine's avatar
Roger Dingledine committed
509

Nick Mathewson's avatar
Nick Mathewson committed
510
  if (crypto_SHA_digest(start, end-start, digest)) {
Roger Dingledine's avatar
Roger Dingledine committed
511
    log_fn(LOG_WARN,"couldn't compute digest");
512
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
513
  }
514
515
516
517

  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
518
int router_get_dir_hash(char *s, char *digest)
519
520
521
522
523
524
525
526
527
528
{
  return router_get_hash_impl(s,digest,
                              "signed-directory","directory-signature");
}
int router_get_router_hash(char *s, char *digest)
{
  return router_get_hash_impl(s,digest,
                              "router ","router-signature");
}

529
530
531
532
533
534
535
536
537
538
539
/* return 0 if myversion is in start. Else return -1. */
int compare_recommended_versions(char *myversion, char *start) {
  int len_myversion = strlen(myversion);
  char *comma;
  char *end = start + strlen(start);

  log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, start);
  
  for(;;) {
    comma = strchr(start, ',');
    if( ((comma ? comma : end) - start == len_myversion) &&
Roger Dingledine's avatar
Roger Dingledine committed
540
541
542
       !strncmp(start, myversion, len_myversion))
      /* only do strncmp if the length matches */
      return 0; /* success, it's there */
543
544
545
546
547
548
    if(!comma)
      return -1; /* nope */
    start = comma+1;
  }
}

Roger Dingledine's avatar
Roger Dingledine committed
549
int router_set_routerlist_from_directory(char *s, crypto_pk_env_t *pkey)
550
{
Roger Dingledine's avatar
Roger Dingledine committed
551
  if (router_get_routerlist_from_directory_impl(s, &routerlist, pkey)) {
Roger Dingledine's avatar
Roger Dingledine committed
552
    log_fn(LOG_WARN, "Couldn't parse directory.");
Nick Mathewson's avatar
Nick Mathewson committed
553
554
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
555
556
  if (router_resolve_routerlist(routerlist)) {
    log_fn(LOG_WARN, "Error resolving routerlist");
Nick Mathewson's avatar
Nick Mathewson committed
557
558
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
559
  if (compare_recommended_versions(VERSION, routerlist->software_versions) < 0) {
560
561
562
    log(options.IgnoreVersion ? LOG_WARN : LOG_ERR,
        "You are running Tor version %s, which is not recommended.\n"
       "Please upgrade to one of %s.",
Roger Dingledine's avatar
Roger Dingledine committed
563
        VERSION, routerlist->software_versions);
564
    if(options.IgnoreVersion) {
Roger Dingledine's avatar
Roger Dingledine committed
565
      log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so.");
566
567
568
569
570
571
    } else {
      fflush(0);
      exit(0);
    }
  }

Nick Mathewson's avatar
Nick Mathewson committed
572
  return 0;
573
574
}

Roger Dingledine's avatar
Roger Dingledine committed
575
576
int router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
                                              crypto_pk_env_t *pkey)
577
578
579
580
{
  directory_token_t tok;
  char digest[20];
  char signed_digest[128];
Roger Dingledine's avatar
Roger Dingledine committed
581
  routerlist_t *new_dir = NULL;
582
  char *versions;
583
584
585
586
  struct tm published;
  time_t published_on;
  const char *good_nickname_lst[1024];
  int n_good_nicknames;
587
588
589
590
  
#define NEXT_TOK()                                                      \
  do {                                                                  \
    if (router_get_next_token(&s, &tok)) {                              \
Roger Dingledine's avatar
Roger Dingledine committed
591
      log_fn(LOG_WARN, "Error reading directory: %s", tok.val.error);\
592
593
594
595
596
      return -1;                                                        \
    } } while (0)
#define TOK_IS(type,name)                                               \
  do {                                                                  \
    if (tok.tp != type) {                                               \
597
      router_release_token(&tok);                                       \
Roger Dingledine's avatar
Roger Dingledine committed
598
      log_fn(LOG_WARN, "Error reading directory: expected %s", name);\
599
600
      return -1;                                                        \
    } } while(0)
601

Nick Mathewson's avatar
Nick Mathewson committed
602
  if (router_get_dir_hash(s, digest)) {
Roger Dingledine's avatar
Roger Dingledine committed
603
    log_fn(LOG_WARN, "Unable to compute digest of directory");
604
    goto err;
Nick Mathewson's avatar
Nick Mathewson committed
605
  }
606
607
608
609
  log(LOG_DEBUG,"Received directory hashes to %02x:%02x:%02x:%02x",
      ((int)digest[0])&0xff,((int)digest[1])&0xff,
      ((int)digest[2])&0xff,((int)digest[3])&0xff);

610
611
612
  NEXT_TOK();
  TOK_IS(K_SIGNED_DIRECTORY, "signed-directory");

613
614
615
  NEXT_TOK();
  TOK_IS(K_PUBLISHED, "published");
  if (tok.val.cmd.n_args != 2) {
Roger Dingledine's avatar
Roger Dingledine committed
616
    log_fn(LOG_WARN, "Invalid published line");
617
618
619
620
    goto err;
  }
  tok.val.cmd.args[1][-1] = ' ';
  if (!strptime(tok.val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) {
Roger Dingledine's avatar
Roger Dingledine committed
621
    log_fn(LOG_WARN, "Published time was unparseable"); goto err;
622
  }
623
  published_on = tor_timegm(&published);  
624

625
  NEXT_TOK();
626
627
  TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software");
  if (tok.val.cmd.n_args != 1) {
Roger Dingledine's avatar
Roger Dingledine committed
628
    log_fn(LOG_WARN, "Invalid recommended-software line");
629
    goto err;
630
  }
631
  versions = tor_strdup(tok.val.cmd.args[0]);
632
  
633
634
635
  NEXT_TOK();
  TOK_IS(K_RUNNING_ROUTERS, "running-routers");
  n_good_nicknames = tok.val.cmd.n_args;
636
  memcpy(good_nickname_lst, tok.val.cmd.args, n_good_nicknames*sizeof(char *));
637
638
639

  if (router_get_list_from_string_impl(&s, &new_dir,
                                       n_good_nicknames, good_nickname_lst)) {
Roger Dingledine's avatar
Roger Dingledine committed
640
    log_fn(LOG_WARN, "Error reading routers from directory");
641
    goto err;
Nick Mathewson's avatar
Nick Mathewson committed
642
  }
643
  new_dir->software_versions = versions;
644
  new_dir->published_on = published_on;
645
646

  NEXT_TOK();
647
648
649
650
651
652
  TOK_IS(K_DIRECTORY_SIGNATURE, "directory-signature");
  NEXT_TOK();
  TOK_IS(_SIGNATURE, "signature");
  if (pkey) {
    if (crypto_pk_public_checksig(pkey, tok.val.signature, 128, signed_digest)
        != 20) {
Roger Dingledine's avatar
Roger Dingledine committed
653
      log_fn(LOG_WARN, "Error reading directory: invalid signature.");
654
      free(tok.val.signature);
655
      goto err;
656
    }
657
658
659
    log(LOG_DEBUG,"Signed directory hash starts %02x:%02x:%02x:%02x",
        ((int)signed_digest[0])&0xff,((int)signed_digest[1])&0xff,
        ((int)signed_digest[2])&0xff,((int)signed_digest[3])&0xff);
660
    if (memcmp(digest, signed_digest, 20)) {
Roger Dingledine's avatar
Roger Dingledine committed
661
      log_fn(LOG_WARN, "Error reading directory: signature does not match.");
662
      free(tok.val.signature);
663
      goto err;
664
665
666
667
668
669
670
    }
  }
  free(tok.val.signature);

  NEXT_TOK();
  TOK_IS(_EOF, "end of directory");

671
  if (*dest) 
Roger Dingledine's avatar
Roger Dingledine committed
672
    routerlist_free(*dest);
673
674
  *dest = new_dir;

675
  return 0;
676
677
678

 err:
  if (new_dir)
Roger Dingledine's avatar
Roger Dingledine committed
679
    routerlist_free(new_dir);
680
  return -1;
681
682
683
684
#undef NEXT_TOK
#undef TOK_IS
}

Roger Dingledine's avatar
Roger Dingledine committed
685
686
int router_get_list_from_string_impl(char **s, routerlist_t **dest,
                                     int n_good_nicknames,
687
                                     const char **good_nickname_lst)
688
{
689
  routerinfo_t *router;
690
691
  routerinfo_t **rarray;
  int rarray_len = 0;
692
  int i;
693

694
  assert(s && *s);
695

696
  rarray = (routerinfo_t **)tor_malloc((sizeof(routerinfo_t *))*MAX_ROUTERS_IN_DIR);
697

698
699
700
701
702
  while (1) {
    *s = eat_whitespace(*s);
    if (strncmp(*s, "router ", 7)!=0)
      break;
    router = router_get_entry_from_string(s);
Nick Mathewson's avatar
Nick Mathewson committed
703
    if (!router) {
Roger Dingledine's avatar
Roger Dingledine committed
704
      log_fn(LOG_WARN, "Error reading router");
705
706
707
      for(i=0;i<rarray_len;i++)
        routerinfo_free(rarray[i]);
      free(rarray);
Nick Mathewson's avatar
Nick Mathewson committed
708
709
      return -1;
    }
710
    if (rarray_len >= MAX_ROUTERS_IN_DIR) {
Roger Dingledine's avatar
Roger Dingledine committed
711
      log_fn(LOG_WARN, "too many routers");
712
713
714
      routerinfo_free(router);
      continue;
    } 
715
716
717
718
719
720
721
722
    if (n_good_nicknames>=0) {
      router->is_running = 0;
      for (i = 0; i < n_good_nicknames; ++i) {
        if (0==strcasecmp(good_nickname_lst[i], router->nickname)) {
          router->is_running = 1;
          break;
        }
      }
723
724
    } else {
      router->is_running = 1; /* start out assuming all dirservers are up */
725
    }
726
    rarray[rarray_len++] = router;
727
    log_fn(LOG_DEBUG,"just added router #%d.",rarray_len);
Roger Dingledine's avatar
Roger Dingledine committed
728
  }
729
730

  if (*dest)
Roger Dingledine's avatar
Roger Dingledine committed
731
732
    routerlist_free(*dest);
  *dest = (routerlist_t *)tor_malloc(sizeof(routerlist_t));
733
734
  (*dest)->routers = rarray;
  (*dest)->n_routers = rarray_len;
Roger Dingledine's avatar
Roger Dingledine committed
735
  (*dest)->software_versions = NULL;
736
  return 0;
737
}
738

Roger Dingledine's avatar
Roger Dingledine committed
739
static int
740
741
742
743
744
745
router_resolve(routerinfo_t *router)
{
  struct hostent *rent;

  rent = (struct hostent *)gethostbyname(router->address);
  if (!rent) {
Roger Dingledine's avatar
Roger Dingledine committed
746
    log_fn(LOG_WARN,"Could not get address for router %s.",router->address);
747
748
749
750
751
752
753
754
755
    return -1; 
  }
  assert(rent->h_length == 4);
  memcpy(&router->addr, rent->h_addr,rent->h_length);
  router->addr = ntohl(router->addr); /* get it back into host order */

  return 0;
}

Roger Dingledine's avatar
Roger Dingledine committed
756
757
static int
router_resolve_routerlist(routerlist_t *rl)
758
{
759
  int i, max, remove;
Roger Dingledine's avatar
Roger Dingledine committed
760
761
  if (!rl)
    rl = routerlist;
762

Roger Dingledine's avatar
Roger Dingledine committed
763
  max = rl->n_routers;
764
  for (i = 0; i < max; ++i) {
765
    remove = 0;
Roger Dingledine's avatar
Roger Dingledine committed
766
    if (router_resolve(rl->routers[i])) {
767
      log_fn(LOG_WARN, "Couldn't resolve router %s; not using",
Roger Dingledine's avatar
Roger Dingledine committed
768
             rl->routers[i]->address);
769
      remove = 1;
Roger Dingledine's avatar
Roger Dingledine committed
770
    } else if (options.Nickname &&
Roger Dingledine's avatar
Roger Dingledine committed
771
               !strcmp(rl->routers[i]->nickname, options.Nickname)) {
772
773
774
      remove = 1;
    }
    if (remove) {
Roger Dingledine's avatar
Roger Dingledine committed
775
776
777
      routerinfo_free(rl->routers[i]);
      rl->routers[i] = rl->routers[--max];
      --rl->n_routers;
778
      --i;
779
780
    }
  }
Roger Dingledine's avatar
Roger Dingledine committed
781

782
783
784
  return 0;
}

785
786
/* reads a single router entry from s.
 * updates s so it points to after the router it just read.
Roger Dingledine's avatar
Roger Dingledine committed
787
 * mallocs a new router and returns it if all goes well, else returns NULL.
788
 */
789
routerinfo_t *router_get_entry_from_string(char**s) {
790
  routerinfo_t *router = NULL;
791
792
793
794
  char signed_digest[128];
  char digest[128];
  directory_token_t _tok;
  directory_token_t *tok = &_tok;
795
  struct tm published;
796
797
  int t;

798
799
#define NEXT_TOKEN()                                                     \
  do { if (router_get_next_token(s, tok)) {                              \
Roger Dingledine's avatar
Roger Dingledine committed
800
      log_fn(LOG_WARN, "Error reading directory: %s", tok->val.error);\
801
      goto err;                                                          \
802
    } } while(0)
803
804
805

#define ARGS tok->val.cmd.args

806
  if (router_get_router_hash(*s, digest) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
807
    log_fn(LOG_WARN, "Couldn't compute router hash.");
808
    return NULL;
809
  }
810
811
812

  NEXT_TOKEN();

813
  if (tok->tp != K_ROUTER) {
814
    router_release_token(tok);
Roger Dingledine's avatar
Roger Dingledine committed
815
    log_fn(LOG_WARN,"Entry does not start with \"router\"");
816
817
    return NULL;
  }
818

819
  router = tor_malloc_zero(sizeof(routerinfo_t));
820
  router->onion_pkey = router->identity_pkey = router->link_pkey = NULL; 
821

822
  if (tok->val.cmd.n_args != 6) {
Roger Dingledine's avatar
Roger Dingledine committed
823
    log_fn(LOG_WARN,"Wrong # of arguments to \"router\"");
824
825
    goto err;
  }
826
  router->nickname = tor_strdup(ARGS[0]);
827
  if (strlen(router->nickname) > MAX_NICKNAME_LEN) {
Roger Dingledine's avatar
Roger Dingledine committed
828
    log_fn(LOG_WARN,"Router nickname too long.");
829
    goto err;
830
  }
831
  if (strspn(router->nickname, LEGAL_NICKNAME_CHARACTERS) != 
832
      strlen(router->nickname)) {
Roger Dingledine's avatar
Roger Dingledine committed
833
    log_fn(LOG_WARN, "Router nickname contains illegal characters.");
834
    goto err;
835
  }
836
  
837
  /* read router.address */
838
  router->address = tor_strdup(ARGS[1]);
839
  router->addr = 0;
840

841
  /* Read router->or_port */
842
  router->or_port = atoi(ARGS[2]);
843
  if(!router->or_port) {
Roger Dingledine's avatar
Roger Dingledine committed
844
    log_fn(LOG_WARN,"or_port unreadable or 0. Failing.");
845
    goto err;
846
847
  }
  
848
849
  /* Router->socks_port */
  router->socks_port = atoi(ARGS[3]);
850
  
851
  /* Router->dir_port */
852
  router->dir_port = atoi(ARGS[4]);
853

854
  /* Router->bandwidth */
855
  router->bandwidth = atoi(ARGS[5]);
856
  if (!router->bandwidth) {
Roger Dingledine's avatar
Roger Dingledine committed
857
    log_fn(LOG_WARN,"bandwidth unreadable or 0. Failing.");
858
    goto err;
859
  }
860
  
861
862
  log_fn(LOG_DEBUG,"or_port %d, socks_port %d, dir_port %d, bandwidth %d.",
    router->or_port, router->socks_port, router->dir_port, router->bandwidth);
863

864
  /* XXX Later, require platform before published. */
865
  NEXT_TOKEN();
866
867
868
869
  if (tok->tp == K_PLATFORM) {
    NEXT_TOKEN();
  }
  
870
  if (tok->tp != K_PUBLISHED) {
Roger Dingledine's avatar
Roger Dingledine committed
871
    log_fn(LOG_WARN, "Missing published time"); goto err;
872
873
  }
  if (tok->val.cmd.n_args != 2) {
Roger Dingledine's avatar
Roger Dingledine committed
874
    log_fn(LOG_WARN, "Wrong number of arguments to published"); goto err;
875
  }
876
877
  ARGS[1][-1] = ' '; /* Re-insert space. */
  if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) {
Roger Dingledine's avatar
Roger Dingledine committed
878
    log_fn(LOG_WARN, "Published time was unparseable"); goto err;
879
  }
880
  router->published_on = tor_timegm(&published);
881

882
883
  NEXT_TOKEN();
  if (tok->tp != K_ONION_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
884
    log_fn(LOG_WARN, "Missing onion-key"); goto err;
885
  }
886
  NEXT_TOKEN();
887
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
888
    log_fn(LOG_WARN, "Missing onion key"); goto err;
889
890
  } /* XXX Check key length */
  router->onion_pkey = tok->val.public_key;
891
892

  NEXT_TOKEN();
893
  if (tok->tp != K_LINK_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
894
    log_fn(LOG_WARN, "Missing link-key");  goto err;
895
896
897
  }
  NEXT_TOKEN();
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
898
    log_fn(LOG_WARN, "Missing link key"); goto err;
899
900
901
902
903
  } /* XXX Check key length */
  router->link_pkey = tok->val.public_key;

  NEXT_TOKEN();
  if (tok->tp != K_SIGNING_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
904
    log_fn(LOG_WARN, "Missing signing-key"); goto err;
905
906
907
  }
  NEXT_TOKEN();
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
908
    log_fn(LOG_WARN, "Missing signing key"); goto err;
909
910
  }
  router->identity_pkey = tok->val.public_key;
911

912
  NEXT_TOKEN();
913
914
915
  while (tok->tp == K_ACCEPT || tok->tp == K_REJECT) {
    router_add_exit_policy(router, tok);
    NEXT_TOKEN();
Roger Dingledine's avatar
Roger Dingledine committed
916
  }
917
  
918
  if (tok->tp != K_ROUTER_SIGNATURE) {
Roger Dingledine's avatar
Roger Dingledine committed
919
    log_fn(LOG_WARN,"Missing router signature");
920
921
922
923
    goto err;
  }
  NEXT_TOKEN();
  if (tok->tp != _SIGNATURE) {
Roger Dingledine's avatar
Roger Dingledine committed
924
    log_fn(LOG_WARN,"Missing router signature");
925
926
927
    goto err;
  }
  assert (router->identity_pkey);
928

929
930
  if ((t=crypto_pk_public_checksig(router->identity_pkey, tok->val.signature,
                                   128, signed_digest)) != 20) {
Roger Dingledine's avatar
Roger Dingledine committed
931
    log_fn(LOG_WARN, "Invalid signature %d",t);
932
933
934
    goto err;
  }
  if (memcmp(digest, signed_digest, 20)) {
Roger Dingledine's avatar
Roger Dingledine committed
935
    log_fn(LOG_WARN, "Mismatched signature");
936
937
938
    goto err;
  }
  
939
  router_release_token(tok); /* free the signature */
Roger Dingledine's avatar
Roger Dingledine committed
940
  return router;
941

942
 err:
943
  router_release_token(tok); 
944
  routerinfo_free(router);
945
  return NULL;
946
947
#undef ARGS
#undef NEXT_TOKEN
948
949
}

950
951
952
void router_add_exit_policy_from_config(routerinfo_t *router) {
  char *s = options.ExitPolicy, *e;
  int last=0;
953
  char line[1024];
954
955
956
957
958
959
960
961
962
963
964
965

  if(!s) {
    log_fn(LOG_INFO,"No exit policy configured. Ok.");
    return; /* nothing to see here */
  }
  if(!*s) {
    log_fn(LOG_INFO,"Exit policy is empty. Ok.");
    return; /* nothing to see here */
  }

  for(;;) {
    e = strchr(s,',');
966
    if(!e) {
967
      last = 1;
968
      strncpy(line,s,1023); 
969
    } else {