routers.c 26 KB
Newer Older
1
2
3
4
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* 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

#include "or.h"

12
13
14
/****************************************************************************/

/* router array */
15
static directory_t *directory = NULL;
16
17

extern or_options_t options; /* command-line and config-file options */
18
extern routerinfo_t *my_routerinfo; /* from main.c */
19
20

/****************************************************************************/
21

22
23
24
struct directory_token;
typedef struct directory_token directory_token_t;

25
/* static function prototypes */
26
void routerlist_free(routerinfo_t *list);
27
static char *eat_whitespace(char *s);
28
static char *eat_whitespace_no_nl(char *s);
29
static char *find_whitespace(char *s);
Roger Dingledine's avatar
Roger Dingledine committed
30
static void router_free_exit_policy(routerinfo_t *router);
31
32
33
34
35
36
static routerinfo_t *router_get_entry_from_string_tok(char**s, 
                                                      directory_token_t *tok);
static int router_get_list_from_string_tok(char **s, directory_t **dest,
                                           directory_token_t *tok);
static int router_add_exit_policy(routerinfo_t *router, 
                                  directory_token_t *tok);
Roger Dingledine's avatar
Roger Dingledine committed
37
38
static int 
router_resolve_directory(directory_t *dir);
39

40
41
/****************************************************************************/

42
43
44
45
46
47
48
int learn_my_address(struct sockaddr_in *me) {
  /* local host information */
  char localhostname[512];
  struct hostent *localhost;

  /* obtain local host information */
  if(gethostname(localhostname,512) < 0) {
Nick Mathewson's avatar
Nick Mathewson committed
49
    log_fn(LOG_ERR,"Error obtaining local hostname");
50
51
    return -1;
  }
52
  log_fn(LOG_DEBUG,"localhostname is '%s'.",localhostname);
53
54
  localhost = gethostbyname(localhostname);
  if (!localhost) {
55
    log_fn(LOG_ERR,"Error obtaining local host info.");
56
57
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
58
  memset(me,0,sizeof(struct sockaddr_in));
59
60
  me->sin_family = AF_INET;
  memcpy((void *)&me->sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr));
Nick Mathewson's avatar
Nick Mathewson committed
61
  me->sin_port = htons((uint16_t) options.ORPort);
62
  log_fn(LOG_DEBUG,"chose address as '%s'.",inet_ntoa(me->sin_addr));
Nick Mathewson's avatar
src/or    
Nick Mathewson committed
63
64
65
66
67
  if (!strncmp("127.",inet_ntoa(me->sin_addr), 4) &&
      strcasecmp(localhostname, "localhost")) {
    /* We're a loopback IP but we're not called localhost.  Uh oh! */
    log_fn(LOG_WARNING, "Got a loopback address: /etc/hosts may be wrong");
  }
68
69
70
71
  return 0;
}

void router_retry_connections(void) {
72
73
74
  int i;
  routerinfo_t *router;

75
76
  for (i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
77
    if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) { /* not in the list */
78
      log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port);
79
      connection_or_connect(router);
80
81
82
83
84
85
86
87
88
    }
  }
}

routerinfo_t *router_pick_directory_server(void) {
  /* currently, pick the first router with a positive dir_port */
  int i;
  routerinfo_t *router;
  
89
  if(!directory)
90
91
    return NULL;

92
93
  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
94
95
96
97
98
99
100
101
102
103
104
    if(router->dir_port > 0)
      return router;
  }

  return NULL;
}

routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
  int i;
  routerinfo_t *router;

105
  assert(directory);
106

107
108
  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
109
110
111
112
113
114
115
    if ((router->addr == addr) && (router->or_port == port))
      return router;
  }

  return NULL;
}

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
routerinfo_t *router_get_by_pk(crypto_pk_env_t *pk) 
{
  int i;
  routerinfo_t *router;

  assert(directory);

  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
    /* XXX Should this really be a separate link key? */
    if (0 == crypto_pk_cmp_keys(router->pkey, pk))
      return router;
  }
  
  return NULL;
}
  

134
135
void router_get_directory(directory_t **pdirectory) {
  *pdirectory = directory;
136
137
}

138
139
140
141
/* return 1 if addr and port corresponds to my addr and my or_listenport. else 0,
 * or -1 for failure.
 */
