dirserv.c 109 KB
Newer Older
1
2
/* Copyright (c) 2001-2004, Roger Dingledine.
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2008, The Tor Project, Inc. */
4
5
/* See LICENSE for licensing information */
/* $Id$ */
6
7
const char dirserv_c_id[] =
  "$Id$";
8

9
#define DIRSERV_PRIVATE
10
11
#include "or.h"

Roger Dingledine's avatar
Roger Dingledine committed
12
13
/**
 * \file dirserv.c
14
15
 * \brief Directory server core implementation. Manages directory
 * contents and generates directories.
16
 */
Nick Mathewson's avatar
Nick Mathewson committed
17

Roger Dingledine's avatar
Roger Dingledine committed
18
/** How far in the future do we allow a router to get? (seconds) */
19
#define ROUTER_ALLOW_SKEW (60*60*12)
20
/** How many seconds do we wait before regenerating the directory? */
21
#define DIR_REGEN_SLACK_TIME 30
22
23
24
/** If we're a cache, keep this many networkstatuses around from non-trusted
 * directory authorities. */
#define MAX_UNTRUSTED_NETWORKSTATUSES 16
Nick Mathewson's avatar
Nick Mathewson committed
25

26
27
28
29
30
/** If a v1 directory is older than this, discard it. */
#define MAX_V1_DIRECTORY_AGE (30*24*60*60)
/** If a v1 running-routers is older than this, discard it. */
#define MAX_V1_RR_AGE (7*24*60*60)

31
32
extern time_t time_of_process_start; /* from main.c */

33
/** Do we need to regenerate the v1 directory when someone asks for it? */
34
static time_t the_directory_is_dirty = 1;
35
36
/** Do we need to regenerate the v1 runningrouters document when somebody
 * asks for it? */
37
static time_t runningrouters_is_dirty = 1;
38
39
/** Do we need to regenerate our v2 networkstatus document when somebody asks
 * for it? */
40
static time_t the_v2_networkstatus_is_dirty = 1;
41

42
43
44
45
46
47
48
/** Most recently generated encoded signed v1 directory. (v1 auth dirservers
 * only.) */
static cached_dir_t *the_directory = NULL;

/** For authoritative directories: the current (v1) network status. */
static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };

49
static void directory_remove_invalid(void);
50
static cached_dir_t *dirserv_regenerate_directory(void);
51
static char *format_versions_list(config_line_t *ln);
52
struct authdir_config_t;
53
54
static int add_fingerprint_to_dir(const char *nickname, const char *fp,
                                  struct authdir_config_t *list);
55
56
57
static uint32_t dirserv_router_get_status(const routerinfo_t *router,
                                          const char **msg);
static uint32_t
58
59
60
61
62
dirserv_get_status_impl(const char *fp, const char *nickname,
                        const char *address,
                        uint32_t addr, uint16_t or_port,
                        const char *platform, const char *contact,
                        const char **msg, int should_log);
63
static void clear_cached_dir(cached_dir_t *d);
64
65
66
static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
                                                        int extrainfo,
                                                        time_t publish_cutoff);
67
68
static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);

69
70
/************** Fingerprint handling code ************/

71
72
73
#define FP_NAMED   1  /**< Listed in fingerprint file. */
#define FP_INVALID 2  /**< Believed invalid. */
#define FP_REJECT  4  /**< We will not publish this router. */
74
75
76
#define FP_BADDIR  8  /**< We'll tell clients to avoid using this as a dir. */
#define FP_BADEXIT 16  /**< We'll tell clients not to use this as an exit. */
#define FP_UNNAMED 32 /**< Another router has this name in fingerprint file. */
77

78
79
/** Encapsulate a nickname and an FP_* status; target of status_by_digest
 * map. */
80
81
82
83
typedef struct router_status_t {
  char nickname[MAX_NICKNAME_LEN+1];
  uint32_t status;
} router_status_t;
84

Nick Mathewson's avatar
Nick Mathewson committed
85
/** List of nickname-\>identity fingerprint mappings for all the routers
86
 * that we name.  Used to prevent router impersonation. */
87
typedef struct authdir_config_t {
Roger Dingledine's avatar
Roger Dingledine committed
88
89
  strmap_t *fp_by_name; /**< Map from lc nickname to fingerprint. */
  digestmap_t *status_by_digest; /**< Map from digest to router_status_t. */
90
91
} authdir_config_t;

Roger Dingledine's avatar
Roger Dingledine committed
92
/** Should be static; exposed for testing. */
93
static authdir_config_t *fingerprint_list = NULL;
94

95
/** Allocate and return a new, empty, authdir_config_t. */
96
97
98
99
100
101
102
103
static authdir_config_t *
authdir_config_new(void)
{
  authdir_config_t *list = tor_malloc_zero(sizeof(authdir_config_t));
  list->fp_by_name = strmap_new();
  list->status_by_digest = digestmap_new();
  return list;
}
104

