routers.c 34.3 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
/****************************************************************************/

19
20
21
static directory_t *directory = NULL; /* router array */
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
void routerlist_free(routerinfo_t *list);
32
static char *eat_whitespace(char *s);
33
static char *eat_whitespace_no_nl(char *s);
34
static char *find_whitespace(char *s);
35
static int router_add_exit_policy_from_string(routerinfo_t *router, char *s);
36
37
static int router_add_exit_policy(routerinfo_t *router, 
                                  directory_token_t *tok);
38
static int router_resolve_directory(directory_t *dir);
39

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

42
void router_retry_connections(void) {
43
44
45
  int i;
  routerinfo_t *router;

46
47
  for (i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
48
    if(!connection_exact_get_by_addr_port(router->addr,router->or_port)) { /* not in the list */
49
      log_fn(LOG_DEBUG,"connecting to OR %s:%u.",router->address,router->or_port);
50
      connection_or_connect(router);
51
52
53
54
55
    }
  }
}

routerinfo_t *router_pick_directory_server(void) {
56
  /* pick the first running router with a positive dir_port */
57
58
59
  int i;
  routerinfo_t *router;
  
60
  if(!directory)
61
62
    return NULL;

63
64
  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
65
    if(router->dir_port > 0 && router->is_running)
66
67
68
69
70
71
      return router;
  }

  return NULL;
}

72
73
74
75
76
77
78
void router_upload_desc_to_dirservers(void) {
  int i;
  routerinfo_t *router;

  if(!directory)
    return;

79
  if (!router_get_my_descriptor()) {
Roger Dingledine's avatar
Roger Dingledine committed
80
    log_fn(LOG_WARN, "No descriptor; skipping upload");
81
82
83
    return;
  }

84
85
86
87
88
89
90
  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
    if(router->dir_port > 0)
      directory_initiate_command(router, DIR_CONN_STATE_CONNECTING_UPLOAD);
  }
}

91
92
93
94
routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) {
  int i;
  routerinfo_t *router;

95
  assert(directory);
96

97
98
  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
99
100
101
102
103
104
    if ((router->addr == addr) && (router->or_port == port))
      return router;
  }
  return NULL;
}

105
routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk) 
106
107
108
109
110
111
112
113
{
  int i;
  routerinfo_t *router;

  assert(directory);

  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
114
    if (0 == crypto_pk_cmp_keys(router->link_pkey, pk))
115
116
117
118
      return router;
  }
  return NULL;
}
119

120
routerinfo_t *router_get_by_nickname(char *nickname)
121
122
123
124
125
126
127
128
{
  int i;
  routerinfo_t *router;

  assert(directory);

  for(i=0;i<directory->n_routers;i++) {
    router = directory->routers[i];
129
    if (0 == strcmp(router->nickname, nickname))
130
131
132
133
      return router;
  }
  return NULL;
}
134

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

139
/* delete a router from memory */
140
void routerinfo_free(routerinfo_t *router)
Roger Dingledine's avatar
Roger Dingledine committed
141
{
142
  struct exit_policy_t *e;
Roger Dingledine's avatar
Roger Dingledine committed
143
  
144
  if (!router)
Roger Dingledine's avatar
Roger Dingledine committed
145
146
    return;

147
148
  tor_free(router->address);
  tor_free(router->nickname);
149
150
151
152
153
154
  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);
155
156
157
  while (router->exit_policy) {
    e = router->exit_policy;
    router->exit_policy = e->next;
158
159
160
    tor_free(e->string);
    tor_free(e->address);
    tor_free(e->port);
161
162
163
    free(e);
  }
  free(router);
164
165
}

166
void directory_free(directory_t *dir)
167
{
168
  int i;
169
170
  for (i = 0; i < dir->n_routers; ++i)
    routerinfo_free(dir->routers[i]);
171
172
  tor_free(dir->routers);
  tor_free(dir->software_versions);
173
  free(dir);
174
175
}