int router_is_me(uint32_t addr, uint16_t port)
142
{
143
  /* XXXX Should this check the key too? */
144
145
  struct sockaddr_in me; /* my router identity */

146
  if(!options.OnionRouter) {
147
148
149
150
151
152
153
154
155
156
157
158
    /* we're not an OR. This obviously isn't us. */
    return 0;
  }
 
  if(learn_my_address(&me) < 0)
    return -1;

  if(ntohl(me.sin_addr.s_addr) == addr && ntohs(me.sin_port) == port)
    return 1;

  return 0;

159
160
}

Roger Dingledine's avatar
Roger Dingledine committed
161
/* delete a list of routers from memory */
162
void routerinfo_free(routerinfo_t *router)
Roger Dingledine's avatar
Roger Dingledine committed
163
{
164
  struct exit_policy_t *e = NULL, *etmp = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
165
  
166
  if (!router)
Roger Dingledine's avatar
Roger Dingledine committed
167
168
    return;

169
170
171
172
  if (router->address)
    free(router->address);
  if (router->pkey)
    crypto_free_pk_env(router->pkey);
173
174
  if (router->signing_pkey)
    crypto_free_pk_env(router->signing_pkey);
175
176
177
178
179
180
181
182
183
184
  e = router->exit_policy;
  while (e) {
    etmp = e->next;
    if (e->string) free(e->string);
    if (e->address) free(e->address);
    if (e->port) free(e->port);
    free(e);
    e = etmp;
  }
  free(router);
185
186
}

187
188
void directory_free(directory_t *directory)
{
189
190
191
192
  int i;
  for (i = 0; i < directory->n_routers; ++i)
    routerinfo_free(directory->routers[i]);
  free(directory->routers);
Roger Dingledine's avatar
Roger Dingledine committed
193
194
  if(directory->software_versions)
    free(directory->software_versions);
195
196
197
  free(directory);
}

198
199
200
201
202
203
204
205
206
void router_forget_router(uint32_t addr, uint16_t port) {
  int i;
  routerinfo_t *router;

  router = router_get_by_addr_port(addr,port);
  if(!router) /* we don't seem to know about him in the first place */
    return;

  /* now walk down router_array until we get to router */
207
208
  for(i=0;i<directory->n_routers;i++)
    if(directory->routers[i] == router)
209
210
      break;

211
  assert(i != directory->n_routers); /* if so then router_get_by_addr_port should have returned null */
212

Roger Dingledine's avatar
Roger Dingledine committed
213
214
//  free(router); /* don't actually free; we'll free it when we free the whole thing */

215
//  log(LOG_DEBUG,"router_forget_router(): Forgot about router %d:%d",addr,port);
216
217
  for(; i<directory->n_routers-1;i++)
    directory->routers[i] = directory->routers[i+1];
218
  /* XXX bug, we're not decrementing n_routers here? needs more attention. -RD */
219
220
}

Roger Dingledine's avatar
Roger Dingledine committed
221
/* load the router list */
222
int router_get_list_from_file(char *routerfile)
Roger Dingledine's avatar
Roger Dingledine committed
223
{
224
225
226
  int fd; /* router file */
  struct stat statbuf;
  char *string;
Roger Dingledine's avatar
Roger Dingledine committed
227

228
  assert(routerfile);
Roger Dingledine's avatar
Roger Dingledine committed
229
  
230
  if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) {
231
    log_fn(LOG_ERR,"Filename %s contains illegal characters.",routerfile);
232
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
233
234
  }
  
235
  if(stat(routerfile, &statbuf) < 0) {
236
    log_fn(LOG_ERR,"Could not stat %s.",routerfile);
237
    return -1;
238
239
  }

Roger Dingledine's avatar
Roger Dingledine committed
240
  /* open the router list */
241
242
  fd = open(routerfile,O_RDONLY,0);
  if (fd<0) {
243
    log_fn(LOG_ERR,"Could not open %s.",routerfile);
244
    return -1;
245
246
  }

247
  string = tor_malloc(statbuf.st_size+1);