Nick Mathewson's avatar
Nick Mathewson committed
105
/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
106
107
 * the smartlist of fingerprint_entry_t's <b>list</b>. Return 0 if it's
 * new, or 1 if we replaced the old value.
Nick Mathewson's avatar
Nick Mathewson committed
108
 */
109
/* static */ int
110
111
add_fingerprint_to_dir(const char *nickname, const char *fp,
                       authdir_config_t *list)
Nick Mathewson's avatar
Nick Mathewson committed
112
{
113
  char *fingerprint;
114
115
  char d[DIGEST_LEN];
  router_status_t *status;
116
117
118
  tor_assert(nickname);
  tor_assert(fp);
  tor_assert(list);
119

120
  fingerprint = tor_strdup(fp);
121
  tor_strstrip(fingerprint, " ");
122
123
124
  if (base16_decode(d, DIGEST_LEN, fingerprint, strlen(fingerprint))) {
    log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
             escaped(fp));
125
    tor_free(fingerprint);
126
127
    return 0;
  }
128

129
130
131
  if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) {
    log_warn(LD_DIRSERV, "Tried to add a mapping for reserved nickname %s",
             UNNAMED_ROUTER_NICKNAME);
132
    tor_free(fingerprint);
133
134
135
    return 0;
  }

136
137
138
139
140
141
  status = digestmap_get(list->status_by_digest, d);
  if (!status) {
    status = tor_malloc_zero(sizeof(router_status_t));
    digestmap_set(list->status_by_digest, d, status);
  }

142
  if (nickname[0] != '!') {
143
    char *old_fp = strmap_get_lc(list->fp_by_name, nickname);
144
145
146
147
148
    if (old_fp && !strcasecmp(fingerprint, old_fp)) {
      tor_free(fingerprint);
    } else {
      tor_free(old_fp);
      strmap_set_lc(list->fp_by_name, nickname, fingerprint);
Nick Mathewson's avatar
Nick Mathewson committed
149
    }
150
151
152
    status->status |= FP_NAMED;
    strlcpy(status->nickname, nickname, sizeof(status->nickname));
  } else {
153
    tor_free(fingerprint);
154
155
156
157
    if (!strcasecmp(nickname, "!reject")) {
      status->status |= FP_REJECT;
    } else if (!strcasecmp(nickname, "!invalid")) {
      status->status |= FP_INVALID;
158
159
    } else if (!strcasecmp(nickname, "!baddir")) {
      status->status |= FP_BADDIR;
160
161
162
    } else if (!strcasecmp(nickname, "!badexit")) {
      status->status |= FP_BADEXIT;
    }
Nick Mathewson's avatar
Nick Mathewson committed
163
  }
164
  return 0;
Nick Mathewson's avatar
Nick Mathewson committed
165
166
}

167
168
/** Add the nickname and fingerprint for this OR to the
 * global list of recognized identity key fingerprints. */
Nick Mathewson's avatar
Nick Mathewson committed
169
170
171
172
int
dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
{
  char fp[FINGERPRINT_LEN+1];
173
  if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
174
    log_err(LD_BUG, "Error computing fingerprint");
Nick Mathewson's avatar
Nick Mathewson committed
175
176
    return -1;
  }
177
  if (!fingerprint_list)
178
    fingerprint_list = authdir_config_new();
179
  add_fingerprint_to_dir(nickname, fp, fingerprint_list);
Nick Mathewson's avatar
Nick Mathewson committed
180
181
182
  return 0;
}

183
184
185
186
187
188
/** Load the nickname-\>fingerprint mappings stored in the approved-routers
 * file.  The file format is line-based, with each non-blank holding one
 * nickname, some space, and a fingerprint for that nickname.  On success,
 * replace the current fingerprint list with the new list and return 0.  On
 * failure, leave the current fingerprint list untouched, and
 * return -1. */
