dirserv.c 106 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 */

Roger Dingledine's avatar
Roger Dingledine committed
33
/** Do we need to regenerate the directory when someone asks for it? */
34
35
36
static time_t the_directory_is_dirty = 1;
static time_t runningrouters_is_dirty = 1;
static time_t the_v2_networkstatus_is_dirty = 1;
37

38
39
40
41
42
43
44
/** 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 };

45
static void directory_remove_invalid(void);
46
static cached_dir_t *dirserv_regenerate_directory(void);
47
static char *format_versions_list(config_line_t *ln);
48
struct authdir_config_t;
49
50
static int add_fingerprint_to_dir(const char *nickname, const char *fp,
                                  struct authdir_config_t *list);
51
52
53
static uint32_t dirserv_router_get_status(const routerinfo_t *router,
                                          const char **msg);
static uint32_t
54
55
56
57
58
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);
59
static void clear_cached_dir(cached_dir_t *d);
60
61
62
static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
                                                        int extrainfo,
                                                        time_t publish_cutoff);
63
64
static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);

65
66
/************** Fingerprint handling code ************/

67
68
69
#define FP_NAMED   1  /**< Listed in fingerprint file. */
#define FP_INVALID 2  /**< Believed invalid. */
#define FP_REJECT  4  /**< We will not publish this router. */
70
71
72
#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. */
73

74
75
/** Encapsulate a nickname and an FP_* status; target of status_by_digest
 * map. */
76
77
78
79
typedef struct router_status_t {
  char nickname[MAX_NICKNAME_LEN+1];
  uint32_t status;
} router_status_t;
80

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

Roger Dingledine's avatar
Roger Dingledine committed
88
/** Should be static; exposed for testing. */
89
static authdir_config_t *fingerprint_list = NULL;
90

91
/** Allocate and return a new, empty, authdir_config_t. */
92
93
94
95
96
97
98
99
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;
}
100

Nick Mathewson's avatar
Nick Mathewson committed
101
/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
102
103
 * 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
104
 */
105
/* static */ int
106
107
add_fingerprint_to_dir(const char *nickname, const char *fp,
                       authdir_config_t *list)
Nick Mathewson's avatar
Nick Mathewson committed
108
{
109
  char *fingerprint;
110
111
  char d[DIGEST_LEN];
  router_status_t *status;
112
113
114
  tor_assert(nickname);
  tor_assert(fp);
  tor_assert(list);
115

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

125
126
127
  if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME)) {
    log_warn(LD_DIRSERV, "Tried to add a mapping for reserved nickname %s",
             UNNAMED_ROUTER_NICKNAME);
128
    tor_free(fingerprint);
129
130
131
    return 0;
  }

132
133
134
135
136
137
  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);
  }

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

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

179
180
181
182
183
184
/** 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. */
185
int
186
dirserv_load_fingerprint_file(void)
187
{
188
  char *fname;
189
  char *cf;
190
  char *nickname, *fingerprint;
191
  authdir_config_t *fingerprint_list_new;
192
  int result;
193
  config_line_t *front=NULL, *list;
194
195
  or_options_t *options = get_options();

196
  fname = get_datadir_fname("approved-routers");
197
198
  log_info(LD_GENERAL,
           "Reloading approved fingerprints from \"%s\"...", fname);
199

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

214
215
216
  result = config_get_lines(cf, &front);
  tor_free(cf);
  if (result < 0) {
217
    log_warn(LD_CONFIG, "Error reading from fingerprint file");
218
219
220
    return -1;
  }

221
  fingerprint_list_new = authdir_config_new();
222

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

  config_free_lines(front);
  dirserv_free_fingerprint_list();
  fingerprint_list = fingerprint_list_new;
  /* Delete any routers whose fingerprints we no longer recognize */
277
  directory_remove_invalid();
278
  return 0;
279
}
280

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

292
  if (crypto_pk_get_digest(router->identity_pkey, d)) {
293
    log_warn(LD_BUG,"Error computing fingerprint");
294
295
    if (msg)
      *msg = "Bug: Error computing fingerprint";
296
    return FP_REJECT;
297
298
  }

299
  return dirserv_get_status_impl(d, router->nickname,
300
301
302
303
304
305
306
307
308
309
310
                                 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)
{
311
  uint32_t res;
312

313
  res = dirserv_get_status_impl(rs->identity_digest, rs->nickname,
314
315
316
317
318
                                "", /* address is only used in logs */
                                rs->addr, rs->or_port,
                                NULL, NULL,
                                NULL, 0);

319
  return (res & FP_REJECT) != 0;
320
321
}

