dirserv.c 50.9 KB
Newer Older
Roger Dingledine's avatar
Roger Dingledine committed
1
/* Copyright 2001-2004 Roger Dingledine.
Nick Mathewson's avatar
Nick Mathewson committed
2
 * Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
3
4
/* See LICENSE for licensing information */
/* $Id$ */
5
const char dirserv_c_id[] = "$Id$";
6
7
8

#include "or.h"

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

Roger Dingledine's avatar
Roger Dingledine committed
15
/** How far in the future do we allow a router to get? (seconds) */
16
#define ROUTER_ALLOW_SKEW (60*60*12) /* 12 hours */
17
/** How many seconds do we wait before regenerating the directory? */
18
#define DIR_REGEN_SLACK_TIME 30
Nick Mathewson's avatar
Nick Mathewson committed
19

20
21
extern long stats_n_seconds_working;

22
23
24
25
26
27
28
typedef enum {
  FP_NAMED, /**< Listed in fingerprint file. */
  FP_VALID, /**< Unlisted but believed valid. */
  FP_INVALID, /**< Believed invalid. */
  FP_REJECT, /**< We will not publish this router. */
} router_status_t;

Roger Dingledine's avatar
Roger Dingledine committed
29
/** Do we need to regenerate the directory when someone asks for it? */
30
static int the_directory_is_dirty = 1;
31
static int runningrouters_is_dirty = 1;
32
static int the_v2_networkstatus_is_dirty = 1;
33

34
static void directory_remove_invalid(void);
35
static int dirserv_regenerate_directory(void);
36
static char *format_versions_list(config_line_t *ln);
37
/* Should be static; exposed for testing */
38
int add_fingerprint_to_dir(const char *nickname, const char *fp, smartlist_t *list);
39
static int router_is_general_exit(routerinfo_t *ri);
40
41
static router_status_t dirserv_router_get_status(const routerinfo_t *router,
                                                 const char **msg);
42
43
static int dirserv_thinks_router_is_reachable(routerinfo_t *router,
                                              time_t now);
44

45
46
/************** Fingerprint handling code ************/

47
48
49
50
51
52
static addr_policy_t *authdir_reject_policy = NULL;
static addr_policy_t *authdir_invalid_policy = NULL;

/** Parse authdir policy strings from the configuration.
 */
void
53
54
parse_authdir_policy(void)
{
55
56
57
58
59
60
  addr_policy_t *n;
  if (authdir_reject_policy) {
    addr_policy_free(authdir_reject_policy);
    authdir_reject_policy = NULL;
  }
  config_parse_addr_policy(get_options()->AuthDirReject,
61
                           &authdir_reject_policy, ADDR_POLICY_REJECT);
62
63
64
65
66
67
68
69
70
71
72
  /* ports aren't used. */
  for (n=authdir_reject_policy; n; n = n->next) {
    n->prt_min = 1;
    n->prt_max = 65535;
  }

  if (authdir_invalid_policy) {
    addr_policy_free(authdir_invalid_policy);
    authdir_invalid_policy = NULL;
  }
  config_parse_addr_policy(get_options()->AuthDirInvalid,
73
                           &authdir_invalid_policy, ADDR_POLICY_REJECT);
74
75
76
77
78
79
80
  /* ports aren't used. */
  for (n=authdir_invalid_policy; n; n = n->next) {
    n->prt_min = 1;
    n->prt_max = 65535;
  }
}

81
82
/** A member of fingerprint_list: maps a name to a fingerprint.
 **/
83
typedef struct fingerprint_entry_t {
84
85
86
87
88
  char *nickname; /**< The name of a router (if this fingerprint is bound to a
                   * name); the string "!reject" (if this fingerprint should
                   * always be rejected); or the string "!invalid" (if this
                   * fingerprint should be accepted but never marked as
                   * valid. */
89
  char *fingerprint; /**< Stored as HEX_DIGEST_LEN characters, followed by a NUL */
90
91
} fingerprint_entry_t;

Nick Mathewson's avatar
Nick Mathewson committed
92
/** List of nickname-\>identity fingerprint mappings for all the routers
93
 * that we name.  Used to prevent router impersonation. */
94
95
/* Should be static; exposed for testing */
smartlist_t *fingerprint_list = NULL;
96

Nick Mathewson's avatar
Nick Mathewson committed
97
/** Add the fingerprint <b>fp</b> for the nickname <b>nickname</b> to
98
99
 * 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
100
 */