189
int
190
dirserv_load_fingerprint_file(void)
191
{
192
  char *fname;
193
  char *cf;
194
  char *nickname, *fingerprint;
195
  authdir_config_t *fingerprint_list_new;
196
  int result;
197
  config_line_t *front=NULL, *list;
198
199
  or_options_t *options = get_options();

200
  fname = get_datadir_fname("approved-routers");
201
202
  log_info(LD_GENERAL,
           "Reloading approved fingerprints from \"%s\"...", fname);
203

204
  cf = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
205
  if (!cf) {
206
    if (options->NamingAuthoritativeDir) {
207
      log_warn(LD_FS, "Cannot open fingerprint file '%s'. Failing.", fname);
208
      tor_free(fname);
209
210
      return -1;
    } else {
Roger Dingledine's avatar
Roger Dingledine committed
211
      log_info(LD_FS, "Cannot open fingerprint file '%s'. That's ok.", fname);
212
      tor_free(fname);
213
214
      return 0;
    }
215
  }
216
217
  tor_free(fname);

218
219
220
  result = config_get_lines(cf, &front);
  tor_free(cf);
  if (result < 0) {
221
    log_warn(LD_CONFIG, "Error reading from fingerprint file");
222
223
224
    return -1;
  }

225
  fingerprint_list_new = authdir_config_new();
226

227
  for (list=front; list; list=list->next) {
228
    char digest_tmp[DIGEST_LEN];
229
    nickname = list->key; fingerprint = list->value;
230
    if (strlen(nickname) > MAX_NICKNAME_LEN) {
231
232
233
      log_notice(LD_CONFIG,
                 "Nickname '%s' too long in fingerprint file. Skipping.",
                 nickname);
234
      continue;
235
    }
236
    if (!is_legal_nickname(nickname) &&
Nick Mathewson's avatar
Nick Mathewson committed
237
        strcasecmp(nickname, "!reject") &&
238
239
        strcasecmp(nickname, "!invalid") &&
        strcasecmp(nickname, "!badexit")) {
240
241
242
      log_notice(LD_CONFIG,
                 "Invalid nickname '%s' in fingerprint file. Skipping.",
                 nickname);
243
244
      continue;
    }
245
246
247
248
    tor_strstrip(fingerprint, " "); /* remove spaces */
    if (strlen(fingerprint) != HEX_DIGEST_LEN ||
        base16_decode(digest_tmp, sizeof(digest_tmp),
                      fingerprint, HEX_DIGEST_LEN) < 0) {
249
250
251
252
      log_notice(LD_CONFIG,
                 "Invalid fingerprint (nickname '%s', "
                 "fingerprint %s). Skipping.",
                 nickname, fingerprint);
253
      continue;
254
    }
255
    if (0==strcasecmp(nickname, DEFAULT_CLIENT_NICKNAME)) {
256
257
      /* If you approved an OR called "client", then clients who use
       * the default nickname could all be rejected.  That's no good. */
258
      log_notice(LD_CONFIG,
259
                 "Authorizing nickname '%s' would break "
260
261
262
263
264
265
                 "many clients; skipping.",
                 DEFAULT_CLIENT_NICKNAME);
      continue;
    }
    if (0==strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) {
      /* If you approved an OR called "unnamed", then clients will be
266
       * confused. */
267
      log_notice(LD_CONFIG,
268
                 "Authorizing nickname '%s' is not allowed; skipping.",
269
270
271
                 UNNAMED_ROUTER_NICKNAME);
      continue;
    }
272
273
    if (add_fingerprint_to_dir(nickname, fingerprint, fingerprint_list_new)
        != 0)
274
      log_notice(LD_CONFIG, "Duplicate nickname '%s'.", nickname);
275
  }
276
277
278
279
280

  config_free_lines(front);
  dirserv_free_fingerprint_list();
  fingerprint_list = fingerprint_list_new;
  /* Delete any routers whose fingerprints we no longer recognize */
281
  directory_remove_invalid();
282
  return 0;
283
}
284

Nick Mathewson's avatar
Nick Mathewson committed
285
/** Check whether <b>router</b> has a nickname/identity key combination that
286
 * we recognize from the fingerprint list, or an IP we automatically act on
287
288
289
 * according to our configuration.  Return the appropriate router status.
 *
 * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
290
 * *<b>msg</b> to an explanation of why. */
291
static uint32_t
292
dirserv_router_get_status(const routerinfo_t *router, const char **msg)
293
{
294
  char d[DIGEST_LEN];
295

296
  if (crypto_pk_get_digest(router->identity_pkey, d)) {
297
    log_warn(LD_BUG,"Error computing fingerprint");
298
299
    if (msg)
      *msg = "Bug: Error computing fingerprint";
300
    return FP_REJECT;
301
302
  }

303
  return dirserv_get_status_impl(d, router->nickname,
304
305
306
307
308
309
310
311
312
313
314
                                 router->address,
                                 router->addr, router->or_port,
                                 router->platform, router->contact_info,
                                 msg, 1);
}

/** Return true if there is no point in downloading the router described by
 * <b>rs</b> because this directory would reject it. */
int
dirserv_would_reject_router(routerstatus_t *rs)
{
315
  uint32_t res;
316

317
  res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
318
319
320
321
322
                                "", /* address is only used in logs */
                                rs->addr, rs->or_port,
                                NULL, NULL,
                                NULL, 0);

323
  return (res & FP_REJECT) != 0;
324
325
}

326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/** Helper: Based only on the ID/Nickname combination,
 * return FP_UNNAMED (unnamed), FP_NAMED (named), or 0 (neither).
 */
