config.c 43.1 KB
Newer Older
1
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2
3
4
/* See LICENSE for licensing information */
/* $Id$ */

Nick Mathewson's avatar
Nick Mathewson committed
5
6
7
8
9
10
11
/**
 * /file config.c
 *
 * /brief Code to parse and interpret configuration files.
 *
 **/

Roger Dingledine's avatar
Roger Dingledine committed
12
#include "or.h"
13
14
15
#ifdef MS_WINDOWS
#include <shlobj.h>
#endif
Roger Dingledine's avatar
Roger Dingledine committed
16

Nick Mathewson's avatar
Nick Mathewson committed
17
18
/** Enumeration of types which option values can take */
typedef enum config_type_t {
19
20
21
22
23
24
25
26
  CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
  CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
  CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
  CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
  CONFIG_TYPE_CSV,          /**< A list of strings, separated by commas and optional
                              * whitespace. */
  CONFIG_TYPE_LINELIST,     /**< Uninterpreted config lines */
  CONFIG_TYPE_OBSOLETE,     /**< Obsolete (ignored) option. */
Nick Mathewson's avatar
Nick Mathewson committed
27
} config_type_t;
28

Nick Mathewson's avatar
Nick Mathewson committed
29
/* An abbreviation for a configuration option allowed on the command line */
30
typedef struct config_abbrev_t {
31
32
  const char *abbreviated;
  const char *full;
33
  int commandline_only;
34
35
} config_abbrev_t;

36
37
38
39
/* Handy macro for declaring "In the config file or on the command line,
 * you can abbreviate <b>tok</b>s as <b>tok</b>". */
#define PLURAL(tok) { #tok, #tok "s", 0 }

Nick Mathewson's avatar
Nick Mathewson committed
40
/* A list of command-line abbreviations. */
41
static config_abbrev_t config_abbrevs[] = {
42
43
44
45
46
47
48
49
  PLURAL(ExitNode),
  PLURAL(EntryNodes),
  PLURAL(ExcludeNode),
  PLURAL(FirewallPort),
  PLURAL(HiddenServiceNode),
  PLURAL(HiddenServiceExcludeNode),
  PLURAL(RendNode),
  PLURAL(RendExcludeNode),
50
51
52
  { "l", "LogLevel", 1},
  { "BandwidthRate", "BandwidthRateBytes", 1},
  { "BandwidthBurst", "BandwidthBurstBytes", 1},
53
  { NULL, NULL , 0},
54
};
55
#undef PLURAL
56

57
/** A variable allowed in the configuration file or on the command line. */
58
typedef struct config_var_t {
59
60
61
62
  const char *name; /**< The full keyword (case insensitive). */
  config_type_t type; /**< How to interpret the type and turn it into a value. */
  off_t var_offset; /**< Offset of the corresponding member of or_options_t. */
  const char *initvalue; /**< String (or null) describing initial value. */
63
64
} config_var_t;

Nick Mathewson's avatar
Nick Mathewson committed
65
/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
Nick Mathewson's avatar
Nick Mathewson committed
66
#define STRUCT_OFFSET(tp, member) ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
Nick Mathewson's avatar
Nick Mathewson committed
67
68
69
70
/** An entry for config_vars: "The option <b>name</b> has type
 * CONFIG_TYPE_<b>conftype</b>, and corresponds to
 * or_options_t.<b>member</b>"
 */
71
72
#define VAR(name,conftype,member,initvalue) \
  { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), initvalue }
Nick Mathewson's avatar
Nick Mathewson committed
73
/** An entry for config_vars: "The option <b>name</b> is obsolete." */
74
#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL }
75

Nick Mathewson's avatar
Nick Mathewson committed
76
77
78
79
/** Array of configuration options.  Until we disallow nonstandard
 * abbreviations, order is significant, since the first matching option will
 * be chosen first.
 */