322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/** 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;
}

344
345
346
347
348
349
350
/** 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.)
 */
351
352
static uint32_t
dirserv_get_status_impl(const char *id_digest, const char *nickname,
353
354
355
356
357
                        const char *address,
                        uint32_t addr, uint16_t or_port,
                        const char *platform, const char *contact,
                        const char **msg, int should_log)
{
358
  int reject_unlisted = get_options()->AuthDirRejectUnlisted;
359
360
  uint32_t result = 0;
  router_status_t *status_by_digest;
361

362
  if (!fingerprint_list)
363
364
    fingerprint_list = authdir_config_new();

365
  if (should_log)
366
367
    log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.",
              strmap_size(fingerprint_list->fp_by_name),
368
              digestmap_size(fingerprint_list->status_by_digest));
369

370
371
372
373
374
375
376
377
378
  /* 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;
  }

379
380
381
382
383
384
385
386
  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);
387
      log_info(LD_DIRSERV,
388
389
390
391
392
393
               "Mismatched fingerprint for '%s'. "
               "ContactInfo '%s', platform '%s'.)",
               nickname,
               esc_contact,
               platform ? escaped(platform) : "");
      tor_free(esc_contact);
394
    }
395
396
397
    if (msg)
      *msg = "Rejected: There is already a named server with this nickname "
        "and a different fingerprint.";
398
  }
399

400
401
  status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
                                   id_digest);
402
403
  if (status_by_digest)
    result |= (status_by_digest->status & ~FP_NAMED);
404
405
406
407
408
409
410
411
412

  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";
  }
413

414
415
416
417
418
419
420
421
  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;
  }

422
423
424
425
426
427
428
429
  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)) {
430
    if (!authdir_policy_permits_address(addr, or_port)) {
431
      if (should_log)
432
433
        log_info(LD_DIRSERV, "Rejecting '%s' because of address '%s'",
                 nickname, address);
434
435
436
437
      if (msg)
        *msg = "Authdir is rejecting routers in this range.";
      return FP_REJECT;
    }
438
    if (!authdir_policy_valid_address(addr, or_port)) {
439
      if (should_log)
440
441
        log_info(LD_DIRSERV, "Not marking '%s' valid because of address '%s'",
                 nickname, address);
442
      result |= FP_INVALID;
443
    }
444
445
446
    if (reject_unlisted) {
      if (msg)
        *msg = "Authdir rejects unknown routers.";
447
      return FP_REJECT;
448
    }
449
  }
450
451

  return result;
452
453
}

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

465
466
  status = digestmap_get(fingerprint_list->status_by_digest, digest);
  return status ? status->nickname : NULL;
467
468
}

Roger Dingledine's avatar
Roger Dingledine committed
469
/** Clear the current fingerprint list. */
Roger Dingledine's avatar
Roger Dingledine committed
470
void
471
dirserv_free_fingerprint_list(void)
472
{
473
474
475
  if (!fingerprint_list)
    return;

476
477
478
  strmap_free(fingerprint_list->fp_by_name, _tor_free);
  digestmap_free(fingerprint_list->status_by_digest, _tor_free);
  tor_free(fingerprint_list);
479
480
481
482
483
}

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

485
486
487
488
489
490
491
492
493
/** 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)) {
494
495
    log_info(LD_DIRSERV,"Router '%s' published non-IP address '%s'. Refusing.",
             ri->nickname, ri->address);
496
497
    return -1;
  }
498
  if (is_internal_IP(ntohl(iaddr.s_addr), 0)) {
499
500
501
    log_info(LD_DIRSERV,
             "Router '%s' published internal IP address '%s'. Refusing.",
             ri->nickname, ri->address);
502
503
504
505
506
    return -1; /* it's a private IP, we should reject it */
  }
  return 0;
}

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

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

564
565
566
  return 0;
}

567
568
569
/** As for dirserv_add_descriptor, but accepts multiple documents, and
 * returns the most severe error that occurred for any one of them. */
int
570
dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
571
                                 const char *source,
572
                                 const char **msg)
573
574
575
576
{
  int r=100; /* higher than any actual return value. */
  int r_tmp;
  const char *msg_out;
577
578
579
  smartlist_t *list;
  const char *s;
  int n_parsed = 0;
580
  time_t now = time(NULL);
Roger Dingledine's avatar
Roger Dingledine committed
581
  char annotation_buf[ROUTER_ANNOTATION_BUF_LEN];
582
583
  char time_buf[ISO_TIME_LEN+1];
  int general = purpose == ROUTER_PURPOSE_GENERAL;
584
  tor_assert(msg);
585

586
587
588
589
590
591
592
593
594
595
596
597
  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;
  }