static uint32_t
dirserv_get_name_status(const char *id_digest, const char *nickname)
{
  char fp[HEX_DIGEST_LEN+1];
  char *fp_by_name;

  base16_encode(fp, sizeof(fp), id_digest, DIGEST_LEN);

  if ((fp_by_name =
       strmap_get_lc(fingerprint_list->fp_by_name, nickname))) {
    if (!strcasecmp(fp, fp_by_name)) {
      return FP_NAMED;
    } else {
      return FP_UNNAMED; /* Wrong fingerprint. */
    }
  }
  return 0;
}

348
349
350
351
352
353
354
/** Helper: As dirserv_get_router_status, but takes the router fingerprint
 * (hex, no spaces), nickname, address (used for logging only), IP address, OR
 * port, platform (logging only) and contact info (logging only) as arguments.
 *
 * If should_log is false, do not log messages.  (There's not much point in
 * logging that we're rejecting servers we'll not download.)
 */
355
356
static uint32_t
dirserv_get_status_impl(const char *id_digest, const char *nickname,
357
358
359
360
361
                        const char *address,
                        uint32_t addr, uint16_t or_port,
                        const char *platform, const char *contact,
                        const char **msg, int should_log)
{
362
  int reject_unlisted = get_options()->AuthDirRejectUnlisted;
363
364
  uint32_t result = 0;
  router_status_t *status_by_digest;
365

366
  if (!fingerprint_list)
367
368
    fingerprint_list = authdir_config_new();

369
  if (should_log)
370
371
    log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
              strmap_size(fingerprint_list->fp_by_name),
372
              digestmap_size(fingerprint_list->status_by_digest));
373

374
375
376
377
378
379
380
381
382
  /* 0.1.1.17-rc was the first version that claimed to be stable, doesn't
   * crash and drop circuits all the time, and is even vaguely compatible with
   * the current network */
  if (platform && !tor_version_as_new_as(platform,"0.1.1.17-rc")) {
    if (msg)
      *msg = "Tor version is far too old to work.";
    return FP_REJECT;
  }

383
384
385
386
387
388
389
390
  result = dirserv_get_name_status(id_digest, nickname);
  if (result & FP_NAMED) {
    if (should_log)
      log_debug(LD_DIRSERV,"Good fingerprint for '%s'",nickname);
  }
  if (result & FP_UNNAMED) {
    if (should_log) {
      char *esc_contact = esc_for_log(contact);
391
      log_info(LD_DIRSERV,
392
393
394
395
396
397
               "Mismatched fingerprint for '%s'. "
               "ContactInfo '%s', platform '%s'.)",
               nickname,
               esc_contact,
               platform ? escaped(platform) : "");
      tor_free(esc_contact);
398
    }
399
400
401
    if (msg)
      *msg = "Rejected: There is already a named server with this nickname "
        "and a different fingerprint.";
402
  }
403

404
405
  status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
                                   id_digest);
406
407
  if (status_by_digest)
    result |= (status_by_digest->status & ~FP_NAMED);
408
409
410
411
412
413
414
415
416

  if (result & FP_REJECT) {
    if (msg)
      *msg = "Fingerprint is marked rejected";
    return FP_REJECT;
  } else if (result & FP_INVALID) {
    if (msg)
      *msg = "Fingerprint is marked invalid";
  }
417

418
419
420
421
422
423
424
425
  if (authdir_policy_baddir_address(addr, or_port)) {
    if (should_log)
      log_info(LD_DIRSERV,
               "Marking '%s' as bad directory because of address '%s'",
               nickname, address);
    result |= FP_BADDIR;
  }

426
427
428
429
430
431
432
433
  if (authdir_policy_badexit_address(addr, or_port)) {
    if (should_log)
      log_info(LD_DIRSERV, "Marking '%s' as bad exit because of address '%s'",
               nickname, address);
    result |= FP_BADEXIT;
  }

  if (!(result & FP_NAMED)) {
434
    if (!authdir_policy_permits_address(addr, or_port)) {
435
      if (should_log)
436
437
        log_info(LD_DIRSERV, "Rejecting '%s' because of address '%s'",
                 nickname, address);
438
439
440
441
      if (msg)
        *msg = "Authdir is rejecting routers in this range.";
      return FP_REJECT;
    }
442
    if (!authdir_policy_valid_address(addr, or_port)) {
443
      if (should_log)
444
445
        log_info(LD_DIRSERV, "Not marking '%s' valid because of address '%s'",
                 nickname, address);
446
      result |= FP_INVALID;
447
    }
448
449
450
    if (reject_unlisted) {
      if (msg)
        *msg = "Authdir rejects unknown routers.";
451
      return FP_REJECT;
452
    }
453
  }
454
455

  return result;
456
457
}