101
102
int /* Should be static; exposed for testing */
add_fingerprint_to_dir(const char *nickname, const char *fp, smartlist_t *list)
Nick Mathewson's avatar
Nick Mathewson committed
103
104
{
  int i;
105
  fingerprint_entry_t *ent;
106
  char *fingerprint;
107
108
109
  tor_assert(nickname);
  tor_assert(fp);
  tor_assert(list);
110

111
112
113
  fingerprint = tor_strdup(fp);
  tor_strstrip(fingerprint, " ");

114
115
116
117
118
119
120
121
  if (nickname[0] != '!') {
    for (i = 0; i < smartlist_len(list); ++i) {
      ent = smartlist_get(list, i);
      if (!strcasecmp(ent->nickname,nickname)) {
        tor_free(ent->fingerprint);
        ent->fingerprint = fingerprint;
        return 1;
      }
Nick Mathewson's avatar
Nick Mathewson committed
122
123
    }
  }
124
125
  ent = tor_malloc(sizeof(fingerprint_entry_t));
  ent->nickname = tor_strdup(nickname);
126
  ent->fingerprint = fingerprint;
127
128
  smartlist_add(list, ent);
  return 0;
Nick Mathewson's avatar
Nick Mathewson committed
129
130
}

131
132
/** Add the nickname and fingerprint for this OR to the
 * global list of recognized identity key fingerprints. */