80
static config_var_t config_vars[] = {
81
82
83
  VAR("Address",             STRING,   Address,              NULL),
  VAR("AllowUnverifiedNodes",CSV,      AllowUnverifiedNodes, NULL),
  VAR("AuthoritativeDirectory",BOOL,   AuthoritativeDir,     "0"),
84
85
  VAR("BandwidthRateBytes",  UINT,     BandwidthRateBytes,   "800000"),
  VAR("BandwidthBurstBytes", UINT,     BandwidthBurstBytes,  "50000000"),
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  VAR("ClientOnly",          BOOL,     ClientOnly,           "0"),
  VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
  VAR("DebugLogFile",        STRING,   DebugLogFile,         NULL),
  VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
  VAR("DirPort",             UINT,     DirPort,              "0"),
  VAR("DirBindAddress",      LINELIST, DirBindAddress,       NULL),
  VAR("DirFetchPostPeriod",  UINT,     DirFetchPostPeriod,   "600"),
  VAR("DirPolicy",           LINELIST, DirPolicy,            NULL),
  VAR("DirServer",           LINELIST, DirServers,           NULL),
  VAR("ExitNodes",           STRING,   ExitNodes,            NULL),
  VAR("EntryNodes",          STRING,   EntryNodes,           NULL),
  VAR("StrictExitNodes",     BOOL,     StrictExitNodes,      "0"),
  VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
  VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
  VAR("ExcludeNodes",        STRING,   ExcludeNodes,         NULL),
  VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
  VAR("FirewallPorts",       CSV,      FirewallPorts,        NULL),
  VAR("MyFamily",            STRING,   MyFamily,             NULL),
  VAR("NodeFamily",          LINELIST, NodeFamilies,         NULL),
  VAR("Group",               STRING,   Group,                NULL),
106
  VAR("HashedControlPassword",STRING,  HashedControlPassword, NULL),
107
108
109
110
111
112
113
114
115
  VAR("HttpProxy",           STRING,   HttpProxy,            NULL),
  VAR("HiddenServiceDir",    LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServicePort",   LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServiceNodes",  LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServiceExcludeNodes", LINELIST, RendConfigLines,NULL),
  VAR("IgnoreVersion",       BOOL,     IgnoreVersion,        "0"),
  VAR("KeepalivePeriod",     UINT,     KeepalivePeriod,      "300"),
  VAR("LogLevel",            LINELIST, LogOptions,           NULL),
  VAR("LogFile",             LINELIST, LogOptions,           NULL),
116
  OBSOLETE("LinkPadding"),
117
118
119
120
121
122
123
124
125
126
127
128
129
  VAR("MaxConn",             UINT,     MaxConn,              "1024"),
  VAR("MaxOnionsPending",    UINT,     MaxOnionsPending,     "100"),
  VAR("MonthlyAccountingStart",UINT,   AccountingStart,      "0"),
  VAR("AccountingMaxKB",     UINT,     AccountingMaxKB,      "0"),
  VAR("Nickname",            STRING,   Nickname,             NULL),
  VAR("NewCircuitPeriod",    UINT,     NewCircuitPeriod,     "30"),
  VAR("NumCpus",             UINT,     NumCpus,              "1"),
  VAR("ORPort",              UINT,     ORPort,               "0"),
  VAR("ORBindAddress",       LINELIST, ORBindAddress,        NULL),
  VAR("OutboundBindAddress", STRING,   OutboundBindAddress,  NULL),
  VAR("PidFile",             STRING,   PidFile,              NULL),
  VAR("PathlenCoinWeight",   DOUBLE,   PathlenCoinWeight,    "0.3"),
  VAR("RedirectExit",        LINELIST, RedirectExit,         NULL),
130
  OBSOLETE("RouterFile"),
131
132
133
134
135
136
137
138
139
  VAR("RunAsDaemon",         BOOL,     RunAsDaemon,          "0"),
  VAR("RunTesting",          BOOL,     RunTesting,           "0"),
  VAR("RecommendedVersions", LINELIST, RecommendedVersions,  NULL),
  VAR("RendNodes",           STRING,   RendNodes,            NULL),
  VAR("RendExcludeNodes",    STRING,   RendExcludeNodes,     NULL),
  VAR("SocksPort",           UINT,     SocksPort,            "0"),
  VAR("SocksBindAddress",    LINELIST, SocksBindAddress,     NULL),
  VAR("SocksPolicy",         LINELIST, SocksPolicy,          NULL),
  VAR("SysLog",              LINELIST, LogOptions,           NULL),
140
  OBSOLETE("TrafficShaping"),
141
142
  VAR("User",                STRING,   User,                 NULL),
  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
143
144
145
146
};
#undef VAR
#undef OBSOLETE

Nick Mathewson's avatar
Nick Mathewson committed
147
/** Largest allowed config line */
148
#define CONFIG_LINE_T_MAXLEN 4096
149

150
static struct config_line_t *config_get_commandlines(int argc, char **argv);
151
static int config_get_lines(FILE *f, struct config_line_t **result);
152
static void config_free_lines(struct config_line_t *front);
Nick Mathewson's avatar
Nick Mathewson committed
153
static int config_assign_line(or_options_t *options, struct config_line_t *c);
154
static int config_assign(or_options_t *options, struct config_line_t *list);
155
static int parse_dir_server_line(const char *line);
Roger Dingledine's avatar
Roger Dingledine committed
156
157
static int parse_redirect_line(or_options_t *options,
                               struct config_line_t *line);
158
static const char *expand_abbrev(const char *option, int commandline_only);
159
static config_var_t *config_find_option(const char *key);
160

Nick Mathewson's avatar
Nick Mathewson committed
161
/** If <b>option</b> is an official abbreviation for a longer option,
162
163
164
 * return the longer option.  Otherwise return <b>option</b>.
 * If <b>command_line</b> is set, apply all abbreviations.  Otherwise, only
 * apply abbreviations that work for the config file and the command line. */
165
static const char *
166
expand_abbrev(const char *option, int command_line)
167
168
169
170
{
  int i;
  for (i=0; config_abbrevs[i].abbreviated; ++i) {
    /* Abbreviations aren't casei. */
171
172
    if (!strcmp(option,config_abbrevs[i].abbreviated) &&
        (command_line || !config_abbrevs[i].commandline_only)) {
173
      return config_abbrevs[i].full;
174
    }
175
176
177
  }
  return option;
}
Nick Mathewson's avatar
Nick Mathewson committed
178

Nick Mathewson's avatar
Nick Mathewson committed
179
/** Helper: Read a list of configuration options from the command line. */
180
181
182
static struct config_line_t *
config_get_commandlines(int argc, char **argv)
{
183
184
  struct config_line_t *new;
  struct config_line_t *front = NULL;
185
186
187
  char *s;
  int i = 1;

188
  while (i < argc-1) {
189
190
191
    if (!strcmp(argv[i],"-f") ||
        !strcmp(argv[i],"--hash-password")) {
      i += 2; /* command-line option with argument. ignore them. */
192
      continue;
193
194
    } else if (!strcmp(argv[i],"--list-fingerprint")) {
      i += 1; /* command-line option. ignore it. */
195
      continue;
196
197
    }

198
    new = tor_malloc(sizeof(struct config_line_t));
199
    s = argv[i];
200

201
202
    while(*s == '-')
      s++;
203

204
    new->key = tor_strdup(expand_abbrev(s, 1));
205
    new->value = tor_strdup(argv[i+1]);
206
207

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
208
        new->key, new->value);
209
210
211
212
213
214
215
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

Nick Mathewson's avatar
Nick Mathewson committed
216
217
/** Helper: allocate a new configuration option mapping 'key' to 'val',
 * prepend it to 'front', and return the newly allocated config_line_t */
218
219
220
221
222
223
static struct config_line_t *
config_line_prepend(struct config_line_t *front,
                    const char *key,
                    const char *val)
{
  struct config_line_t *newline;
224

225
226
227
228
229
230
231
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

232
/** Helper: parse the config file and strdup into key/value
233
234
235
 * strings. Set *result to the list, or NULL if parsing the file
 * failed.  Return 0 on success, -1 on failure. Warn and ignore any
 * misformatted lines. */
236
237
238
static int
config_get_lines(FILE *f, struct config_line_t **result)
{
239
240
  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
241
  int r;
242
  char *key, *value;
243

244
  while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) {
245
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
246
  }
247
248

  if (r < 0) {
249
250
251
252
253
254
    *result = NULL;
    return -1;
  } else {
    *result = front;
    return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
255
256
}

Nick Mathewson's avatar
Nick Mathewson committed
257
258
259
/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
260
261
262
static void
config_free_lines(struct config_line_t *front)
{
263
  struct config_line_t *tmp;
264

265
  while (front) {
266
267
268
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
269
270
271
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
272
273
274
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
275
276
277
278
/** If <b>key</b> is a configuration option, return the corresponding
 * config_var_t.  Otherwise, if <b>key</b> is a non-standard abbreviation,
 * warn, and return the corresponding config_var_t.  Otherwise return NULL.
 */
279
280
281
static config_var_t *config_find_option(const char *key)
{
  int i;
Nick Mathewson's avatar
Nick Mathewson committed
282
  /* First, check for an exact (case-insensitive) match */
283
  for (i=0; config_vars[i].name; ++i) {
Nick Mathewson's avatar
Nick Mathewson committed
284
    if (!strcasecmp(key, config_vars[i].name))
285
      return &config_vars[i];
Nick Mathewson's avatar
Nick Mathewson committed
286
287
288
289
  }
  /* If none, check for an abbreviated match */
  for (i=0; config_vars[i].name; ++i) {
    if (!strncasecmp(key, config_vars[i].name, strlen(key))) {
290
291
292
293
294
295
      log_fn(LOG_WARN, "The abbreviation '%s' is deprecated. "
          "Tell Nick and Roger to make it official, or just use '%s' instead",
             key, config_vars[i].name);
      return &config_vars[i];
    }
  }
Nick Mathewson's avatar
Nick Mathewson committed
296
  /* Okay, unrecogized options */
297
298
299
  return NULL;
}

Nick Mathewson's avatar
Nick Mathewson committed
300
301
/** If <b>c</b> is a syntactically valid configuration line, update
 * <b>options</b> with its value and return 0.  Otherwise return -1. */
302
static int
303
config_assign_line(or_options_t *options, struct config_line_t *c)
304
{
305
  int i, ok;
306
307
  config_var_t *var;
  void *lvalue;
308

309
310
311
312
313
  var = config_find_option(c->key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", c->key);
    return -1;
  }
314

315
316
  /* Put keyword into canonical case. */
  if (strcmp(var->name, c->key)) {
317
    tor_free(c->key);
318
    c->key = tor_strdup(var->name);
319
320
  }

Nick Mathewson's avatar
Nick Mathewson committed
321
  lvalue = ((char*)options) + var->var_offset;
322
  switch(var->type) {
323

324
325
326
327
328
329
330
  case CONFIG_TYPE_UINT:
    i = tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
    if (!ok) {
      log(LOG_WARN, "Int keyword '%s %s' is malformed or out of bounds. Skipping.",
          c->key,c->value);
      return 0;
    }
331
    *(int *)lvalue = i;
332
333
334
335
336
337
338
339
    break;

  case CONFIG_TYPE_BOOL:
    i = tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
    if (!ok) {
      log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1. Skipping.", c->key);
      return 0;
    }
340
    *(int *)lvalue = i;
341
342
343
    break;

  case CONFIG_TYPE_STRING:
344
345
    tor_free(*(char **)lvalue);
    *(char **)lvalue = tor_strdup(c->value);
346
347
348
    break;

  case CONFIG_TYPE_DOUBLE:
349
    *(double *)lvalue = atof(c->value);
350
351
352
    break;

  case CONFIG_TYPE_CSV:
353
354
    if (*(smartlist_t**)lvalue == NULL)
      *(smartlist_t**)lvalue = smartlist_create();
355

356
    smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
357
358
359
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

360
361
362
363
    case CONFIG_TYPE_LINELIST:
      /* Note: this reverses the order that the lines appear in.  That's
       * just fine, since we build up the list of lines reversed in the
       * first place. */
364
365
      *(struct config_line_t**)lvalue =
        config_line_prepend(*(struct config_line_t**)lvalue, c->key, c->value);
366
      break;
367
368
369
370

  case CONFIG_TYPE_OBSOLETE:
    log_fn(LOG_WARN, "Skipping obsolete configuration option '%s'", c->key);
    break;
371
  }
372

373
  return 0;
374
375
}

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/** Return a canonicalized list of the options assigned for key.
 */
struct config_line_t *
config_get_assigned_option(or_options_t *options, const char *key)
{
  config_var_t *var;
  const void *value;
  char buf[32];
  struct config_line_t *result;

  var = config_find_option(key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", key);
    return NULL;
  }
  value = ((char*)options) + var->var_offset;

  if (var->type == CONFIG_TYPE_LINELIST) {
    /* Linelist requires special handling: we just copy and return it. */
    const struct config_line_t *next_in = value;
    struct config_line_t **next_out = &result;
    while (next_in) {
      *next_out = tor_malloc(sizeof(struct config_line_t));
      (*next_out)->key = tor_strdup(next_in->key);
      (*next_out)->value = tor_strdup(next_in->value);
      next_in = next_in->next;
      next_out = &((*next_out)->next);
    }
    (*next_out) = NULL;
    return result;
  }

  result = tor_malloc_zero(sizeof(struct config_line_t));
  result->key = tor_strdup(var->name);
  switch(var->type)
    {
    case CONFIG_TYPE_STRING:
      result->value = tor_strdup(value ? (char*)value : "");
      break;
    case CONFIG_TYPE_UINT:
      tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
      result->value = tor_strdup(buf);
      break;
    case CONFIG_TYPE_DOUBLE:
      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
      result->value = tor_strdup(buf);
      break;
    case CONFIG_TYPE_BOOL:
      result->value = tor_strdup(*(int*)value ? "1" : "0");
      break;
    case CONFIG_TYPE_CSV:
      if (value)
        result->value = smartlist_join_strings((smartlist_t*)value,",",0,NULL);
      else
        result->value = tor_strdup("");
      break;
    default:
      tor_free(result->key);
      tor_free(result);
435
      log_fn(LOG_WARN,"Bug: unknown type %d for known key %s", var->type, key);
436
437
438
439
440
441
      return NULL;
    }

  return result;
}

Nick Mathewson's avatar
Nick Mathewson committed
442
443
/** Iterate through the linked list of options <b>list</b>.
 * For each item, convert as appropriate and assign to <b>options</b>.
444
445
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
446
447
448
449
static int
config_assign(or_options_t *options, struct config_line_t *list)
{
  while (list) {
450
451
452
453
454
455
    const char *full = expand_abbrev(list->key, 0);
    if (strcmp(full,list->key)) {
      tor_free(list->key);
      list->key = tor_strdup(full);
    }

456
    if (config_assign_line(options, list))
457
      return -1;
458
    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
459
  }
460
  return 0;
461
462
}

463
464
465
static void
add_default_trusted_dirservers(void)
{
Nick Mathewson's avatar
Nick Mathewson committed
466
467
468
469
  /* moria1 */
  parse_dir_server_line("18.244.0.188:9031 "
                        "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441");
  /* moria2 */
470
  parse_dir_server_line("18.244.0.114:80 "
Nick Mathewson's avatar
Nick Mathewson committed
471
472
473
474
                        "719B E45D E224 B607 C537 07D0 E214 3E2D 423E 74CF");
  /* tor26 */
  parse_dir_server_line("62.116.124.106:9030 "
                        "847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D");
475
476
}

Nick Mathewson's avatar
Nick Mathewson committed
477
478
/** Set <b>options</b> to a reasonable default.
 *
479
 * Call this function before we parse the torrc file.
480
 */
481
482
483
static int
config_assign_defaults(or_options_t *options)
{
484
485
486
  /* set them up as a client only */
  options->SocksPort = 9050;

487
  options->AllowUnverifiedNodes = smartlist_create();
488
489
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("middle"));
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("rendezvous"));
490

Roger Dingledine's avatar
Roger Dingledine committed
491
  config_free_lines(options->ExitPolicy);
492
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
493

494
495
496
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
497
/** Print a usage message for tor. */
498
499
500
static void
print_usage(void)
{
501
  printf("tor -f <torrc> [args]\n"
502
         "See man page for more options. This -h is probably obsolete.\n\n"
503
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
504
         "-d <file>\t\tDebug file\n"
505
//         "-m <max>\t\tMax number of connections\n"
506
507
508
509
         "-l <level>\t\tLog level\n"
         "-r <file>\t\tList of known routers\n");
  printf("\nClient options:\n"
         "-e \"nick1 nick2 ...\"\t\tExit nodes\n"
510
         "-s <IP>\t\t\tPort to bind to for Socks\n");
511
512
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
513
         "-o <port>\t\tOR port to bind to\n"
514
         "-p <file>\t\tPID file\n");
515
516
}

Nick Mathewson's avatar
Nick Mathewson committed
517
/**
518
519
 * Based on <b>address</b>, guess our public IP address and put it
 * in <b>addr</b>.
Nick Mathewson's avatar
Nick Mathewson committed
520
 */
521
522
523
int
resolve_my_address(const char *address, uint32_t *addr)
{
524
525
  struct in_addr in;
  struct hostent *rent;
526
  char hostname[256];
527
  int explicit_ip=1;
528

529
530
  tor_assert(addr);

531
532
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
533
  } else { /* then we need to guess our address */
534
    explicit_ip = 0; /* it's implicit */
535

536
    if (gethostname(hostname, sizeof(hostname)) < 0) {
537
538
539
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
540
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
541
542
  }

543
  /* now we know hostname. resolve it and keep only the IP */
544

545
  if (tor_inet_aton(hostname, &in) == 0) {
546
547
    /* then we have to resolve it */
    explicit_ip = 0;
548
    rent = (struct hostent *)gethostbyname(hostname);
549
    if (!rent) {
550
      log_fn(LOG_WARN,"Could not resolve local Address %s. Failing.", hostname);
551
552
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
553
    tor_assert(rent->h_length == 4);
554
    memcpy(&in.s_addr, rent->h_addr, rent->h_length);
555
  }
556
557

  if (!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
558
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
559
           "Please set the Address config option to be the IP you want to use.",
560
           hostname, inet_ntoa(in));
561
562
    return -1;
  }
563
564

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
565
  *addr = ntohl(in.s_addr);
566
567
568
  return 0;
}

569
570
static char *
get_default_nickname(void)
571
572
573
574
{
  char localhostname[256];
  char *cp, *out, *outp;

575
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
576
577
578
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
579

580
  /* Put it in lowercase; stop at the first dot. */
581
  for (cp = localhostname; *cp; ++cp) {
582
583
584
585
586
587
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
588

589
590
  /* Strip invalid characters. */
  cp = localhostname;
591
  out = outp = tor_malloc(strlen(localhostname) + 1);
592
593
594
595
596
597
598
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
599

600
601
602
603
604
605
606
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
607
/** Release storage held by <b>options</b> */
608
609
610
static void
free_options(or_options_t *options)
{
611
  config_free_lines(options->LogOptions);
612
  tor_free(options->ContactInfo);
613
614
615
616
617
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
618
619
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
620
  tor_free(options->ExcludeNodes);
621
622
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
623
  tor_free(options->OutboundBindAddress);
624
625
  tor_free(options->User);
  tor_free(options->Group);
626
  tor_free(options->HttpProxy);
627
  config_free_lines(options->RendConfigLines);
628
629
630
631
632
  config_free_lines(options->SocksBindAddress);
  config_free_lines(options->ORBindAddress);
  config_free_lines(options->DirBindAddress);
  config_free_lines(options->ExitPolicy);
  config_free_lines(options->SocksPolicy);
633
  config_free_lines(options->DirPolicy);
634
  config_free_lines(options->DirServers);
635
  config_free_lines(options->RecommendedVersions);
636
  config_free_lines(options->NodeFamilies);
637
638
  config_free_lines(options->RedirectExit);
  if (options->RedirectExitList) {
Nick Mathewson's avatar
Nick Mathewson committed
639
640
    SMARTLIST_FOREACH(options->RedirectExitList,
                      exit_redirect_t *, p, tor_free(p));
Roger Dingledine's avatar
Roger Dingledine committed
641
    smartlist_free(options->RedirectExitList);
642
    options->RedirectExitList = NULL;
643
  }
644
645
646
  if (options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
    smartlist_free(options->FirewallPorts);
647
    options->FirewallPorts = NULL;
648
  }
649
650
651
652
653
  if (options->AllowUnverifiedNodes) {
    SMARTLIST_FOREACH(options->AllowUnverifiedNodes, char *, cp, tor_free(cp));
    smartlist_free(options->AllowUnverifiedNodes);
    options->AllowUnverifiedNodes = NULL;
  }
654
}
655

656
657
658
659
660
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
661
  memset(options,0,sizeof(or_options_t));
662
  options->PathlenCoinWeight = 0.3;
663
  options->MaxConn = 1024;
664
  options->DirFetchPostPeriod = 600;
665
  options->KeepalivePeriod = 300;
666
  options->MaxOnionsPending = 100;
667
  options->NewCircuitPeriod = 30; /* twice a minute */
668
669
  options->BandwidthRateBytes = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurstBytes = 10000000; /* max burst on the token bucket */
670
  options->NumCpus = 1;
671
672
}

673
#ifdef MS_WINDOWS
674
675
676
677
678
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

679
680
681
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
682
683
684

  if (is_set)
    return path;
685

686
687
688
689
690
  /* Find X:\documents and settings\username\applicatation data\ .
   * We would use SHGetSpecialFolder path, but that wasn't added until IE4.
   */
  if (!SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA,
                                            &idl))) {
691
692
693
694
    GetCurrentDirectory(MAX_PATH, path);
    is_set = 1;
    log_fn(LOG_WARN, "I couldn't find your application data folder: are you running an ancient version of Windows 95? Defaulting to '%s'", path);
    return path;
695
696
697
698
699
700
701
702
703
704
  }
  /* Convert the path from an "ID List" (whatever that is!) to a path. */
  result = SHGetPathFromIDList(idl, path);
  /* Now we need to free the */
  SHGetMalloc(&m);
  if (m) {
    m->lpVtbl->Free(m, idl);
    m->lpVtbl->Release(m);
  }
  if (!SUCCEEDED(result)) {
Nick Mathewson's avatar
Nick Mathewson committed
705
    return NULL;
706
  }
707
  strlcat(path,"\\tor",MAX_PATH);
708
709
710
711
712
713
714
715
716
717
718
  is_set = 1;
  return path;
}
#endif