458
/** If we are an authoritative dirserver, and the list of approved
459
460
 * servers contains one whose identity key digest is <b>digest</b>,
 * return that router's nickname.  Otherwise return NULL. */
461
462
const char *
dirserv_get_nickname_by_digest(const char *digest)
463
{
464
  router_status_t *status;
465
  if (!fingerprint_list)
466
467
468
    return NULL;
  tor_assert(digest);

469
470
  status = digestmap_get(fingerprint_list->status_by_digest, digest);
  return status ? status->nickname : NULL;
471
472
}

Roger Dingledine's avatar
Roger Dingledine committed
473
/** Clear the current fingerprint list. */
Roger Dingledine's avatar
Roger Dingledine committed
474
void
475
dirserv_free_fingerprint_list(void)
476
{
477
478
479
  if (!fingerprint_list)
    return;

480
481
482
  strmap_free(fingerprint_list->fp_by_name, _tor_free);
  digestmap_free(fingerprint_list->status_by_digest, _tor_free);
  tor_free(fingerprint_list);
483
484
485
486
487
}

/*
 *    Descriptor list
 */
Nick Mathewson's avatar
Nick Mathewson committed
488

489
490
491
492
493
494
495
496
497
/** Return -1 if <b>ri</b> has a private or otherwise bad address,
 * unless we're configured to not care. Return 0 if all ok. */
static int
dirserv_router_has_valid_address(routerinfo_t *ri)
{
  struct in_addr iaddr;
  if (get_options()->DirAllowPrivateAddresses)
    return 0; /* whatever it is, we're fine with it */
  if (!tor_inet_aton(ri->address, &iaddr)) {
498
499
    log_info(LD_DIRSERV,"Router '%s' published non-IP address '%s'. Refusing.",
             ri->nickname, ri->address);
500
501
    return -1;
  }
502
  if (is_internal_IP(ntohl(iaddr.s_addr), 0)) {
503
504
505
    log_info(LD_DIRSERV,
             "Router '%s' published internal IP address '%s'. Refusing.",
             ri->nickname, ri->address);
506
507
508
509
510
    return -1; /* it's a private IP, we should reject it */
  }
  return 0;
}

511
/** Check whether we, as a directory server, want to accept <b>ri</b>.  If so,
512
 * set its is_valid,named,running fields and return 0.  Otherwise, return -1.
513
 *
514
 * If the router is rejected, set *<b>msg</b> to an explanation of why.
515
516
517
 *
 * If <b>complain</b> then explain at log-level 'notice' why we refused
 * a descriptor; else explain at log-level 'info'.
518
 */
519
int
520
521
authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
                               int complain)
522
523
{
  /* Okay.  Now check whether the fingerprint is recognized. */
524
  uint32_t status = dirserv_router_get_status(ri, msg);
525
  time_t now;
526
  int severity = complain ? LOG_NOTICE : LOG_INFO;
Nick Mathewson's avatar
Nick Mathewson committed
527
  tor_assert(msg);
528
  if (status & FP_REJECT)
529
530
    return -1; /* msg is already set. */

Nick Mathewson's avatar
Nick Mathewson committed
531
  /* Is there too much clock skew? */
532
  now = time(NULL);
533
  if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) {
534
535
    log_fn(severity, LD_DIRSERV, "Publication time for nickname '%s' is too "
           "far (%d minutes) in the future; possible clock skew. Not adding "
536
           "(%s)",
537
           ri->nickname, (int)((ri->cache_info.published_on-now)/60),
538
           esc_router_info(ri));
539
540
    *msg = "Rejected: Your clock is set too far in the future, or your "
      "timezone is not correct.";
541
    return -1;
542
  }
543
  if (ri->cache_info.published_on < now-ROUTER_MAX_AGE_TO_PUBLISH) {
544
    log_fn(severity, LD_DIRSERV,
545
           "Publication time for router with nickname '%s' is too far "
546
           "(%d minutes) in the past. Not adding (%s)",
547
           ri->nickname, (int)((now-ri->cache_info.published_on)/60),
548
           esc_router_info(ri));
549
550
    *msg = "Rejected: Server is expired, or your clock is too far in the past,"
      " or your timezone is not correct.";
551
    return -1;
552
  }
553
  if (dirserv_router_has_valid_address(ri) < 0) {
554
555
    log_fn(severity, LD_DIRSERV,
           "Router with nickname '%s' has invalid address '%s'. "
556
           "Not adding (%s).",
557
           ri->nickname, ri->address,
558
           esc_router_info(ri));
559
560
    *msg = "Rejected: Address is not an IP, or IP is a private address.";
    return -1;
561
  }
562
  /* Okay, looks like we're willing to accept this one. */
563
564
  ri->is_named = (status & FP_NAMED) ? 1 : 0;
  ri->is_valid = (status & FP_INVALID) ? 0 : 1;