248
249

  if(read(fd,string,statbuf.st_size) != statbuf.st_size) {
250
251
    log_fn(LOG_ERR,"Couldn't read all %ld bytes of file '%s'.",
           (long)statbuf.st_size,routerfile);
252
253
254
    free(string);
    close(fd);
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
255
  }
256
  close(fd);
Roger Dingledine's avatar
Roger Dingledine committed
257
  
258
  string[statbuf.st_size] = 0; /* null terminate it */
259

260
  if(router_get_list_from_string(string) < 0) {
Nick Mathewson's avatar
Nick Mathewson committed
261
    log_fn(LOG_ERR,"The routerfile itself was corrupt.");
262
263
264
265
266
267
268
269
    free(string);
    return -1;
  }

  free(string);
  return 0;
} 

270
271
272
273

typedef enum {
  K_ACCEPT,
  K_DIRECTORY_SIGNATURE,
274
  K_RECOMMENDED_SOFTWARE,
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  K_REJECT, 
  K_ROUTER, 
  K_SIGNED_DIRECTORY,
  K_SIGNING_KEY,
  _SIGNATURE, 
  _PUBLIC_KEY, 
  _ERR, 
  _EOF 
} 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 },
292
  { "recommended-software", K_RECOMMENDED_SOFTWARE },
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  { "signed-directory", K_SIGNED_DIRECTORY },
  { "signing-key", K_SIGNING_KEY },
  { NULL, -1 }
};

#define MAX_ARGS 8
struct directory_token {
  directory_keyword tp;
  union {
    struct {
      char *args[MAX_ARGS+1]; 
      int n_args;
    } cmd;
    char *signature;
    char *error;
    crypto_pk_env_t *public_key;
  } val;
};

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* 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;
    }
}

331
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
      signature = tor_malloc(256);
374
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
      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;
  }
}

422
#ifdef DEBUG_ROUTER_TOKENS
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
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;
443
    case K_RECOMMENDED_SOFTWARE: printf("Server-Software"); break;
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
    case K_ROUTER: printf("Router"); break;
    case K_SIGNED_DIRECTORY: printf("Signed-Directory"); break;
    case K_SIGNING_KEY: printf("Signing-Key"); break;
    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



/* return the first char of s that is not whitespace and not a comment */
static char *eat_whitespace(char *s) {
  assert(s);

  while(isspace(*s) || *s == '#') {
    while(isspace(*s))
      s++;
    if(*s == '#') { /* read to a \n or \0 */
      while(*s && *s != '\n')
        s++;
      if(!*s)
        return s;
    }
  }
  return s;
}

static char *eat_whitespace_no_nl(char *s) {
  while(*s == ' ' || *s == '\t') 
    ++s;
  return s;
}

/* return the first char of s that is whitespace or '#' or '\0 */
static char *find_whitespace(char *s) {
  assert(s);

  while(*s && !isspace(*s) && *s != '#')
    s++;

  return s;
}

503
504
int router_get_list_from_string(char *s) 
{
Nick Mathewson's avatar
Nick Mathewson committed
505
506
507
508
509
510
511
512
513
  if (router_get_list_from_string_impl(s, &directory)) {
    log(LOG_ERR, "Error parsing router file");
    return -1;
  }
  if (router_resolve_directory(directory)) {
    log(LOG_ERR, "Error resolving directory");
    return -1;
  }
  return 0;
514
515
}

516
517
518
int router_get_list_from_string_impl(char *s, directory_t **dest) {
  directory_token_t tok;
  if (router_get_next_token(&s, &tok)) {
Nick Mathewson's avatar
Nick Mathewson committed
519
    log(LOG_ERR, "Error reading routers: %s", tok.val.error); 
520
    return -1;
521
522
523
524
525
526
527
528
  }
  return router_get_list_from_string_tok(&s, dest, &tok);
}