Nick Mathewson's avatar
Nick Mathewson committed
133
134
135
136
int
dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
{
  char fp[FINGERPRINT_LEN+1];
137
  if (crypto_pk_get_fingerprint(pk, fp, 0)<0) {
138
    err(LD_BUG, "Error computing fingerprint");
Nick Mathewson's avatar
Nick Mathewson committed
139
140
    return -1;
  }
141
142
143
  if (!fingerprint_list)
    fingerprint_list = smartlist_create();
  add_fingerprint_to_dir(nickname, fp, fingerprint_list);
Nick Mathewson's avatar
Nick Mathewson committed
144
145
146
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
147
148
/** Parse the nickname-\>fingerprint mappings stored in the file named
 * <b>fname</b>.  The file format is line-based, with each non-blank
Nick Mathewson's avatar
Nick Mathewson committed
149
150
 * holding one nickname, some space, and a fingerprint for that
 * nickname.  On success, replace the current fingerprint list with
Nick Mathewson's avatar
Nick Mathewson committed
151
 * the contents of <b>fname</b> and return 0.  On failure, leave the
Nick Mathewson's avatar
Nick Mathewson committed
152
 * current fingerprint list untouched, and return -1. */
153
int
154
155
dirserv_parse_fingerprint_file(const char *fname)
{
156
  char *cf;
157
  char *nickname, *fingerprint;
158
  smartlist_t *fingerprint_list_new;
159
  int result;
160
  config_line_t *front=NULL, *list;
161

162
163
  cf = read_file_to_str(fname, 0);
  if (!cf) {
164
    warn(LD_FS, "Cannot open fingerprint file %s", fname);
165
    return -1;
166
  }
167
168
169
  result = config_get_lines(cf, &front);
  tor_free(cf);
  if (result < 0) {
170
    warn(LD_CONFIG, "Error reading from fingerprint file");
171
172
173
    return -1;
  }

174
  fingerprint_list_new = smartlist_create();
175

176
  for (list=front; list; list=list->next) {
177
    nickname = list->key; fingerprint = list->value;
178
    if (strlen(nickname) > MAX_NICKNAME_LEN) {
179
180
      notice(LD_CONFIG,
            "Nickname '%s' too long in fingerprint file. Skipping.", nickname);
181
      continue;
182
    }
183
    if (!is_legal_nickname(nickname) &&
Nick Mathewson's avatar
Nick Mathewson committed
184
185
        strcasecmp(nickname, "!reject") &&
        strcasecmp(nickname, "!invalid")) {
186
187
      notice(LD_CONFIG,
             "Invalid nickname '%s' in fingerprint file. Skipping.", nickname);
188
189
      continue;
    }
190
    if (strlen(fingerprint) != FINGERPRINT_LEN ||
191
        !crypto_pk_check_fingerprint_syntax(fingerprint)) {
192
193
      notice(LD_CONFIG,
             "Invalid fingerprint (nickname '%s', fingerprint %s). Skipping.",
194
195
             nickname, fingerprint);
      continue;
196
    }
197
    if (0==strcasecmp(nickname, DEFAULT_CLIENT_NICKNAME)) {
198
199
      /* If you approved an OR called "client", then clients who use
       * the default nickname could all be rejected.  That's no good. */
200
      notice(LD_CONFIG,
201
202
203
204
          "Authorizing a nickname '%s' would break many clients; skipping.",
          DEFAULT_CLIENT_NICKNAME);
      continue;
    }
205
    if (add_fingerprint_to_dir(nickname, fingerprint, fingerprint_list_new) != 0)
206
      notice(LD_CONFIG, "Duplicate nickname '%s'.", nickname);
207
  }
208
209
210
211
212

  config_free_lines(front);
  dirserv_free_fingerprint_list();
  fingerprint_list = fingerprint_list_new;
  /* Delete any routers whose fingerprints we no longer recognize */
213
  directory_remove_invalid();
214
  return 0;
215
}
216

Nick Mathewson's avatar
Nick Mathewson committed
217
/** Check whether <b>router</b> has a nickname/identity key combination that
218
 * we recognize from the fingerprint list, or an IP we automatically act on
219
220
221
222
 * according to our configuration.  Return the appropriate router status.
 *
 * If the status is 'FP_REJECT' and <b>msg</b> is provided, set
 * *<b>msg</b> to an explanation of why.
223
224
225
 */
static router_status_t
dirserv_router_get_status(const routerinfo_t *router, const char **msg)
226
{
227
  fingerprint_entry_t *nn_ent = NULL, *fp_ent = NULL;
228
229
  char fp[FINGERPRINT_LEN+1];

230
231
232
  if (!fingerprint_list)
    fingerprint_list = smartlist_create();

233
  if (crypto_pk_get_fingerprint(router->identity_pkey, fp, 0)) {
234
    warn(LD_BUG,"Error computing fingerprint");
235
236
237
    return -1;
  }

238
  debug(LD_DIRSERV, "%d fingerprints known.", smartlist_len(fingerprint_list));
239
240
  SMARTLIST_FOREACH(fingerprint_list, fingerprint_entry_t *, ent,
  {
241
242
243
    if (!strcasecmp(fp,ent->fingerprint))
      fp_ent = ent;
    if (!strcasecmp(router->nickname,ent->nickname))
244
245
246
      nn_ent = ent;
  });

247
248
249
250
251
252
253
254
255
256
257
258
  if (fp_ent) {
    if (!strcasecmp(fp_ent->nickname, "!reject")) {
      if (msg)
        *msg = "Fingerprint is marked rejected";
      return FP_REJECT;
    } else if (!strcasecmp(fp_ent->nickname, "!invalid")) {
      if (msg)
        *msg = "Fingerprint is marged invalid";
      return FP_INVALID;
    }
  }

259
  if (!nn_ent) { /* No such server known with that nickname */
260
261
262
263
264
    addr_policy_result_t rej = router_compare_addr_to_addr_policy(
                       router->addr, router->or_port, authdir_reject_policy);
    addr_policy_result_t inv = router_compare_addr_to_addr_policy(
                       router->addr, router->or_port, authdir_invalid_policy);

265
    if (rej == ADDR_POLICY_PROBABLY_REJECTED || rej == ADDR_POLICY_REJECTED) {
266
267
      info(LD_DIRSERV, "Rejecting '%s' because of address %s",
           router->nickname, router->address);
268
269
270
271
      if (msg)
        *msg = "Authdir is rejecting routers in this range.";
      return FP_REJECT;
    }
272
    if (inv == ADDR_POLICY_PROBABLY_REJECTED || inv == ADDR_POLICY_REJECTED) {
273
274
      info(LD_DIRSERV, "Not marking '%s' valid because of address %s",
           router->nickname, router->address);
275
276
      return FP_INVALID;
    }
277
278
279
280
    if (tor_version_as_new_as(router->platform,"0.1.0.2-rc"))
      return FP_VALID;
    else
      return FP_INVALID;
281
    info(LD_DIRSERV,"No fingerprint found for '%s'",router->nickname);
282
283
    return 0;
  }
284
  if (0==strcasecmp(nn_ent->fingerprint, fp)) {
285
    debug(LD_DIRSERV,"Good fingerprint for '%s'",router->nickname);
286
    return FP_NAMED; /* Right fingerprint. */
287
  } else {
288
289
290
291
    warn(LD_DIRSERV,"Mismatched fingerprint for '%s': expected '%s' got '%s'. ContactInfo '%s', platform '%s'.)",
         router->nickname, nn_ent->fingerprint, fp,
         router->contact_info ? router->contact_info : "",
         router->platform ? router->platform : "");
292
293
294
    if (msg)
      *msg = "Rejected: There is already a verified server with this nickname and a different fingerprint.";
    return FP_REJECT; /* Wrong fingerprint. */
295
296
297
  }
}

298
/** If we are an authoritative dirserver, and the list of approved
299
300
 * servers contains one whose identity key digest is <b>digest</b>,
 * return that router's nickname.  Otherwise return NULL. */
301
302
const char *
dirserv_get_nickname_by_digest(const char *digest)
303
{
304
  char hexdigest[HEX_DIGEST_LEN+1];
305
  if (!fingerprint_list)
306
307
308
    return NULL;
  tor_assert(digest);

309
  base16_encode(hexdigest, HEX_DIGEST_LEN+1, digest, DIGEST_LEN);
310
  SMARTLIST_FOREACH(fingerprint_list, fingerprint_entry_t*, ent,
311
                    { if (!strcasecmp(hexdigest, ent->fingerprint))
312
313
314
315
                         return ent->nickname; } );
  return NULL;
}

Roger Dingledine's avatar
Roger Dingledine committed
316
/** Clear the current fingerprint list. */
Roger Dingledine's avatar
Roger Dingledine committed
317
void
318
dirserv_free_fingerprint_list(void)
319
320
{
  int i;
321
322
323
324
325
326
327
328
329
  fingerprint_entry_t *ent;
  if (!fingerprint_list)
    return;

  for (i = 0; i < smartlist_len(fingerprint_list); ++i) {
    ent = smartlist_get(fingerprint_list, i);
    tor_free(ent->nickname);
    tor_free(ent->fingerprint);
    tor_free(ent);
330
  }
331
332
  smartlist_free(fingerprint_list);
  fingerprint_list = NULL;
333
334
335
336
337
}

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

339
340
341
342
343
344
345
346
347
/** 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)) {
348
349
    info(LD_DIRSERV,"Router '%s' published non-IP address '%s'. Refusing.",
         ri->nickname, ri->address);
350
351
352
    return -1;
  }
  if (is_internal_IP(ntohl(iaddr.s_addr))) {
353
354
355
    info(LD_DIRSERV,
         "Router '%s' published internal IP address '%s'. Refusing.",
         ri->nickname, ri->address);
356
357
358
359
360
    return -1; /* it's a private IP, we should reject it */
  }
  return 0;
}