565
  ri->is_bad_directory = (status & FP_BADDIR) ? 1 : 0;
566
  ri->is_bad_exit = (status & FP_BADEXIT) ? 1 : 0;
567

568
569
570
  return 0;
}

571
/** True iff <b>a</b> is more severe than <b>b</b>. */
572
573
574
static int
WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
{
575
  return a < b;
576
577
}

578
/** As for dirserv_add_descriptor(), but accepts multiple documents, and
579
 * returns the most severe error that occurred for any one of them. */
580
was_router_added_t
581
dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
582
                                 const char *source,
583
                                 const char **msg)
584
{
585
  was_router_added_t r, r_tmp;
586
  const char *msg_out;
587
588
589
  smartlist_t *list;
  const char *s;
  int n_parsed = 0;
590
  time_t now = time(NULL);
Roger Dingledine's avatar
Roger Dingledine committed
591
  char annotation_buf[ROUTER_ANNOTATION_BUF_LEN];
592
593
  char time_buf[ISO_TIME_LEN+1];
  int general = purpose == ROUTER_PURPOSE_GENERAL;
594
  tor_assert(msg);
595

596
  r=ROUTER_ADDED_SUCCESSFULLY; /*Least severe return value. */
597

598
599
600
601
602
603
604
605
606
607
608
609
  format_iso_time(time_buf, now);
  if (tor_snprintf(annotation_buf, sizeof(annotation_buf),
                   "@uploaded-at %s\n"
                   "@source %s\n"
                   "%s%s%s", time_buf, escaped(source),
                   !general ? "@purpose " : "",
                   !general ? router_purpose_to_string(purpose) : "",
                   !general ? "\n" : "")<0) {
    *msg = "Couldn't format annotations";
    return -1;
  }

610
611
  s = desc;
  list = smartlist_create();
612
  if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0,
613
                                     annotation_buf)) {
614
    SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
615
        msg_out = NULL;
616
        tor_assert(ri->purpose == purpose);
617
        r_tmp = dirserv_add_descriptor(ri, &msg_out);
618
        if (WRA_MORE_SEVERE(r_tmp, r)) {
619
620
621
622
          r = r_tmp;
          *msg = msg_out;
        }
      });
623
  }
624
625
626
627
  n_parsed += smartlist_len(list);
  smartlist_clear(list);

  s = desc;
628
629
  if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0,
                                     NULL)) {
630
    SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
631
        msg_out = NULL;
632

633
        r_tmp = dirserv_add_extrainfo(ei, &msg_out);
634
        if (WRA_MORE_SEVERE(r_tmp, r)) {
635
636
637
638
639
640
641
          r = r_tmp;
          *msg = msg_out;
        }
      });
  }
  n_parsed += smartlist_len(list);
  smartlist_free(list);
642

643
644
645
  if (! *msg) {
    if (!n_parsed) {
      *msg = "No descriptors found in your POST.";
646
647
      if (WRA_WAS_ADDED(r))
        r = ROUTER_WAS_NOT_NEW;
648
649
650
651
652
    } else {
      *msg = "(no message)";
    }
  }

653
  return r;
654
655
}

656
657
658
/** Examine the parsed server descriptor in <b>ri</b> and maybe insert it into
 * the list of server descriptors. Set *<b>msg</b> to a message that should be
 * passed back to the origin of this descriptor.
659
 *
660
 * Return the status of the operation
661
662
663
 *
 * This function is only called when fresh descriptors are posted, not when
 * we re-load the cache.
664
 */
665
was_router_added_t
666
dirserv_add_descriptor(routerinfo_t *ri, const char **msg)
667
{
668
  was_router_added_t r;
669
  routerinfo_t *ri_old;
670
671
  char *desc = NULL;
  size_t desclen = 0;
672

673
674
675
676
677
678
679
680
681
682
683
684
685
  /* If it's too big, refuse it now. Otherwise we'll cache it all over the
   * network and it'll clog everything up. */
  if (ri->cache_info.signed_descriptor_len > MAX_DESCRIPTOR_UPLOAD_SIZE) {
    log_notice(LD_DIR, "Somebody attempted to publish a router descriptor "
               "with size %d. Either this is an attack, or the "
               "MAX_DESCRIPTOR_UPLOAD_SIZE (%d) constant is too low.",
               (int)ri->cache_info.signed_descriptor_len,
               MAX_DESCRIPTOR_UPLOAD_SIZE);
    *msg = "Router descriptor was too large";
    control_event_or_authdir_new_descriptor("REJECTED",
               ri->cache_info.signed_descriptor_body,
               ri->cache_info.signed_descriptor_len, *msg);
    routerinfo_free(ri);
686
    return ROUTER_AUTHDIR_REJECTS;
687
688
  }

689
690
691
692
  /* Check whether this descriptor is semantically identical to the last one
   * from this server.  (We do this here and not in router_add_to_routerlist
   * because we want to be able to accept the newest router descriptor that
   * another authority has, so we all converge on the same one.) */
693
694
  ri_old = router_get_by_digest(ri->cache_info.identity_digest);
  if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
695
696
      && router_differences_are_cosmetic(ri_old, ri)
      && !router_is_me(ri)) {
697
698
699
    log_info(LD_DIRSERV,
             "Not replacing descriptor from '%s'; differences are cosmetic.",
             ri->nickname);
700
701
    *msg = "Not replacing router descriptor; no information has changed since "
      "the last one with this identity.";
702
703
704
    control_event_or_authdir_new_descriptor("DROPPED",
                         ri->cache_info.signed_descriptor_body,
                         ri->cache_info.signed_descriptor_len, *msg);
705
    routerinfo_free(ri);
706
    return ROUTER_WAS_NOT_NEW;
707
  }