176
177
void router_mark_as_down(char *nickname) {
  routerinfo_t *router = router_get_by_nickname(nickname);
178
179
  if(!router) /* we don't seem to know about him in the first place */
    return;
180
181
  log_fn(LOG_DEBUG,"Marking %s as down.",router->nickname);
  router->is_running = 0;
182
183
}

Roger Dingledine's avatar
Roger Dingledine committed
184
/* load the router list */
185
int router_get_list_from_file(char *routerfile)
Roger Dingledine's avatar
Roger Dingledine committed
186
{
187
  char *string;
Roger Dingledine's avatar
Roger Dingledine committed
188

189
190
  string = read_file_to_str(routerfile);
  if(!string) {
Roger Dingledine's avatar
Roger Dingledine committed
191
    log_fn(LOG_WARN,"Failed to load routerfile %s.",routerfile);
192
    return -1;
Roger Dingledine's avatar
Roger Dingledine committed
193
194
  }
  
195
  if(router_get_list_from_string(string) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
196
    log_fn(LOG_WARN,"The routerfile itself was corrupt.");
197
198
199
200
201
202
203
204
    free(string);
    return -1;
  }

  free(string);
  return 0;
} 

205
206
207
208

typedef enum {
  K_ACCEPT,
  K_DIRECTORY_SIGNATURE,
209
  K_RECOMMENDED_SOFTWARE,
210
211
212
213
  K_REJECT, 
  K_ROUTER, 
  K_SIGNED_DIRECTORY,
  K_SIGNING_KEY,
214
215
216
  K_ONION_KEY,
  K_LINK_KEY,
  K_ROUTER_SIGNATURE,
217
  K_PUBLISHED,
218
  K_RUNNING_ROUTERS,
219
  K_PLATFORM,
220
221
222
223
224
225
226
227
228
229
230
231
232
  _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 },
233
  { "recommended-software", K_RECOMMENDED_SOFTWARE },
234
235
  { "signed-directory", K_SIGNED_DIRECTORY },
  { "signing-key", K_SIGNING_KEY },
236
237
238
  { "onion-key", K_ONION_KEY },
  { "link-key", K_LINK_KEY },
  { "router-signature", K_ROUTER_SIGNATURE },
239
  { "published", K_PUBLISHED },
240
  { "running-routers", K_RUNNING_ROUTERS },
241
  { "platform", K_PLATFORM },
242
243
244
  { NULL, -1 }
};