361
/** Check whether we, as a directory server, want to accept <b>ri</b>.  If so,
362
363
 * return 0, and set its is_valid,named,running fields.  Otherwise, return -1.
 *
364
 * If the router is rejected, set *<b>msg</b> to an explanation of why.
365
 */
366
int
367
authdir_wants_to_reject_router(routerinfo_t *ri,
368
                               const char **msg)
369
370
{
  /* Okay.  Now check whether the fingerprint is recognized. */
371
  router_status_t status = dirserv_router_get_status(ri, msg);
372
  time_t now;
Nick Mathewson's avatar
Nick Mathewson committed
373
  tor_assert(msg);
374
375
376
  if (status == FP_REJECT)
    return -1; /* msg is already set. */

Nick Mathewson's avatar
Nick Mathewson committed
377
  /* Is there too much clock skew? */
378
  now = time(NULL);
379
  if (ri->cache_info.published_on > now+ROUTER_ALLOW_SKEW) {
380
    notice(LD_DIRSERV, "Publication time for nickname '%s' is too far (%d minutes) in the future; possible clock skew. Not adding (ContactInfo '%s', platform '%s').",
381
           ri->nickname, (int)((ri->cache_info.published_on-now)/60),
382
383
           ri->contact_info ? ri->contact_info : "",
           ri->platform ? ri->platform : "");
384
385
    *msg = "Rejected: Your clock is set too far in the future, or your timezone is not correct.";
    return -1;
386
  }
387
  if (ri->cache_info.published_on < now-ROUTER_MAX_AGE) {
388
    notice(LD_DIRSERV, "Publication time for router with nickname '%s' is too far (%d minutes) in the past. Not adding (ContactInfo '%s', platform '%s').",
389
           ri->nickname, (int)((now-ri->cache_info.published_on)/60),
390
391
           ri->contact_info ? ri->contact_info : "",
           ri->platform ? ri->platform : "");
392
393
    *msg = "Rejected: Server is expired, or your clock is too far in the past, or your timezone is not correct.";
    return -1;
394
  }
395
  if (dirserv_router_has_valid_address(ri) < 0) {
396
    notice(LD_DIRSERV, "Router with nickname '%s' has invalid address '%s'. Not adding (ContactInfo '%s', platform '%s').",
397
398
399
           ri->nickname, ri->address,
           ri->contact_info ? ri->contact_info : "",
           ri->platform ? ri->platform : "");
400
401
    *msg = "Rejected: Address is not an IP, or IP is a private address.";
    return -1;
402
  }
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  /* Okay, looks like we're willing to accept this one. */
  switch (status) {
    case FP_NAMED:
      ri->is_named = ri->is_verified = 1;
      break;
    case FP_VALID:
      ri->is_named = 0;
      ri->is_verified = 1;
      break;
    case FP_INVALID:
      ri->is_named = ri->is_verified = 0;
      break;
    default:
      tor_assert(0);
  }

419
420
421
  return 0;
}