598
599
  s = desc;
  list = smartlist_create();
600
  if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 0, 0,
601
                                     annotation_buf)) {
602
    SMARTLIST_FOREACH(list, routerinfo_t *, ri, {
603
        msg_out = NULL;
604
        tor_assert(ri->purpose == purpose);
605
606
607
608
609
610
        r_tmp = dirserv_add_descriptor(ri, &msg_out);
        if (r_tmp < r) {
          r = r_tmp;
          *msg = msg_out;
        }
      });
611
  }
612
613
614
615
  n_parsed += smartlist_len(list);
  smartlist_clear(list);

  s = desc;
616
617
  if (!router_parse_list_from_string(&s, NULL, list, SAVED_NOWHERE, 1, 0,
                                     NULL)) {
618
    SMARTLIST_FOREACH(list, extrainfo_t *, ei, {
619
        msg_out = NULL;
620

621
622
623
624
625
626
627
628
629
        r_tmp = dirserv_add_extrainfo(ei, &msg_out);
        if (r_tmp < r) {
          r = r_tmp;
          *msg = msg_out;
        }
      });
  }
  n_parsed += smartlist_len(list);
  smartlist_free(list);
630

631
632
633
634
635
636
637
638
639
640
  if (! *msg) {
    if (!n_parsed) {
      *msg = "No descriptors found in your POST.";
      if (r > -1)
        r = -1;
    } else {
      *msg = "(no message)";
    }
  }

641
  return r <= 2 ? r : 2;
642
643
}

644
645
646
/** 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.
647
 *
648
649
 * Return 2 if descriptor is well-formed and accepted;
 *  1 if well-formed and accepted but origin should hear *msg;
Roger Dingledine's avatar
Roger Dingledine committed
650
 *  0 if well-formed but redundant with one we already have;
651
 * -1 if it is rejected and origin should hear *msg;
652
653
654
 *
 * This function is only called when fresh descriptors are posted, not when
 * we re-load the cache.
655
656
 */
int
657
dirserv_add_descriptor(routerinfo_t *ri, const char **msg)
658
{
659
  int r;
660
  routerinfo_t *ri_old;
661
662
  char *desc = NULL;
  size_t desclen = 0;
663

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  /* 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);
    return -1;
  }

680
681
682
683
  /* 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.) */
684
685
  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
686
687
      && router_differences_are_cosmetic(ri_old, ri)
      && !router_is_me(ri)) {
688
689
690
    log_info(LD_DIRSERV,
             "Not replacing descriptor from '%s'; differences are cosmetic.",
             ri->nickname);
691
692
    *msg = "Not replacing router descriptor; no information has changed since "
      "the last one with this identity.";
693
694
695
    control_event_or_authdir_new_descriptor("DROPPED",
                         ri->cache_info.signed_descriptor_body,
                         ri->cache_info.signed_descriptor_len, *msg);
696
    routerinfo_free(ri);
697
698
    return 0;
  }
699
700
701
702
703
704
705
  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);
  }

706
  if ((r = router_add_to_routerlist(ri, msg, 0, 0))<0) {
707
708
709
    if (r < -1 && desc) /* unless the routerinfo was fine, just out-of-date */
      control_event_or_authdir_new_descriptor("REJECTED", desc, desclen, *msg);
    tor_free(desc);
710
    return r == -1 ? 0 : -1;
711
  } else {
712
    smartlist_t *changed;
713
    control_event_or_authdir_new_descriptor("ACCEPTED", desc, desclen, *msg);
714

715
    changed = smartlist_create();
716
717
718
    smartlist_add(changed, ri);
    control_event_descriptors_changed(changed);
    smartlist_free(changed);
719
    if (!*msg) {
720
721
      *msg =  ri->is_valid ? "Descriptor for valid server accepted" :
        "Descriptor for invalid server accepted";
722
    }
723
    tor_free(desc);
724
    return r == 0 ? 2 : 1;
725
  }
726
727
}

728
/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */
729
730
731
732
static int
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
  routerinfo_t *ri;
733
  int r;
734
735
736
737
738
739
740
741
742
  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);
    return -1;
  }
743
744
745
746
747
748
749
750
751
752
753
754
755
756

  /* 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);
    return -1;
  }

757
  if ((r = routerinfo_incompatible_with_extrainfo(ri, ei, NULL, msg))) {
758
    extrainfo_free(ei);
759
    return r < 0 ? 0 : -1;
760
761
762
763
764
  }
  router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
  return 2;
}

765
766
767
/** 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
768
 */