245
#define MAX_ARGS 1024
246
247
248
249
250
251
252
253
254
255
256
257
258
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;
};

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/* 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;
    }
}

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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; }
      
320
      signature = tor_malloc(256);
321
322
323
324
325
326
327
328
329
330
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
      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;
  }
}

369
#ifdef DEBUG_ROUTER_TOKENS
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
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;
390
    case K_RECOMMENDED_SOFTWARE: printf("Server-Software"); break;
391
392
393
    case K_ROUTER: printf("Router"); break;
    case K_SIGNED_DIRECTORY: printf("Signed-Directory"); break;
    case K_SIGNING_KEY: printf("Signing-Key"); break;
394
395
396
    case K_ONION_KEY: printf("Onion-key"); break;
    case K_LINK_KEY: printf("Link-key"); break;
    case K_ROUTER_SIGNATURE: printf("Router-signature"); break;
397
    case K_PUBLISHED: printf("Published"); break;
398
    case K_RUNNING_ROUTERS: printf("Running-routers"); break;
399
    case K_PLATFORM: printf("Platform"); break;
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    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;
}

454
455
int router_get_list_from_string(char *s) 
{
456
  if (router_get_list_from_string_impl(&s, &directory, -1, NULL)) {
Roger Dingledine's avatar
Roger Dingledine committed
457
    log(LOG_WARN, "Error parsing router file");
Nick Mathewson's avatar
Nick Mathewson committed
458
459
460
    return -1;
  }
  if (router_resolve_directory(directory)) {
Roger Dingledine's avatar
Roger Dingledine committed
461
    log(LOG_WARN, "Error resolving directory");
Nick Mathewson's avatar
Nick Mathewson committed
462
463
464
    return -1;
  }
  return 0;
465
466
}

467
468
static int router_get_hash_impl(char *s, char *digest, const char *start_str,
                                const char *end_str) 
469
470
{
  char *start, *end;
471
  start = strstr(s, start_str);
Nick Mathewson's avatar
Nick Mathewson committed
472
  if (!start) {
Roger Dingledine's avatar
Roger Dingledine committed
473
    log_fn(LOG_WARN,"couldn't find \"%s\"",start_str);
Nick Mathewson's avatar
Nick Mathewson committed
474
475
    return -1;
  }
476
  end = strstr(start+strlen(start_str), end_str);
Nick Mathewson's avatar
Nick Mathewson committed
477
  if (!end) {
Roger Dingledine's avatar
Roger Dingledine committed
478
    log_fn(LOG_WARN,"couldn't find \"%s\"",end_str);
Nick Mathewson's avatar
Nick Mathewson committed
479
480
    return -1;
  }
481
  end = strchr(end, '\n');
Nick Mathewson's avatar
Nick Mathewson committed
482
  if (!end) {
Roger Dingledine's avatar
Roger Dingledine committed
483
    log_fn(LOG_WARN,"couldn't find EOL");
Nick Mathewson's avatar
Nick Mathewson committed
484
485
    return -1;
  }
486
487
  ++end;
  
Nick Mathewson's avatar
Nick Mathewson committed
488
  if (crypto_SHA_digest(start, end-start, digest)) {
Roger Dingledine's avatar
Roger Dingledine committed
489
    log_fn(LOG_WARN,"couldn't compute digest");
490
    return -1;
Nick Mathewson's avatar
Nick Mathewson committed
491
  }
492
493
494
495

  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
496
int router_get_dir_hash(char *s, char *digest)
497
498
499
500
501
502
503
504
505
506
{
  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");
}

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
/* 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;
  }
}

526
527
int router_get_dir_from_string(char *s, crypto_pk_env_t *pkey)
{
Nick Mathewson's avatar
Nick Mathewson committed
528
  if (router_get_dir_from_string_impl(s, &directory, pkey)) {
Roger Dingledine's avatar
Roger Dingledine committed
529
    log_fn(LOG_WARN, "Couldn't parse directory.");
Nick Mathewson's avatar
Nick Mathewson committed
530
531
532
    return -1;
  }
  if (router_resolve_directory(directory)) {
Roger Dingledine's avatar
Roger Dingledine committed
533
    log_fn(LOG_WARN, "Error resolving directory");
Nick Mathewson's avatar
Nick Mathewson committed
534
535
    return -1;
  }
536
  if (compare_recommended_versions(VERSION, directory->software_versions) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
537
    log(LOG_WARN, "You are running tor version %s, which is no longer supported.\nPlease upgrade to one of %s.", VERSION, directory->software_versions);
538
    if(options.IgnoreVersion) {
Roger Dingledine's avatar
Roger Dingledine committed
539
      log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so.");
540
    } else {
541
      log(LOG_ERR,"Set IgnoreVersion config variable if you want to proceed.");
542
543
544
545
546
      fflush(0);
      exit(0);
    }
  }

Nick Mathewson's avatar
Nick Mathewson committed
547
  return 0;
548
549
550
551
552
553
554
555
}

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];
556
  directory_t *new_dir = NULL;
557
  char *versions;
558
559
560
561
  struct tm published;
  time_t published_on;
  const char *good_nickname_lst[1024];
  int n_good_nicknames;
562
563
564
565
  
#define NEXT_TOK()                                                      \
  do {                                                                  \
    if (router_get_next_token(&s, &tok)) {                              \
Roger Dingledine's avatar
Roger Dingledine committed
566
      log_fn(LOG_WARN, "Error reading directory: %s", tok.val.error);\
567
568
569
570
571
      return -1;                                                        \
    } } while (0)
#define TOK_IS(type,name)                                               \
  do {                                                                  \
    if (tok.tp != type) {                                               \
572
      router_release_token(&tok);                                       \
Roger Dingledine's avatar
Roger Dingledine committed
573
      log_fn(LOG_WARN, "Error reading directory: expected %s", name);\
574
575
      return -1;                                                        \
    } } while(0)
576

Nick Mathewson's avatar
Nick Mathewson committed
577
  if (router_get_dir_hash(s, digest)) {
Roger Dingledine's avatar
Roger Dingledine committed
578
    log_fn(LOG_WARN, "Unable to compute digest of directory");
579
    goto err;
Nick Mathewson's avatar
Nick Mathewson committed
580
  }
581
582
583
584
  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);

585
586
587
  NEXT_TOK();
  TOK_IS(K_SIGNED_DIRECTORY, "signed-directory");

588
589
590
  NEXT_TOK();
  TOK_IS(K_PUBLISHED, "published");
  if (tok.val.cmd.n_args != 2) {
Roger Dingledine's avatar
Roger Dingledine committed
591
    log_fn(LOG_WARN, "Invalid published line");
592
593
594
595
    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
596
    log_fn(LOG_WARN, "Published time was unparseable"); goto err;
597
  }
598
  published_on = tor_timegm(&published);  
599

600
  NEXT_TOK();
601
602
  TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software");
  if (tok.val.cmd.n_args != 1) {
Roger Dingledine's avatar
Roger Dingledine committed
603
    log_fn(LOG_WARN, "Invalid recommended-software line");
604
    goto err;
605
  }
606
  versions = tor_strdup(tok.val.cmd.args[0]);
607
  
608
609
610
  NEXT_TOK();
  TOK_IS(K_RUNNING_ROUTERS, "running-routers");
  n_good_nicknames = tok.val.cmd.n_args;
611
  memcpy(good_nickname_lst, tok.val.cmd.args, n_good_nicknames*sizeof(char *));
612
613
614

  if (router_get_list_from_string_impl(&s, &new_dir,
                                       n_good_nicknames, good_nickname_lst)) {
Roger Dingledine's avatar
Roger Dingledine committed
615
    log_fn(LOG_WARN, "Error reading routers from directory");
616
    goto err;
Nick Mathewson's avatar
Nick Mathewson committed
617
  }
618
  new_dir->software_versions = versions;
619
  new_dir->published_on = published_on;
620
621

  NEXT_TOK();
622
623
624
625
626
627
  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
628
      log_fn(LOG_WARN, "Error reading directory: invalid signature.");
629
      free(tok.val.signature);
630
      goto err;
631
    }
632
633
634
    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);
635
    if (memcmp(digest, signed_digest, 20)) {
Roger Dingledine's avatar
Roger Dingledine committed
636
      log_fn(LOG_WARN, "Error reading directory: signature does not match.");
637
      free(tok.val.signature);
638
      goto err;
639
640
641
642
643
644
645
    }
  }
  free(tok.val.signature);

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

646
647
648
649
  if (*dest) 
    directory_free(*dest);
  *dest = new_dir;

650
  return 0;
651
652
653
654
655

 err:
  if (new_dir)
    directory_free(new_dir);
  return -1;
656
657
658
659
#undef NEXT_TOK
#undef TOK_IS
}

660
661
662
int router_get_list_from_string_impl(char **s, directory_t **dest, 
                                     int n_good_nicknames, 
                                     const char **good_nickname_lst)
663
{
664
  routerinfo_t *router;
665
666
  routerinfo_t **rarray;
  int rarray_len = 0;
667
  int i;
668

669
  assert(s && *s);
670

671
  rarray = (routerinfo_t **)tor_malloc((sizeof(routerinfo_t *))*MAX_ROUTERS_IN_DIR);
672

673
674
675
676
677
  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
678
    if (!router) {
Roger Dingledine's avatar
Roger Dingledine committed
679
      log_fn(LOG_WARN, "Error reading router");
680
681
682
      for(i=0;i<rarray_len;i++)
        routerinfo_free(rarray[i]);
      free(rarray);
Nick Mathewson's avatar
Nick Mathewson committed
683
684
      return -1;
    }
685
    if (rarray_len >= MAX_ROUTERS_IN_DIR) {
Roger Dingledine's avatar
Roger Dingledine committed
686
      log_fn(LOG_WARN, "too many routers");
687
688
689
      routerinfo_free(router);
      continue;
    } 
690
691
692
693
694
695
696
697
    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;
        }
      }
698
699
    } else {
      router->is_running = 1; /* start out assuming all dirservers are up */