422
423
424
/** Parse the server descriptor at <b>desc</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.
425
 *
426
427
 * 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
428
 *  0 if well-formed but redundant with one we already have;
429
 * -1 if it looks vaguely like a router descriptor but rejected;
430
 * -2 if we can't find a router descriptor in <b>desc</b>.
431
432
 */
int
433
dirserv_add_descriptor(const char *desc, const char **msg)
434
{
435
  int r;
436
  routerinfo_t *ri = NULL, *ri_old = NULL;
437
438
439
440
  tor_assert(msg);
  *msg = NULL;

  /* Check: is the descriptor syntactically valid? */
441
  ri = router_parse_entry_from_string(desc, NULL);
442
  if (!ri) {
443
    warn(LD_DIRSERV, "Couldn't parse uploaded server descriptor");
444
    *msg = "Rejected: Couldn't parse server descriptor.";
445
    return -2;
446
  }
447
448
449
450
  /* 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.) */
451
452
453
  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
      && router_differences_are_cosmetic(ri_old, ri)) {
454
455
456
    info(LD_DIRSERV,
         "Not replacing descriptor from '%s'; differences are cosmetic.",
         ri->nickname);
457
    *msg = "Not replacing router descriptor; no information has changed since the last one with this identity.";
458
    routerinfo_free(ri);
459
    control_event_or_authdir_new_descriptor("DROPPED", desc, *msg);
460
461
    return 0;
  }
462
  if ((r = router_add_to_routerlist(ri, msg, 0))<0) {
463
464
    if (r < -1) /* unless the routerinfo was fine, just out-of-date */
      control_event_or_authdir_new_descriptor("REJECTED", desc, *msg);
465
    return r == -1 ? 0 : -1;
466
  } else {
467
    smartlist_t *changed;
468
469
    control_event_or_authdir_new_descriptor("ACCEPTED", desc, *msg);

470
    changed = smartlist_create();
471
472
473
    smartlist_add(changed, ri);
    control_event_descriptors_changed(changed);
    smartlist_free(changed);
474
475
476
477
    if (!*msg) {
      *msg =  ri->is_verified ? "Verified server descriptor accepted" :
        "Unverified server descriptor accepted";
    }
478
    return r == 0 ? 2 : 1;
479
  }
480
481
}

482
483
484
/** 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
485
 */
486
static void
487
directory_remove_invalid(void)
488
489
{
  int i;
490
  int changed = 0;
491
  routerlist_t *rl = router_get_routerlist();
492

493
  for (i = 0; i < smartlist_len(rl->routers); ++i) {
494
    const char *msg;
495
    routerinfo_t *ent = smartlist_get(rl->routers, i);
496
497
498
    router_status_t r = dirserv_router_get_status(ent, &msg);
    switch (r) {
      case FP_REJECT:
499
        info(LD_DIRSERV, "Router '%s' is now rejected: %s",
500
            ent->nickname, msg?msg:"");
501
        routerlist_remove(rl, ent, i--, 0);
502
503
504
505
        changed = 1;
        break;
      case FP_NAMED:
        if (!ent->is_verified || !ent->is_named) {
506
507
          info(LD_DIRSERV,
               "Router '%s' is now verified and named.", ent->nickname);
508
509
510
511
512
513
          ent->is_verified = ent->is_named = 1;
          changed = 1;
        }
        break;
      case FP_VALID:
        if (!ent->is_verified || ent->is_named) {
514
          info(LD_DIRSERV, "Router '%s' is now verified.", ent->nickname);
515
516
517
518
519
520
521
          ent->is_verified = 1;
          ent->is_named = 0;
          changed = 1;
        }
        break;
      case FP_INVALID:
        if (ent->is_verified || ent->is_named) {
522
523
          info(LD_DIRSERV,
               "Router '%s' is no longer verified.", ent->nickname);
524
525
526
527
          ent->is_verified = ent->is_named = 0;
          changed = 1;
        }
        break;
528
529
    }
  }
530
531
  if (changed)
    directory_set_dirty();
532
533
}