static char *
get_default_conf_file(void)
{
#ifdef MS_WINDOWS
  char *path = tor_malloc(MAX_PATH);
  strlcpy(path, get_windows_conf_root(), MAX_PATH);
719
  strlcat(path,"\\torrc",MAX_PATH);
720
721
  return path;
#else
722
  return tor_strdup(CONFDIR "/torrc");
723
724
725
#endif
}

726
727
728
729
/** Verify whether lst is a string containing valid-looking space-separated
 * nicknames, or NULL. Return 0 on success. Warn and return -1 on failure.
 */
static int check_nickname_list(const char *lst, const char *name)
730
{
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
  int r = 0;
  smartlist_t *sl;

  if (!lst)
    return 0;
  sl = smartlist_create();
  smartlist_split_string(sl, lst, ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
  SMARTLIST_FOREACH(sl, const char *, s,
    {
      if (!is_legal_nickname_or_hexdigest(s)) {
        log_fn(LOG_WARN, "Invalid nickname '%s' in %s line", s, name);
        r = -1;
      }
    });
  SMARTLIST_FOREACH(sl, char *, s, tor_free(s));
746
  smartlist_free(sl);
747
748
749
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
750
751
752
/** Read a configuration file into <b>options</b>, finding the configuration
 * file location based on the command line.  After loading the options,
 * validate them for consistency. Return 0 if success, <0 if failure. */
753
754
755
int
getconfig(int argc, char **argv, or_options_t *options)
{
756
  struct config_line_t *cl;
757
758
759
760
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
761
762
763
764
765
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
766
  int previous_orport = -1;
767
  int using_default_torrc;
768

769
  if (first_load) { /* first time we're called. save commandline args */
770
771
772
773
774
775
776
777
    backup_argv = argv;
    backup_argc = argc;
    first_load = 0;
  } else { /* we're reloading. need to clean up old ones first. */
    argv = backup_argv;
    argc = backup_argc;

    /* record some previous values, so we can fail if they change */
778
    if (options->PidFile)
779
      previous_pidfile = tor_strdup(options->PidFile);
780
    previous_runasdaemon = options->RunAsDaemon;
781
    previous_orport = options->ORPort;
782
783
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
784
  init_options(options);
785

786
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
787
788
789
790
    print_usage();
    exit(0);
  }

791
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
792
793
794
795
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  /* learn config file name, get config lines, assign them */
  fname = NULL;
  using_default_torrc = 1;
  options->command = CMD_RUN_TOR;
  for (i = 1; i < argc; ++i) {
    if (i < argc-1 && !strcmp(argv[i],"-f")) {
      if (fname) {
        log(LOG_WARN, "Duplicate -f options on command line.");
        tor_free(fname);
      }
      fname = tor_strdup(argv[i+1]);
      using_default_torrc = 0;
      ++i;
    } else if (!strcmp(argv[i],"--list-fingerprint")) {
      options->command = CMD_LIST_FINGERPRINT;
811
812
813
814
    } else if (!strcmp(argv[i],"--hash-password")) {
      options->command = CMD_HASH_PASSWORD;
      options->command_arg = tor_strdup(argv[i+1]);
      ++i;
815
    }
816

817
  }
818

819
  if (using_default_torrc) {
820
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
821
822
    char *fn;
    fn = get_default_conf_file();
823
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
824
825
826
827
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
828
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
829
830
831
832
833
834
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
835
  }
836
  tor_assert(fname);
837
  log(LOG_DEBUG, "Opening config file '%s'", fname);
838

839
  if (config_assign_defaults(options) < 0) {
840
841
    return -1;
  }
842

843
  cf = fopen(fname, "r");
844
845
846
847
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
848
      tor_free(fname);
849
    } else {
850
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
851
      tor_free(fname);
852
853
854
      return -1;
    }
  } else { /* it opened successfully. use it. */
855
    tor_free(fname);
856
857
    if (config_get_lines(cf, &cl)<0)
      return -1;
858
    if (config_assign(options,cl) < 0)
859
      return -1;
860
    config_free_lines(cl);
861
    fclose(cf);
862
  }
863

864
865
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
866
  if (config_assign(options,cl) < 0)
867
    return -1;
868
869
870
871
  config_free_lines(cl);

/* Validate options */

872
  /* first check if any of the previous options have changed but aren't allowed to */
873
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
874
875
876
877
878
879
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

880
  if (previous_runasdaemon && !options->RunAsDaemon) {
881
882
883
884
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

885
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
886
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
887
888
889
    return -1;
  }

890
891
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
892
893
894
    result = -1;
  }