700
    }
701
    rarray[rarray_len++] = router;
702
    log_fn(LOG_DEBUG,"just added router #%d.",rarray_len);
Roger Dingledine's avatar
Roger Dingledine committed
703
  }
704
 
705
706
  if (*dest) 
    directory_free(*dest);
707
  *dest = (directory_t *)tor_malloc(sizeof(directory_t));
708
709
  (*dest)->routers = rarray;
  (*dest)->n_routers = rarray_len;
Roger Dingledine's avatar
Roger Dingledine committed
710
  (*dest)->software_versions = NULL;
711
  return 0;
712
}
713

Roger Dingledine's avatar
Roger Dingledine committed
714
static int 
715
716
717
718
719
720
router_resolve(routerinfo_t *router)
{
  struct hostent *rent;

  rent = (struct hostent *)gethostbyname(router->address);
  if (!rent) {
Roger Dingledine's avatar
Roger Dingledine committed
721
    log_fn(LOG_WARN,"Could not get address for router %s.",router->address);
722
723
724
725
726
727
728
729
730
    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
731
static int 
732
733
router_resolve_directory(directory_t *dir)
{
734
  int i, max, remove;
735
736
737
738
739
  if (!dir)
    dir = directory;

  max = dir->n_routers;
  for (i = 0; i < max; ++i) {
740
    remove = 0;
741
    if (router_resolve(dir->routers[i])) {
Roger Dingledine's avatar
Roger Dingledine committed
742
      log_fn(LOG_WARN, "Couldn't resolve router %s; removing",
743
             dir->routers[i]->address);
744
      remove = 1;
Roger Dingledine's avatar
Roger Dingledine committed
745
746
    } else if (options.Nickname &&
               !strcmp(dir->routers[i]->nickname, options.Nickname)) {
747
748
749
      remove = 1;
    }
    if (remove) {
Roger Dingledine's avatar
Roger Dingledine committed
750
      routerinfo_free(dir->routers[i]);
751
752
      dir->routers[i] = dir->routers[--max];
      --dir->n_routers;
753
      --i;
754
755
756
757
758
759
    }
  }
  
  return 0;
}

760
761
762
763
/* 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.
 */
764
routerinfo_t *router_get_entry_from_string(char**s) {
765
  routerinfo_t *router = NULL;
766
767
768
769
  char signed_digest[128];
  char digest[128];
  directory_token_t _tok;
  directory_token_t *tok = &_tok;
770
  struct tm published;
771
772
  int t;

773
774
#define NEXT_TOKEN()                                                     \
  do { if (router_get_next_token(s, tok)) {                              \
Roger Dingledine's avatar
Roger Dingledine committed
775
      log_fn(LOG_WARN, "Error reading directory: %s", tok->val.error);\
776
      goto err;                                                          \
777
    } } while(0)
778
779
780

#define ARGS tok->val.cmd.args

781
  if (router_get_router_hash(*s, digest) < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
782
    log_fn(LOG_WARN, "Couldn't compute router hash.");
783
    return NULL;
784
  }
785
786
787

  NEXT_TOKEN();

788
  if (tok->tp != K_ROUTER) {
789
    router_release_token(tok);
Roger Dingledine's avatar
Roger Dingledine committed
790
    log_fn(LOG_WARN,"Entry does not start with \"router\"");
791
792
    return NULL;
  }
793
794

  router = tor_malloc(sizeof(routerinfo_t));
795
  memset(router,0,sizeof(routerinfo_t)); /* zero it out first */
796
  router->onion_pkey = router->identity_pkey = router->link_pkey = NULL; 
797

798
  if (tok->val.cmd.n_args != 6) {
Roger Dingledine's avatar
Roger Dingledine committed
799
    log_fn(LOG_WARN,"Wrong # of arguments to \"router\"");
800
801
    goto err;
  }
802
  router->nickname = tor_strdup(ARGS[0]);
803
  if (strlen(router->nickname) > MAX_NICKNAME_LEN) {
Roger Dingledine's avatar
Roger Dingledine committed
804
    log_fn(LOG_WARN,"Router nickname too long.");
805
    goto err;
806
  }
807
  if (strspn(router->nickname, LEGAL_NICKNAME_CHARACTERS) != 
808
      strlen(router->nickname)) {
Roger Dingledine's avatar
Roger Dingledine committed
809
    log_fn(LOG_WARN, "Router nickname contains illegal characters.");
810
    goto err;
811
  }
812
  
813
  /* read router.address */
814
  router->address = tor_strdup(ARGS[1]);
815
  router->addr = 0;
816

817
  /* Read router->or_port */
818
  router->or_port = atoi(ARGS[2]);
819
  if(!router->or_port) {
Roger Dingledine's avatar
Roger Dingledine committed
820
    log_fn(LOG_WARN,"or_port unreadable or 0. Failing.");
821
    goto err;
822
823
  }
  
824
825
  /* Router->socks_port */
  router->socks_port = atoi(ARGS[3]);
826
  
827
  /* Router->dir_port */
828
  router->dir_port = atoi(ARGS[4]);
829

830
  /* Router->bandwidth */
831
  router->bandwidth = atoi(ARGS[5]);
832
  if (!router->bandwidth) {
Roger Dingledine's avatar
Roger Dingledine committed
833
    log_fn(LOG_WARN,"bandwidth unreadable or 0. Failing.");
834
    goto err;
835
  }
836
  
837
838
  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);
839

840
  /* XXX Later, require platform before published. */
841
  NEXT_TOKEN();
842
843
844
845
  if (tok->tp == K_PLATFORM) {
    NEXT_TOKEN();
  }
  
846
  if (tok->tp != K_PUBLISHED) {
Roger Dingledine's avatar
Roger Dingledine committed
847
    log_fn(LOG_WARN, "Missing published time"); goto err;
848
849
  }
  if (tok->val.cmd.n_args != 2) {
Roger Dingledine's avatar
Roger Dingledine committed
850
    log_fn(LOG_WARN, "Wrong number of arguments to published"); goto err;
851
  }
852
853
  ARGS[1][-1] = ' '; /* Re-insert space. */
  if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) {
Roger Dingledine's avatar
Roger Dingledine committed
854
    log_fn(LOG_WARN, "Published time was unparseable"); goto err;
855
  }
856
  router->published_on = tor_timegm(&published);
857

858
859
  NEXT_TOKEN();
  if (tok->tp != K_ONION_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
860
    log_fn(LOG_WARN, "Missing onion-key"); goto err;
861
  }
862
  NEXT_TOKEN();
863
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
864
    log_fn(LOG_WARN, "Missing onion key"); goto err;
865
866
  } /* XXX Check key length */
  router->onion_pkey = tok->val.public_key;