534
535
536
537
538
/** 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.
 */
char *
539
dirserver_getinfo_unregistered(const char *question)
540
{
541
  router_status_t r;
542
543
544
  smartlist_t *answerlist;
  char buf[1024];
  char *answer;
545
  int min_bw = atoi(question);
546
  routerlist_t *rl = router_get_routerlist();
547
548

  answerlist = smartlist_create();
549
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ent, {
550
    r = dirserv_router_get_status(ent, NULL);
551
552
    if (ent->bandwidthcapacity >= (size_t)min_bw &&
        ent->bandwidthrate >= (size_t)min_bw &&
553
        r != FP_NAMED) {
554
555
556
557
558
559
560
      /* then log this one */
      tor_snprintf(buf, sizeof(buf),
                   "%s: BW %d on '%s'.",
                   ent->nickname, ent->bandwidthcapacity,
                   ent->platform ? ent->platform : "");
      smartlist_add(answerlist, tor_strdup(buf));
    }
561
  });
562
563
564
565
566
567
  answer = smartlist_join_strings(answerlist, "\r\n", 0, NULL);
  SMARTLIST_FOREACH(answerlist, char *, cp, tor_free(cp));
  smartlist_free(answerlist);
  return answer;
}

Nick Mathewson's avatar
Nick Mathewson committed
568
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
Nick Mathewson's avatar
Nick Mathewson committed
569
570
571
 * directory, we will rebuild it instead of reusing the most recently
 * generated one.
 */
Roger Dingledine's avatar
Roger Dingledine committed
572
void
573
directory_set_dirty(void)
574
{
575
576
  time_t now = time(NULL);

577
  if (!the_directory_is_dirty)
578
    the_directory_is_dirty = now;
579
  if (!runningrouters_is_dirty)
580
    runningrouters_is_dirty = now;
581
582
  if (!the_v2_networkstatus_is_dirty)
    the_v2_networkstatus_is_dirty = now;
583
584
}

585
586
/**
 * Allocate and return a description of the status of the server <b>desc</b>,
587
 * for use in a router-status line.  The server is listed
588
 * as running iff <b>is_live</b> is true.
Nick Mathewson's avatar
Nick Mathewson committed
589
 */
590
static char *
591
list_single_server_status(routerinfo_t *desc, int is_live)
592
{
593
  char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
594
  char *cp;
595

596
597
598
599
600
601
  tor_assert(desc);

  cp = buf;
  if (!is_live) {
    *cp++ = '!';
  }
602
  if (desc->is_verified) {
Nick Mathewson's avatar
Nick Mathewson committed
603
    strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
604
    cp += strlen(cp);
605
    *cp++ = '=';
606
  }
607
  *cp++ = '$';
608
  base16_encode(cp, HEX_DIGEST_LEN+1, desc->cache_info.identity_digest,
609
                DIGEST_LEN);
610
611
612
  return tor_strdup(buf);
}

613
614
615
#define REACHABLE_TIMEOUT (60*60) /* an hour */
/* Make sure this is 3 times the value of get_dir_fetch_period() */

616
617
618
619
620
621
622
623
624
/** Treat a router as alive if
 *    - It's me, and I'm not hibernating.
 * or - we're connected to it and we've found it reachable recently. */
static int
dirserv_thinks_router_is_reachable(routerinfo_t *router, time_t now)
{
  connection_t *conn;
  if (router_is_me(router) && !we_are_hibernating())
    return 1;
625
  conn = connection_get_by_identity_digest(router->cache_info.identity_digest);
626
627
628
629
630
631
632
633
634
  if (conn && conn->state == OR_CONN_STATE_OPEN)
    return get_options()->AssumeReachable ||
           now < router->last_reachable + REACHABLE_TIMEOUT;
  return 0;
}

/** Return 1 if we're confident that there's a problem with
 * <b>router</b>'s reachability and its operator should be notified.
 */
635
int
636
637
638
dirserv_thinks_router_is_blatantly_unreachable(routerinfo_t *router, time_t now)
{
  connection_t *conn;
639
640
  if (router->is_hibernating)
    return 0;
641
  conn = connection_get_by_identity_digest(router->cache_info.identity_digest);
642
643
644
645
646
647
648
649
  if (conn && conn->state == OR_CONN_STATE_OPEN &&
      now >= router->last_reachable + 2*REACHABLE_TIMEOUT &&
      router->testing_since &&
      now >= router->testing_since + 2*REACHABLE_TIMEOUT)
    return 1;
  return 0;
}