static int router_get_dir_hash(char *s, char *digest)
{
  char *start, *end;
  start = strstr(s, "signed-directory");
Nick Mathewson's avatar
Nick Mathewson committed
529
530
531
532
  if (!start) {
    log(LOG_ERR,"router_get_dir_hash(): couldn't find \"signed-directory\"");
    return -1;
  }
533
  end = strstr(start, "directory-signature");
Nick Mathewson's avatar
Nick Mathewson committed
534
535
536
537
  if (!end) {
    log(LOG_ERR,"router_get_dir_hash(): couldn't find \"directory-signature\"");
    return -1;
  }
538
  end = strchr(end, '\n');
Nick Mathewson's avatar
Nick Mathewson committed
539
540
541
542
  if (!end) {
    log(LOG_ERR,"router_get_dir_hash(): couldn't find EOL");
    return -1;
  }
543
544
  ++end;
  
Nick Mathewson's avatar
Nick Mathewson committed
545
546
  if (crypto_SHA_digest(start, end-start, digest)) {
    log(LOG_ERR,"router_get_dir_hash(): couldn't compute digest");
547
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
548
  }
549
550
551
552

  return 0;
}

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
/* 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) &&
       !strncmp(start, myversion, len_myversion)) /* only do strncmp if the length matches */
        return 0; /* success, it's there */
    if(!comma)
      return -1; /* nope */
    start = comma+1;
  }
}

572
573
int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey)
{
Nick Mathewson's avatar
Nick Mathewson committed
574
  if (router_get_dir_from_string_impl(s, &directory, pkey)) {
575
    log(LOG_ERR, "router_get_dir_from_string: Couldn't parse directory.");
Nick Mathewson's avatar
Nick Mathewson committed
576
577
578
579
580
581
    return -1;
  }
  if (router_resolve_directory(directory)) {
    log(LOG_ERR, "Error resolving directory");
    return -1;
  }
582
583
584
585
586
587
588
589
590
591
592
  if (compare_recommended_versions(VERSION, directory->software_versions) < 0) {
    log(LOG_ERR, "You are running tor version %s, which is no longer supported.\nPlease upgrade to one of %s.", VERSION, RECOMMENDED_SOFTWARE_VERSIONS);
    if(options.IgnoreVersion) {
      log(LOG_WARNING, "IgnoreVersion is set. If it breaks, we told you so.");
    } else {
      log(LOG_ERR,"Set IgnoreVersion config variable if you want to survive this error.");
      fflush(0);
      exit(0);
    }
  }

Nick Mathewson's avatar
Nick Mathewson committed
593
  return 0;
594
595
596
597
598
599
600
601
}

int router_get_dir_from_string_impl(char *s, directory_t **dest,
                                    crypto_pk_env_t *pkey)
{
  directory_token_t tok;
  char digest[20];
  char signed_digest[128];
602
  directory_t *new_dir = NULL;
603
  char *versions;
604
605
606
607
608
609
610
611
612
613
  
#define NEXT_TOK()                                                      \
  do {                                                                  \
    if (router_get_next_token(&s, &tok)) {                              \
      log(LOG_ERR, "Error reading directory: %s", tok.val.error);       \
      return -1;                                                        \
    } } while (0)
#define TOK_IS(type,name)                                               \
  do {                                                                  \
    if (tok.tp != type) {                                               \
614
      router_release_token(&tok);                                       \
615
616
617
618
      log(LOG_ERR, "Error reading directory: expected %s", name);       \
      return -1;                                                        \
    } } while(0)
  
Nick Mathewson's avatar
Nick Mathewson committed
619
620
  if (router_get_dir_hash(s, digest)) {
    log(LOG_ERR, "Unable to compute digest of directory");
621
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
622
  }
623
624
625
626
627

  NEXT_TOK();
  TOK_IS(K_SIGNED_DIRECTORY, "signed-directory");

  NEXT_TOK();
628
629
630
631
632
633
  TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software");
  if (tok.val.cmd.n_args != 1) {
    log(LOG_ERR, "Invalid recommded-software line");
    return -1;
  }
  versions = strdup(tok.val.cmd.args[0]);
634
635
  
  NEXT_TOK();
Nick Mathewson's avatar
Nick Mathewson committed
636
637
  if (router_get_list_from_string_tok(&s, &new_dir, &tok)) {
    log(LOG_ERR, "Error reading routers from directory");
638
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
639
  }
640
  new_dir->software_versions = versions;
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  
  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) {
      log(LOG_ERR, "Error reading directory: invalid signature.");
      free(tok.val.signature);
      return -1;
    }
    if (memcmp(digest, signed_digest, 20)) {
      log(LOG_ERR, "Error reading directory: signature does not match.");
      free(tok.val.signature);
      return -1;
    }
  }
  free(tok.val.signature);

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