895
  if (options->Nickname == NULL) {
896
    if (server_mode()) {
897
898
899
900
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
901
902
903
904
905
906
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
907
908
909
910
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
911
912
913
914
    if (strlen(options->Nickname) > MAX_NICKNAME_LEN) {
      log_fn(LOG_WARN, "Nickname '%s' has more than %d characters.",
             options->Nickname, MAX_NICKNAME_LEN);
      result = -1;
915
    }
916
917
  }

918
  if (server_mode()) {
919
920
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
921
    if (resolve_my_address(options->Address, &tmp) < 0)
922
      result = -1;
923
924
  }

925
926
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
927
928
929
    result = -1;
  }

930
931
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
932
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
933
  }
934

935
936
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
937
938
939
    result = -1;
  }

940
941
  if (options->StrictExitNodes &&
      (!options->ExitNodes || !strlen(options->ExitNodes))) {
942
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
943
944
  }

945
946
  if (options->StrictEntryNodes &&
      (!options->EntryNodes || !strlen(options->EntryNodes))) {
947
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
948
949
  }

950
951
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
952
953
954
    result = -1;
  }

955
956
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
957
958
959
    result = -1;
  }

960
961
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
962
963
    result = -1;
  }
964

965
966
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
967
968
    result = -1;
  }
969

970
  if (options->FascistFirewall && !options->FirewallPorts) {
971
    options->FirewallPorts = smartlist_create();
972
973
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
974
  }
975
  if (options->FirewallPorts) {
976
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
977
978
    {
      i = atoi(cp);
979
      if (