867
868

  NEXT_TOKEN();
869
  if (tok->tp != K_LINK_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
870
    log_fn(LOG_WARN, "Missing link-key");  goto err;
871
872
873
  }
  NEXT_TOKEN();
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
874
    log_fn(LOG_WARN, "Missing link key"); goto err;
875
876
877
878
879
  } /* 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
880
    log_fn(LOG_WARN, "Missing signing-key"); goto err;
881
882
883
  }
  NEXT_TOKEN();
  if (tok->tp != _PUBLIC_KEY) {
Roger Dingledine's avatar
Roger Dingledine committed
884
    log_fn(LOG_WARN, "Missing signing key"); goto err;
885
886
  }
  router->identity_pkey = tok->val.public_key;
887

888
  NEXT_TOKEN();
889
890
891
  while (tok->tp == K_ACCEPT || tok->tp == K_REJECT) {
    router_add_exit_policy(router, tok);
    NEXT_TOKEN();
Roger Dingledine's avatar
Roger Dingledine committed
892
  }
893
  
894
  if (tok->tp != K_ROUTER_SIGNATURE) {
Roger Dingledine's avatar
Roger Dingledine committed
895
    log_fn(LOG_WARN,"Missing router signature");
896
897
898
899
    goto err;
  }
  NEXT_TOKEN();
  if (tok->tp != _SIGNATURE) {
Roger Dingledine's avatar
Roger Dingledine committed
900
    log_fn(LOG_WARN,"Missing router signature");
901
902
903
    goto err;
  }
  assert (router->identity_pkey);
904

905
906
  if ((t=crypto_pk_public_checksig(router->identity_pkey, tok->val.signature,
                                   128, signed_digest)) != 20) {
Roger Dingledine's avatar
Roger Dingledine committed
907
    log_fn(LOG_WARN, "Invalid signature %d",t);
908
909
910
    goto err;
  }
  if (memcmp(digest, signed_digest, 20)) {
Roger Dingledine's avatar
Roger Dingledine committed
911
    log_fn(LOG_WARN, "Mismatched signature");
912
913
914
    goto err;
  }
  
915
  router_release_token(tok); /* free the signature */