663
664
665
666
  if (*dest) 
    directory_free(*dest);
  *dest = new_dir;

667
668
669
670
671
672
673
  return 0;
#undef NEXT_TOK
#undef TOK_IS
}

static int router_get_list_from_string_tok(char **s, directory_t **dest,
                                           directory_token_t *tok)
674
{
675
  routerinfo_t *router;
676
677
  routerinfo_t **rarray;
  int rarray_len = 0;
678
679
680

  assert(s);

681
  rarray = (routerinfo_t **)tor_malloc((sizeof(routerinfo_t *))*MAX_ROUTERS_IN_DIR);
682

683
684
  while (tok->tp == K_ROUTER) {
    router = router_get_entry_from_string_tok(s, tok);
Nick Mathewson's avatar
Nick Mathewson committed
685
686
687
688
    if (!router) {
      log(LOG_ERR, "Error reading router");
      return -1;
    }
689
690
691
692
693
694
    if (rarray_len >= MAX_ROUTERS_IN_DIR) {
      log(LOG_ERR, "router_get_list_from_string_tok(): too many routers");
      routerinfo_free(router);
      continue;
    } 
    rarray[rarray_len++] = router;
Roger Dingledine's avatar
Roger Dingledine committed
695
  }
696
 
697
698
  if (*dest) 
    directory_free(*dest);
699
  *dest = (directory_t *)tor_malloc(sizeof(directory_t));
700
701
  (*dest)->routers = rarray;
  (*dest)->n_routers = rarray_len;
Roger Dingledine's avatar
Roger Dingledine committed
702
  (*dest)->software_versions = NULL;
703
  return 0;
704
}
705