708
709
710
711
712
713
714
  if (control_event_is_interesting(EVENT_AUTHDIR_NEWDESCS)) {
    /* Make a copy of desc, since router_add_to_routerlist might free
     * ri and its associated signed_descriptor_t. */
    desclen = ri->cache_info.signed_descriptor_len;
    desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen);
  }

715
716
717
718
  r = router_add_to_routerlist(ri, msg, 0, 0);
  if (!WRA_WAS_ADDED(r)) {
    /* unless the routerinfo was fine, just out-of-date */
    if (WRA_WAS_REJECTED(r) && desc)
719
720
      control_event_or_authdir_new_descriptor("REJECTED", desc, desclen, *msg);
    tor_free(desc);
721
  } else {
722
    smartlist_t *changed;
723
724
    if (desc)
      control_event_or_authdir_new_descriptor("ACCEPTED", desc, desclen, *msg);
725

726
    changed = smartlist_create();
727
728
729
    smartlist_add(changed, ri);
    control_event_descriptors_changed(changed);
    smartlist_free(changed);
730
    if (!*msg) {
731
732
      *msg =  ri->is_valid ? "Descriptor for valid server accepted" :
        "Descriptor for invalid server accepted";
733
    }
734
    tor_free(desc);
735
  }
736
  return r;
737
738
}

739
/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */
740
static was_router_added_t
741
742
743
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
  routerinfo_t *ri;
744
  int r;
745
746
747
748
749
750
751
  tor_assert(msg);
  *msg = NULL;

  ri = router_get_by_digest(ei->cache_info.identity_digest);
  if (!ri) {
    *msg = "No corresponding router descriptor for extra-info descriptor";
    extrainfo_free(ei);
752
    return ROUTER_BAD_EI;
753
  }
754
755
756
757
758
759
760
761
762
763
764

  /* If it's too big, refuse it now. Otherwise we'll cache it all over the
   * network and it'll clog everything up. */
  if (ei->cache_info.signed_descriptor_len > MAX_EXTRAINFO_UPLOAD_SIZE) {
    log_notice(LD_DIR, "Somebody attempted to publish an extrainfo "
               "with size %d. Either this is an attack, or the "
               "MAX_EXTRAINFO_UPLOAD_SIZE (%d) constant is too low.",
               (int)ei->cache_info.signed_descriptor_len,
               MAX_EXTRAINFO_UPLOAD_SIZE);
    *msg = "Extrainfo document was too large";
    extrainfo_free(ei);
765
    return ROUTER_BAD_EI;
766
767
  }

768
  if ((r = routerinfo_incompatible_with_extrainfo(ri, ei, NULL, msg))) {
769
    extrainfo_free(ei);
770
    return r < 0 ? ROUTER_WAS_NOT_NEW : ROUTER_BAD_EI;
771
772
  }
  router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
773
  return ROUTER_ADDED_SUCCESSFULLY;
774
775
}

776
777
778
/** Remove all descriptors whose nicknames or fingerprints no longer
 * are allowed by our fingerprint list. (Descriptors that used to be
 * good can become bad when we reload the fingerprint list.)
Nick Mathewson's avatar
Nick Mathewson committed
779
 */