Roger Dingledine's avatar
Roger Dingledine committed
916
  return router;
917

918
 err:
919
  router_release_token(tok); 
920
  routerinfo_free(router);
921
  return NULL;
922
923
#undef ARGS
#undef NEXT_TOKEN
924
925
}

926
927
928
void router_add_exit_policy_from_config(routerinfo_t *router) {
  char *s = options.ExitPolicy, *e;
  int last=0;
929
  char line[1024];
930
931
932
933
934
935
936
937
938
939
940
941

  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,',');
942
    if(!e) {
943
      last = 1;
944
945
946
947
948
949
950
      strcpy(line,s);
    } else {
      memcpy(line,s,e-s);
      line[e-s] = 0;
    }
    log_fn(LOG_DEBUG,"Adding new entry '%s'",line);
    if(router_add_exit_policy_from_string(router,line) < 0)
Roger Dingledine's avatar
Roger Dingledine committed
951
      log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line);
952
953
954
955
956
957
958
959
960
    if(last)
      return;
    s = e+1;
  }
}

static int
router_add_exit_policy_from_string(routerinfo_t *router,
                                   char *s)
961
962
963
964
{
  directory_token_t tok;
  char *tmp, *cp;
  int r;
Nick Mathewson's avatar
Nick Mathewson committed
965
  int len, idx;
966

967
968
  len = strlen(s);
  tmp = cp = tor_malloc(len+2);
Nick Mathewson's avatar
Nick Mathewson committed
969
970
971
  for (idx = 0; idx < len; ++idx) {
    tmp[idx] = tolower(s[idx]);
  }
972
973
  tmp[len]='\n';
  tmp[len+1]='\0';
974
  if (router_get_next_token(&cp, &tok)) {
Roger Dingledine's avatar
Roger Dingledine committed
975
    log_fn(LOG_WARN, "Error reading exit policy: %s", tok.val.error);
976
977
978
979
    free(tmp);
    return -1;                                                        
  }
  if (tok.tp != K_ACCEPT && tok.tp != K_REJECT) {
Roger Dingledine's avatar
Roger Dingledine committed
980
    log_fn(LOG_WARN, "Expected 'accept' or 'reject'.");
981
982
983
984
985
986
987
988
    free(tmp);
    return -1;
  }
  r = router_add_exit_policy(router, &tok);
  free(tmp);
  return r;
}

989
990
static int router_add_exit_policy(routerinfo_t *router, 
                                  directory_token_t *tok) {
Roger Dingledine's avatar
Roger Dingledine committed
991
  struct exit_policy_t *tmpe, *newe;
992
  char *arg, *colon;
Roger Dingledine's avatar
Roger Dingledine committed
993

994
995
996
  if (tok->val.cmd.n_args != 1)
    return -1;
  arg = tok->val.cmd.args[0];
Roger Dingledine's avatar
Roger Dingledine committed
997

998
  newe = tor_malloc(sizeof(struct exit_policy_t));
Roger Dingledine's avatar
Roger Dingledine committed
999
  memset(newe,0,sizeof(struct exit_policy_t));
1000
  
For faster browsing, not all history is shown. View entire blame