Roger Dingledine's avatar
Roger Dingledine committed
706
static int 
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
router_resolve(routerinfo_t *router)
{
  struct hostent *rent;

  rent = (struct hostent *)gethostbyname(router->address);
  if (!rent) {
    log(LOG_ERR,"router_resolve(): Could not get address for router %s.",router->address);
    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
723
static int 
724
725
router_resolve_directory(directory_t *dir)
{
726
  int i, max, remove;
727
728
729
730
731
  if (!dir)
    dir = directory;

  max = dir->n_routers;
  for (i = 0; i < max; ++i) {
732
    remove = 0;
733
    if (router_resolve(dir->routers[i])) {
734
      log(LOG_INFO, "Couldn't resolve router %s; removing",
Nick Mathewson's avatar
Nick Mathewson committed
735
          dir->routers[i]->address);
736
      remove = 1;
737
      routerinfo_free(dir->routers[i]);
738
739
740
741
742
    } else if (router_is_me(dir->routers[i]->addr, dir->routers[i]->or_port)) {
      my_routerinfo = dir->routers[i];
      remove = 1;
    }
    if (remove) {
743
744
      dir->routers[i] = dir->routers[--max];
      --dir->n_routers;
745
      --i;
746
747
748
749
750
751
    }
  }
  
  return 0;
}

752
753
754
755
756
757

routerinfo_t *router_get_entry_from_string(char **s) {
  directory_token_t tok;
  routerinfo_t *router;
  if (router_get_next_token(s, &tok)) return NULL;
  router = router_get_entry_from_string_tok(s, &tok);
758
759
  if (tok.tp != _EOF) {
    router_release_token(&tok);
760
    return NULL;
761
  }
762
763
764
  return router;
}

765
766
767
768
/* reads a single router entry from s.
 * updates s so it points to after the router it just read.
 * mallocs a new router, returns it if all goes well, else returns NULL.
 */
769
770
static routerinfo_t *router_get_entry_from_string_tok(char**s, directory_token_t *tok) {
  routerinfo_t *router = NULL;
771

772
773
774
775
776
#define NEXT_TOKEN()                                                    \
  do { if (router_get_next_token(s, tok)) {                             \
      log(LOG_ERR, "Error reading directory: %s", tok->val.error);      \
      goto err;                                                         \
    } } while(0)
777
778
779
780

#define ARGS tok->val.cmd.args

  if (tok->tp != K_ROUTER) {
781
    router_release_token(tok);
782
783
784
    log(LOG_ERR,"router_get_entry_from_string(): Entry does not start with \"router\"");
    return NULL;
  }
785
786

  router = tor_malloc(sizeof(routerinfo_t));
787
  memset(router,0,sizeof(routerinfo_t)); /* zero it out first */
788
789
790
  /* C doesn't guarantee that NULL is represented by 0 bytes.  You'll
     thank me for this someday. */
  router->pkey = router->signing_pkey = NULL; 
791

792
  if (tok->val.cmd.n_args != 5) {
793
794
795
796
797
798
799
    log(LOG_ERR,"router_get_entry_from_string(): Wrong # of arguments to \"router\"");
    goto err;
  }

  /* read router.address */
  if (!(router->address = strdup(ARGS[0])))
    goto err;
800
  router->addr = 0;
801

802
803
  /* Read router->or_port */
  router->or_port = atoi(ARGS[1]);
804
  if(!router->or_port) {
805
806
    log(LOG_ERR,"router_get_entry_from_string(): or_port unreadable or 0. Failing.");
    goto err;
807
808
  }
  
809
  /* Router->ap_port */
810
  router->ap_port = atoi(ARGS[2]);
811
  
812
  /* Router->dir_port */
813
  router->dir_port = atoi(ARGS[3]);
814

815
  /* Router->bandwidth */
816
  router->bandwidth = atoi(ARGS[4]);
817
818
  if (!router->bandwidth) {
    log(LOG_ERR,"router_get_entry_from_string(): bandwidth unreadable or 0. Failing.");
819
  }
820
  
821
822
  log(LOG_DEBUG,"or_port %d, ap_port %d, dir_port %d, bandwidth %d.",
    router->or_port, router->ap_port, router->dir_port, router->bandwidth);
823

824
  NEXT_TOKEN();
825
  if (tok->tp != _PUBLIC_KEY) {
826
827
828
829
830
831
832
833
834
835
836
    log(LOG_ERR,"router_get_entry_from_string(): Missing public key");
    goto err;
  } /* Check key length */
  router->pkey = tok->val.public_key;

  NEXT_TOKEN();
  if (tok->tp == K_SIGNING_KEY) {
    NEXT_TOKEN();
    if (tok->tp != _PUBLIC_KEY) {
      log(LOG_ERR,"router_get_entry_from_string(): Missing signing key");
      goto err;
837
    }
838
839
840
    router->signing_pkey = tok->val.public_key;
    NEXT_TOKEN();
  } 
841

842
843
844
  while (tok->tp == K_ACCEPT || tok->tp == K_REJECT) {
    router_add_exit_policy(router, tok);
    NEXT_TOKEN();
Roger Dingledine's avatar
Roger Dingledine committed
845
  }
846
  
Roger Dingledine's avatar
Roger Dingledine committed
847
  return router;
848

849
 err:
850
  router_release_token(tok); 
851
852
853
854
  if(router->address)
    free(router->address);
  if(router->pkey)
    crypto_free_pk_env(router->pkey);
855
856
  if(router->signing_pkey)
    crypto_free_pk_env(router->signing_pkey);
Roger Dingledine's avatar
Roger Dingledine committed
857
  router_free_exit_policy(router);
858
859
  free(router);
  return NULL;
860
861
#undef ARGS
#undef NEXT_TOKEN
862
863
}

Roger Dingledine's avatar
Roger Dingledine committed
864
865
866
867
868
869
870
871
872
873
874
875
876
static void router_free_exit_policy(routerinfo_t *router) {
  struct exit_policy_t *tmpe;

  while(router->exit_policy) {
    tmpe = router->exit_policy;
    router->exit_policy = tmpe->next;
    free(tmpe->string);
    free(tmpe->address);
    free(tmpe->port);
    free(tmpe);
  }
}

877
878
879
880
881
882
883
884
885
886
887
888
#if 0
void test_write_pkey(crypto_pk_env_t *pkey) {
  char *string;
  int len;

  log(LOG_DEBUG,"Trying test write.");
  if(crypto_pk_write_public_key_to_string(pkey,&string,&len)<0) {
    log(LOG_DEBUG,"router_get_entry_from_string(): write pkey to string failed\n");
    return;
  }
  log(LOG_DEBUG,"I did it: len %d, string '%s'.",len,string);
  free(string);
Roger Dingledine's avatar
Roger Dingledine committed
889
}
890
#endif
891

892
893
static int router_add_exit_policy(routerinfo_t *router, 
                                  directory_token_t *tok) {
Roger Dingledine's avatar
Roger Dingledine committed
894
  struct exit_policy_t *tmpe, *newe;
895
  char *arg, *colon;
Roger Dingledine's avatar
Roger Dingledine committed
896

897
898
899
  if (tok->val.cmd.n_args != 1)
    return -1;
  arg = tok->val.cmd.args[0];
Roger Dingledine's avatar
Roger Dingledine committed
900

901
  newe = tor_malloc(sizeof(struct exit_policy_t));
Roger Dingledine's avatar
Roger Dingledine committed
902
  memset(newe,0,sizeof(struct exit_policy_t));
903
  
904
  newe->string = tor_malloc(8+strlen(arg));
905
906
  if (tok->tp == K_REJECT) {
    strcpy(newe->string, "reject ");
Roger Dingledine's avatar
Roger Dingledine committed
907
908
    newe->policy_type = EXIT_POLICY_REJECT;
  } else {
909
910
911
    assert(tok->tp == K_ACCEPT);
    strcpy(newe->string, "accept ");
    newe->policy_type = EXIT_POLICY_ACCEPT;
Roger Dingledine's avatar
Roger Dingledine committed
912
  }
913
914
915
916
  strcat(newe->string, arg);
  
  colon = strchr(arg,':');
  if(!colon)
Roger Dingledine's avatar
Roger Dingledine committed
917
    goto policy_read_failed;
918
919
920
  *colon = 0;
  newe->address = strdup(arg);
  newe->port = strdup(colon+1);
Roger Dingledine's avatar
Roger Dingledine committed
921

922
923
924
  log(LOG_DEBUG,"router_add_exit_policy(): %s %s:%s",
      newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept",
      newe->address, newe->port);
Roger Dingledine's avatar
Roger Dingledine committed
925
926
927
928
929

  /* now link newe onto the end of exit_policy */

  if(!router->exit_policy) {
    router->exit_policy = newe;
930
    return 0;
Roger Dingledine's avatar
Roger Dingledine committed
931
932
933
934
935
  }

  for(tmpe=router->exit_policy; tmpe->next; tmpe=tmpe->next) ;
  tmpe->next = newe;

936
  return 0;
Roger Dingledine's avatar
Roger Dingledine committed
937
938
939
940
941
942
943
944
945
946
947

policy_read_failed:
  assert(newe->string);
  log(LOG_INFO,"router_add_exit_policy(): Couldn't parse line '%s'. Dropping", newe->string);
  if(newe->string)
    free(newe->string);
  if(newe->address)
    free(newe->address);
  if(newe->port)
    free(newe->port);
  free(newe);
948
  return -1;
Roger Dingledine's avatar
Roger Dingledine committed
949
950
}

951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
/* Return 0 if my exit policy says to allow connection to conn.
 * Else return -1.
 */
int router_compare_to_exit_policy(connection_t *conn) {
  struct exit_policy_t *tmpe;

  if(!my_routerinfo) {
    log(LOG_WARNING, "router_compare_to_exit_policy(): my_routerinfo undefined! Rejected.");
    return -1;
  }

  for(tmpe=my_routerinfo->exit_policy; tmpe; tmpe=tmpe->next) {
    assert(tmpe->address);
    assert(tmpe->port);

    /* Totally ignore the address field of the exit policy, for now. */

    if(!strcmp(tmpe->port,"*") || atoi(tmpe->port) == conn->port) {
      log(LOG_INFO,"router_compare_to_exit_policy(): Port '%s' matches '%d'. %s.",
          tmpe->port, conn->port,
          tmpe->policy_type == EXIT_POLICY_ACCEPT ? "Accepting" : "Rejecting");
      if(tmpe->policy_type == EXIT_POLICY_ACCEPT)
        return 0;
      else
        return -1;
    }
  }

  return 0; /* accept all by default. */

}

983
984
985
986
987
988
989
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/
Roger Dingledine's avatar
Roger Dingledine committed
990