780
static void
781
directory_remove_invalid(void)
782
783
{
  int i;
784
  int changed = 0;
785
  routerlist_t *rl = router_get_routerlist();
786

787
788
  routerlist_assert_ok(rl);

789
  for (i = 0; i < smartlist_len(rl->routers); ++i) {
790
    const char *msg;
791
    routerinfo_t *ent = smartlist_get(rl->routers, i);
792
793
794
795
    uint32_t r = dirserv_router_get_status(ent, &msg);
    if (r & FP_REJECT) {
      log_info(LD_DIRSERV, "Router '%s' is now rejected: %s",
               ent->nickname, msg?msg:"");
796
797
      routerlist_remove(rl, ent, 0);
      i--;
798
      changed = 1;
799
      continue;
800
    }
801
    if (bool_neq((r & FP_NAMED), ent->is_named)) {
802
803
804
805
806
807
      log_info(LD_DIRSERV,
               "Router '%s' is now %snamed.", ent->nickname,
               (r&FP_NAMED)?"":"un");
      ent->is_named = (r&FP_NAMED)?1:0;
      changed = 1;
    }
808
    if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
809
810
811
812
813
      log_info(LD_DIRSERV, "Router '%s' is now %svalid.", ent->nickname,
               (r&FP_INVALID) ? "in" : "");
      ent->is_valid = (r&FP_INVALID)?0:1;
      changed = 1;
    }
814
815
816
817
818
819
    if (bool_neq((r & FP_BADDIR), ent->is_bad_directory)) {
      log_info(LD_DIRSERV, "Router '%s' is now a %s directory", ent->nickname,
               (r & FP_BADDIR) ? "bad" : "good");
      ent->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
      changed = 1;
    }
820
    if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
821
822
823
824
      log_info(LD_DIRSERV, "Router '%s' is now a %s exit", ent->nickname,
               (r & FP_BADEXIT) ? "bad" : "good");
      ent->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
      changed = 1;
825
826
    }
  }
827
828
  if (changed)
    directory_set_dirty();
829
830

  routerlist_assert_ok(rl);
831
832
}

833
834
835
836
/** Write a list of unregistered descriptors into a newly allocated
 * string and return it. Used by dirserv operators to keep track of
 * fast nodes that haven't registered.
 */
837
838
839
int
getinfo_helper_dirserv_unregistered(control_connection_t *control_conn,
                                    const char *question, char **answer_out)
840
841
842
843
{
  smartlist_t *answerlist;
  char buf[1024];
  char *answer;
844
  int min_bw = atoi(question);
845
  routerlist_t *rl = router_get_routerlist();
846

847
848
849
850
851
852
  (void) control_conn;

  if (strcmpstart(question, "unregistered-servers-"))
    return 0;
  question += strlen("unregistered-servers-");

853
  answerlist = smartlist_create();
854
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ent, {
855
    uint32_t r = dirserv_router_get_status(ent, NULL);
856
    if (router_get_advertised_bandwidth(ent) >= (size_t)min_bw &&
857
        !(r & FP_NAMED)) {
858
859
860
      /* then log this one */
      tor_snprintf(buf, sizeof(buf),
                   "%s: BW %d on '%s'.",
861
                   ent->nickname, router_get_advertised_bandwidth(ent),
862
863
864
                   ent->platform ? ent->platform : "");
      smartlist_add(answerlist, tor_strdup(buf));
    }
865
  });
866
867
868
  answer = smartlist_join_strings(answerlist, "\r\n", 0, NULL);
  SMARTLIST_FOREACH(answerlist, char *, cp, tor_free(cp));
  smartlist_free(answerlist);
869
870
  *answer_out = answer;
  return 0;
871
872
}

Nick Mathewson's avatar
Nick Mathewson committed
873
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
Nick Mathewson's avatar
Nick Mathewson committed
874
875
876
 * directory, we will rebuild it instead of reusing the most recently
 * generated one.
 */
Roger Dingledine's avatar
Roger Dingledine committed
877
void
878
directory_set_dirty(void)
879
{
880
  time_t now = time(NULL);
881
  int set_v1_dirty=0;
882

883
  /* Regenerate stubs only every 8 hours.
884
885
   * XXXX It would be nice to generate less often, but these are just
   * stubs: it doesn't matter. */
886
887
888
889
890
891
892
893
894
895
896
897
898
#define STUB_REGENERATE_INTERVAL (8*60*60)
  if (!the_directory || !the_runningrouters.dir)
    set_v1_dirty = 1;
  else if (the_directory->published < now - STUB_REGENERATE_INTERVAL ||
           the_runningrouters.published < now - STUB_REGENERATE_INTERVAL)
    set_v1_dirty = 1;

  if (set_v1_dirty) {
    if (!the_directory_is_dirty)
      the_directory_is_dirty = now;
    if (!runningrouters_is_dirty)
      runningrouters_is_dirty = now;
  }
899
900
  if (!the_v2_networkstatus_is_dirty)
    the_v2_networkstatus_is_dirty = now;
901
902
}

903
904
/**
 * Allocate and return a description of the status of the server <b>desc</b>,
905
 * for use in a v1-style router-status line.  The server is listed
906
 * as running iff <b>is_live</b> is true.
Nick Mathewson's avatar
Nick Mathewson committed
907
 */
908
static char *
909
list_single_server_status(routerinfo_t *desc, int is_live)