650
/** Based on the routerinfo_ts in <b>routers</b>, allocate the
651
652
 * contents of a router-status line, and store it in
 * *<b>router_status_out</b>.  Return 0 on success, -1 on failure.
653
 */
654
int
655
list_server_status(smartlist_t *routers, char **router_status_out)
656
657
658
659
{
  /* List of entries in a router-status style: An optional !, then an optional
   * equals-suffixed nickname, then a dollar-prefixed hexdigest. */
  smartlist_t *rs_entries;
660
  time_t now = time(NULL);
661
  int authdir_mode = get_options()->AuthoritativeDir;
662
  tor_assert(router_status_out);
663
664
665

  rs_entries = smartlist_create();

666
  SMARTLIST_FOREACH(routers, routerinfo_t *, ri,
667
  {
668
    if (authdir_mode) {
669
      /* Update router status in routerinfo_t. */
670
      ri->is_running = dirserv_thinks_router_is_reachable(ri, now);
671
    }
672
    smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
673
674
  });

675
  *router_status_out = smartlist_join_strings(rs_entries, " ", 0, NULL);
676
677
678
679

  SMARTLIST_FOREACH(rs_entries, char *, cp, tor_free(cp));
  smartlist_free(rs_entries);

680
681
682
  return 0;
}

683
684
685
/** Helper: Given pointers to two strings describing tor versions, return -1
 * if _a precedes _b, 1 if _b preceeds _a, and 0 if they are equivalent.
 * Used to sort a list of versions. */
686
static int
687
688
689
690
691
692
693
694
_compare_tor_version_str_ptr(const void **_a, const void **_b)
{
  const char *a = *_a, *b = *_b;
  int ca, cb;
  tor_version_t va, vb;
  ca = tor_version_parse(a, &va);
  cb = tor_version_parse(b, &vb);
  /* If they both parse, compare them. */
695
  if (!ca && !cb)
696
697
698
    return tor_version_compare(&va,&vb);
  /* If one parses, it comes first. */
  if (!ca && cb)
699
700
    return -1;
  if (ca && !cb)
701
    return 1;
702
  /* If neither parses, compare strings.  Also, the directory server admin needs
703
704
705
706
  ** to be smacked upside the head.  But Tor is tolerant and gentle. */
  return strcmp(a,b);
}

707
708
709
/* Given a (possibly empty) list of config_line_t, each line of which contains
 * a list of comma-separated version numbers surrounded by optional space,
 * allocate and return a new string containing the version numbers, in order,
710
711
 * separated by commas.  Used to generate Recommended(Client|Server)?Versions
 */
712
713
714
715
716
717
718
719
720
721
static char *
format_versions_list(config_line_t *ln)
{
  smartlist_t *versions;
  char *result;
  versions = smartlist_create();
  for ( ; ln; ln = ln->next) {
    smartlist_split_string(versions, ln->value, ",",
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  }
722
  smartlist_sort(versions, _compare_tor_version_str_ptr);
723
724
725
726
727
728
  result = smartlist_join_strings(versions,",",0,NULL);
  SMARTLIST_FOREACH(versions,char *,s,tor_free(s));
  smartlist_free(versions);
  return result;
}

729
730
/** Generate a new directory and write it into a newly allocated string.
 * Point *<b>dir_out</b> to the allocated string.  Sign the
Nick Mathewson's avatar
Nick Mathewson committed
731
732
 * directory with <b>private_key</b>.  Return 0 on success, -1 on
 * failure.
733
 */
734
int
735
dirserv_dump_directory_to_string(char **dir_out,
736
737
                                 crypto_pk_env_t *private_key)
{
738
  char *cp;
739
  char *router_status;
740
  char *identity_pkey; /* Identity key, DER64-encoded. */
741
  char *recommended_versions;
742
743
  char digest[DIGEST_LEN];
  char published[ISO_TIME_LEN+1];
744
  time_t published_on;
745
746
  char *buf = NULL;
  size_t buf_len;
747
  size_t identity_pkey_len;
748
  routerlist_t *rl = router_get_routerlist();
749
750
751

  tor_assert(dir_out);
  *dir_out = NULL;
752

753
  if (list_server_status(rl->routers, &router_status))
754
    return -1;
755

756
757
  if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
                                           &identity_pkey_len)<0) {
758
    warn(LD_BUG,"write identity_pkey to string failed!");
759
760
    return -1;
  }
761

762
  recommended_versions = format_versions_list(get_options()->RecommendedVersions);