769
static void
770
directory_remove_invalid(void)
771
772
{
  int i;
773
  int changed = 0;
774
  routerlist_t *rl = router_get_routerlist();
775

776
777
  routerlist_assert_ok(rl);

778
  for (i = 0; i < smartlist_len(rl->routers); ++i) {
779
    const char *msg;
780
    routerinfo_t *ent = smartlist_get(rl->routers, i);
781
782
783
784
    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:"");
785
786
      routerlist_remove(rl, ent, 0);
      i--;
787
      changed = 1;
788
      continue;
789
    }
790
    if (bool_neq((r & FP_NAMED), ent->is_named)) {
791
792
793
794
795
796
      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;
    }
797
    if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
798
799
800
801
802
      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;
    }
803
804
805
806
807
808
    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;
    }
809
    if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
810
811
812
813
      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;
814
815
    }
  }
816
817
  if (changed)
    directory_set_dirty();
818
819

  routerlist_assert_ok(rl);
820
821
}

822
823
824
825
/** 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.
 */
826
827
828
int
getinfo_helper_dirserv_unregistered(control_connection_t *control_conn,
                                    const char *question, char **answer_out)
829
830
831
832
{
  smartlist_t *answerlist;
  char buf[1024];
  char *answer;
833
  int min_bw = atoi(question);
834
  routerlist_t *rl = router_get_routerlist();
835

836
837
838
839
840
841
  (void) control_conn;

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

842
  answerlist = smartlist_create();
843
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ent, {
844
    uint32_t r = dirserv_router_get_status(ent, NULL);
845
    if (router_get_advertised_bandwidth(ent) >= (size_t)min_bw &&
846
        !(r & FP_NAMED)) {
847
848
849
      /* then log this one */
      tor_snprintf(buf, sizeof(buf),
                   "%s: BW %d on '%s'.",
850
                   ent->nickname, router_get_advertised_bandwidth(ent),
851
852
853
                   ent->platform ? ent->platform : "");
      smartlist_add(answerlist, tor_strdup(buf));
    }
854
  });
855
856
857
  answer = smartlist_join_strings(answerlist, "\r\n", 0, NULL);
  SMARTLIST_FOREACH(answerlist, char *, cp, tor_free(cp));
  smartlist_free(answerlist);
858
859
  *answer_out = answer;
  return 0;
860
861
}

Nick Mathewson's avatar
Nick Mathewson committed
862
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
Nick Mathewson's avatar
Nick Mathewson committed
863
864
865
 * directory, we will rebuild it instead of reusing the most recently
 * generated one.
 */
Roger Dingledine's avatar
Roger Dingledine committed
866
void
867
directory_set_dirty(void)
868
{
869
  time_t now = time(NULL);
870
  int set_v1_dirty=0;
871

872
873
  /* Regenerate stubs only every 8 hours.
   * XXXX021 It would be nice to generate less often. */
874
875
876
877
878
879
880
881
882
883
884
885
886
#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;
  }
887
888
  if (!the_v2_networkstatus_is_dirty)
    the_v2_networkstatus_is_dirty = now;
889
890
}

891
892
/**
 * Allocate and return a description of the status of the server <b>desc</b>,
893
 * for use in a v1-style router-status line.  The server is listed
894
 * as running iff <b>is_live</b> is true.
Nick Mathewson's avatar
Nick Mathewson committed
895
 */
896
static char *
897
list_single_server_status(routerinfo_t *desc, int is_live)
898
{
899
  char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
900
  char *cp;
901

902
903
904
905
906
907
  tor_assert(desc);

  cp = buf;
  if (!is_live) {
    *cp++ = '!';
  }
908
  if (desc->is_valid) {
Nick Mathewson's avatar
Nick Mathewson committed
909
    strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
910
    cp += strlen(cp);
911
    *cp++ = '=';
912
  }
913
  *cp++ = '$';
914
  base16_encode(cp, HEX_DIGEST_LEN+1, desc->cache_info.identity_digest,
915
                DIGEST_LEN);
916
917
918
  return tor_strdup(buf);
}

919
920
921
/** Each server needs to have passed a reachability test no more
 * than this number of seconds ago, or he is listed as down in
 * the directory. */
922
#define REACHABLE_TIMEOUT (45*60)
923

924
925
/** Treat a router as alive if
 *    - It's me, and I'm not hibernating.
926
 * or - We've found it reachable recently. */
927
void
928
dirserv_set_router_is_running(routerinfo_t *router, time_t now)
929
{
930
931
  int answer;

932
  if (router_is_me(router) && !we_are_hibernating())