763

764
  published_on = time(NULL);
765
  format_iso_time(published, published_on);
766

767
  buf_len = 2048+strlen(recommended_versions)+
768
    strlen(router_status);
769
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
770
                    buf_len += ri->cache_info.signed_descriptor_len+1);
771
772
773
774
775
776
777
  buf = tor_malloc(buf_len);
  /* We'll be comparing against buf_len throughout the rest of the
     function, though strictly speaking we shouldn't be able to exceed
     it.  This is C, after all, so we may as well check for buffer
     overruns.*/

  tor_snprintf(buf, buf_len,
778
779
           "signed-directory\n"
           "published %s\n"
780
           "recommended-software %s\n"
781
782
783
           "router-status %s\n"
           "dir-signing-key\n%s\n",
           published, recommended_versions, router_status,
784
           identity_pkey);
785

Peter Palfrader's avatar
Peter Palfrader committed
786
  tor_free(recommended_versions);
787
  tor_free(router_status);
788
  tor_free(identity_pkey);
789

790
  cp = buf + strlen(buf);
791
  SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
792
    {
793
794
      size_t len = ri->cache_info.signed_descriptor_len;
      if (cp+len+1 >= buf+buf_len)
795
        goto truncated;
796
797
      memcpy(cp, ri->cache_info.signed_descriptor, len);
      cp += len;
798
799
800
801
      *cp++ = '\n'; /* add an extra newline in case somebody was depending on
                     * it. */
    });
  *cp = '\0';
802

803
  /* These multiple strlcat calls are inefficient, but dwarfed by the RSA
804
805
     signature.
  */
806
  if (strlcat(buf, "directory-signature ", buf_len) >= buf_len)
807
    goto truncated;
808
  if (strlcat(buf, get_options()->Nickname, buf_len) >= buf_len)
809
    goto truncated;
810
  if (strlcat(buf, "\n", buf_len) >= buf_len)
811
812
    goto truncated;

813
  if (router_get_dir_hash(buf,digest)) {
814
    warn(LD_BUG,"couldn't compute digest");
815
    tor_free(buf);
816
817
    return -1;
  }
818
  if (router_append_dirobj_signature(buf,buf_len,digest,private_key)<0) {
819
    tor_free(buf);
820
821
    return -1;
  }
Roger Dingledine's avatar
Roger Dingledine committed
822

823
  *dir_out = buf;
824
  return 0;
825
 truncated:
826
  warn(LD_BUG,"tried to exceed string length.");
827
  tor_free(buf);
828
  return -1;
829
830
}

831
832
833
/** Most recently generated encoded signed directory. (auth dirservers only.)*/
static cached_dir_t the_directory = { NULL, NULL, 0, 0, 0 };

834
/* Used only by non-auth dirservers: The directory and runningrouters we'll
835
 * serve when requested. */
836
837
static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 };
static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 };
838

839
/* Used for other dirservers' v2 network statuses.  Map from hexdigest to
840
 * cached_dir_t. */
841
static digestmap_t *cached_v2_networkstatus = NULL;
842

843
/** Possibly replace the contents of <b>d</b> with the value of
844
845
846
847
848
 * <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
 * the last value, or too far in the future.
 *
 * Does not copy <b>directory</b>; free it if it isn't used.
 */
849
static void
850
set_cached_dir(cached_dir_t *d, char *directory, time_t when)
851
{
852
  time_t now = time(NULL);
853
  if (when<=d->published) {
854
    info(LD_DIRSERV, "Ignoring old directory; not caching.");
855
    tor_free(directory);
856
  } else if (when>=now+ROUTER_MAX_AGE) {
857
    info(LD_DIRSERV, "Ignoring future directory; not caching.");
858
    tor_free(directory);
859
860
  } else {
    /* if (when>d->published && when<now+ROUTER_MAX_AGE) */
861
    debug(LD_DIRSERV, "Caching directory.");
862
    tor_free(d->dir);
863
    d->dir = directory;
864
865
866
    d->dir_len = strlen(directory);
    tor_free(d->dir_z);
    if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
867
                          ZLIB_METHOD)) {
868
      warn(LD_BUG,"Error compressing cached directory");
869
    }
870
    d->published = when;
871
872
873
  }
}

874
/** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */
875
876
877
878
879
880
881
882
static void
clear_cached_dir(cached_dir_t *d)
{
  tor_free(d->dir);
  tor_free(d->dir_z);
  memset(d, 0, sizeof(cached_dir_t));
}

883
/** Free all storage held by the cached_dir_t in <b>d</b>. */