config.c 42.6 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
50
  PLURAL(ExitNode),
  PLURAL(EntryNodes),
  PLURAL(ExcludeNode),
  PLURAL(FirewallPort),
  PLURAL(HiddenServiceNode),
  PLURAL(HiddenServiceExcludeNode),
  PLURAL(RendNode),
  PLURAL(RendExcludeNode),
  { "l",        "LogLevel" , 1},
51
  { NULL, NULL , 0},
52
};
53
#undef PLURAL
54

55
/** A variable allowed in the configuration file or on the command line. */
56
typedef struct config_var_t {
57
58
59
60
  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. */
61
62
} config_var_t;

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

Nick Mathewson's avatar
Nick Mathewson committed
74
75
76
77
/** Array of configuration options.  Until we disallow nonstandard
 * abbreviations, order is significant, since the first matching option will
 * be chosen first.
 */
78
static config_var_t config_vars[] = {
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  VAR("Address",             STRING,   Address,              NULL),
  VAR("AllowUnverifiedNodes",CSV,      AllowUnverifiedNodes, NULL),
  VAR("AuthoritativeDirectory",BOOL,   AuthoritativeDir,     "0"),
  VAR("BandwidthRate",       UINT,     BandwidthRate,        "800000"),
  VAR("BandwidthBurst",      UINT,     BandwidthBurst,       "50000000"),
  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),
  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),
113
  OBSOLETE("LinkPadding"),
114
115
116
117
118
119
120
121
122
123
124
125
126
  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),
127
  OBSOLETE("RouterFile"),
128
129
130
131
132
133
134
135
136
  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),
137
  OBSOLETE("TrafficShaping"),
138
139
  VAR("User",                STRING,   User,                 NULL),
  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
140
141
142
143
};
#undef VAR
#undef OBSOLETE

Nick Mathewson's avatar
Nick Mathewson committed
144
/** Largest allowed config line */
145
#define CONFIG_LINE_T_MAXLEN 4096
146

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

Nick Mathewson's avatar
Nick Mathewson committed
158
/** If <b>option</b> is an official abbreviation for a longer option,
159
160
161
 * 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. */
162
static const char *
163
expand_abbrev(const char *option, int command_line)
164
165
166
167
{
  int i;
  for (i=0; config_abbrevs[i].abbreviated; ++i) {
    /* Abbreviations aren't casei. */
168
169
    if (!strcmp(option,config_abbrevs[i].abbreviated) &&
        (command_line || !config_abbrevs[i].commandline_only)) {
170
      return config_abbrevs[i].full;
171
    }
172
173
174
  }
  return option;
}
Nick Mathewson's avatar
Nick Mathewson committed
175

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

185
186
  while (i < argc-1) {
    if (!strcmp(argv[i],"-f")) {
187
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
188
      i += 2; /* this is the config file option. ignore it. */
189
      continue;
190
191
    } else if (!strcmp(argv[i],"--list-fingerprint")) {
      i += 1; /* command-line option. ignore it. */
192
193
    }

194
    new = tor_malloc(sizeof(struct config_line_t));
195
    s = argv[i];
196

197
198
    while(*s == '-')
      s++;
199

200
    new->key = tor_strdup(expand_abbrev(s, 1));
201
    new->value = tor_strdup(argv[i+1]);
202
203

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
204
        new->key, new->value);
205
206
207
208
209
210
211
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

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

221
222
223
224
225
226
227
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

228
/** Helper: parse the config file and strdup into key/value
229
230
231
 * 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. */
232
233
234
static int
config_get_lines(FILE *f, struct config_line_t **result)
{
235
236
  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
237
  int r;
238
  char *key, *value;
239

240
  while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) {
241
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
242
  }
243
244

  if (r < 0) {
245
246
247
248
249
250
    *result = NULL;
    return -1;
  } else {
    *result = front;
    return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
251
252
}

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

261
  while (front) {
262
263
264
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
265
266
267
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
268
269
270
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
271
272
273
274
/** 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.
 */
275
276
277
static config_var_t *config_find_option(const char *key)
{
  int i;
Nick Mathewson's avatar
Nick Mathewson committed
278
  /* First, check for an exact (case-insensitive) match */
279
  for (i=0; config_vars[i].name; ++i) {
Nick Mathewson's avatar
Nick Mathewson committed
280
    if (!strcasecmp(key, config_vars[i].name))
281
      return &config_vars[i];
Nick Mathewson's avatar
Nick Mathewson committed
282
283
284
285
  }
  /* If none, check for an abbreviated match */
  for (i=0; config_vars[i].name; ++i) {
    if (!strncasecmp(key, config_vars[i].name, strlen(key))) {
286
287
288
289
290
291
      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
292
  /* Okay, unrecogized options */
293
294
295
  return NULL;
}

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

305
306
307
308
309
  var = config_find_option(c->key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", c->key);
    return -1;
  }
310

311
312
  /* Put keyword into canonical case. */
  if (strcmp(var->name, c->key)) {
313
    tor_free(c->key);
314
    c->key = tor_strdup(var->name);
315
316
  }

Nick Mathewson's avatar
Nick Mathewson committed
317
  lvalue = ((char*)options) + var->var_offset;
318
  switch(var->type) {
319

320
321
322
323
324
325
326
  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;
    }
327
    *(int *)lvalue = i;
328
329
330
331
332
333
334
335
    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;
    }
336
    *(int *)lvalue = i;
337
338
339
    break;

  case CONFIG_TYPE_STRING:
340
341
    tor_free(*(char **)lvalue);
    *(char **)lvalue = tor_strdup(c->value);
342
343
344
    break;

  case CONFIG_TYPE_DOUBLE:
345
    *(double *)lvalue = atof(c->value);
346
347
348
    break;

  case CONFIG_TYPE_CSV:
349
350
    if (*(smartlist_t**)lvalue == NULL)
      *(smartlist_t**)lvalue = smartlist_create();
351

352
    smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
353
354
355
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

356
357
358
359
    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. */
360
361
      *(struct config_line_t**)lvalue =
        config_line_prepend(*(struct config_line_t**)lvalue, c->key, c->value);
362
      break;
363
364
365
366

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

369
  return 0;
370
371
}

372
373
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
435
436
/** 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);
      return NULL;
    }

  return result;
}

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

451
    if (config_assign_line(options, list))
452
      return -1;
453
    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
454
  }
455
  return 0;
456
457
}

458
459
460
static void
add_default_trusted_dirservers(void)
{
Nick Mathewson's avatar
Nick Mathewson committed
461
462
463
464
  /* moria1 */
  parse_dir_server_line("18.244.0.188:9031 "
                        "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441");
  /* moria2 */
465
  parse_dir_server_line("18.244.0.114:80 "
Nick Mathewson's avatar
Nick Mathewson committed
466
467
468
469
                        "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");
470
471
}

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

482
  options->AllowUnverifiedNodes = smartlist_create();
483
484
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("middle"));
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("rendezvous"));
485

Roger Dingledine's avatar
Roger Dingledine committed
486
  config_free_lines(options->ExitPolicy);
487
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
488

489
490
491
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
492
/** Print a usage message for tor. */
493
494
495
static void
print_usage(void)
{
496
  printf("tor -f <torrc> [args]\n"
497
         "See man page for more options. This -h is probably obsolete.\n\n"
498
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
499
         "-d <file>\t\tDebug file\n"
500
//         "-m <max>\t\tMax number of connections\n"
501
502
503
504
         "-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"
505
         "-s <IP>\t\t\tPort to bind to for Socks\n");
506
507
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
508
         "-o <port>\t\tOR port to bind to\n"
509
         "-p <file>\t\tPID file\n");
510
511
}

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

524
525
  tor_assert(addr);

526
527
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
528
  } else { /* then we need to guess our address */
529
    explicit_ip = 0; /* it's implicit */
530

531
    if (gethostname(hostname, sizeof(hostname)) < 0) {
532
533
534
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
535
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
536
537
  }

538
  /* now we know hostname. resolve it and keep only the IP */
539

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

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

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
560
  *addr = ntohl(in.s_addr);
561
562
563
  return 0;
}

564
565
static char *
get_default_nickname(void)
566
567
568
569
{
  char localhostname[256];
  char *cp, *out, *outp;

570
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
571
572
573
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
574

575
  /* Put it in lowercase; stop at the first dot. */
576
  for (cp = localhostname; *cp; ++cp) {
577
578
579
580
581
582
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
583

584
585
  /* Strip invalid characters. */
  cp = localhostname;
586
  out = outp = tor_malloc(strlen(localhostname) + 1);
587
588
589
590
591
592
593
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
594

595
596
597
598
599
600
601
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

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

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

668
#ifdef MS_WINDOWS
669
670
671
672
673
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

674
675
676
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
677
678
679

  if (is_set)
    return path;
680

681
682
683
684
685
  /* 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))) {
686
687
688
689
    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;
690
691
692
693
694
695
696
697
698
699
  }
  /* 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
700
    return NULL;
701
  }
702
  strlcat(path,"\\tor",MAX_PATH);
703
704
705
706
707
708
709
710
711
712
713
  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);
714
  strlcat(path,"\\torrc",MAX_PATH);
715
716
  return path;
#else
717
  return tor_strdup(CONFDIR "/torrc");
718
719
720
#endif
}

721
722
723
724
/** 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)
725
{
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
  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));
741
  smartlist_free(sl);
742
743
744
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
745
746
747
/** 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. */
748
749
750
int
getconfig(int argc, char **argv, or_options_t *options)
{
751
  struct config_line_t *cl;
752
753
754
755
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
756
757
758
759
760
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
761
  int previous_orport = -1;
762
  int using_default_torrc;
763

764
  if (first_load) { /* first time we're called. save commandline args */
765
766
767
768
769
770
771
772
    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 */
773
    if (options->PidFile)
774
      previous_pidfile = tor_strdup(options->PidFile);
775
    previous_runasdaemon = options->RunAsDaemon;
776
    previous_orport = options->ORPort;
777
778
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
779
  init_options(options);
780

781
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
782
783
784
785
    print_usage();
    exit(0);
  }

786
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
787
788
789
790
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  /* 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;
806
    }
807
  }
808

809
  if (using_default_torrc) {
810
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
811
812
    char *fn;
    fn = get_default_conf_file();
813
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
814
815
816
817
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
818
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
819
820
821
822
823
824
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
825
  }
826
  tor_assert(fname);
827
  log(LOG_DEBUG, "Opening config file '%s'", fname);
828

829
  if (config_assign_defaults(options) < 0) {
830
831
    return -1;
  }
832

833
  cf = fopen(fname, "r");
834
835
836
837
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
838
      tor_free(fname);
839
    } else {
840
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
841
      tor_free(fname);
842
843
844
      return -1;
    }
  } else { /* it opened successfully. use it. */
845
    tor_free(fname);
846
847
    if (config_get_lines(cf, &cl)<0)
      return -1;
848
    if (config_assign(options,cl) < 0)
849
      return -1;
850
    config_free_lines(cl);
851
    fclose(cf);
852
  }
853

854
855
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
856
  if (config_assign(options,cl) < 0)
857
    return -1;
858
859
860
861
  config_free_lines(cl);

/* Validate options */

862
  /* first check if any of the previous options have changed but aren't allowed to */
863
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
864
865
866
867
868
869
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

870
  if (previous_runasdaemon && !options->RunAsDaemon) {
871
872
873
874
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

875
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
876
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
877
878
879
    return -1;
  }

880
881
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
882
883
884
    result = -1;
  }

885
  if (options->Nickname == NULL) {
886
    if (server_mode()) {
887
888
889
890
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
891
892
893
894
895
896
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
897
898
899
900
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
901
902
903
904
    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;
905
    }
906
907
  }

908
  if (server_mode()) {
909
910
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
911
    if (resolve_my_address(options->Address, &tmp) < 0)
912
      result = -1;
913
914
  }

915
916
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
917
918
919
    result = -1;
  }

920
921
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
922
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
923
  }
924

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

930
931
  if (options->StrictExitNodes &&
      (!options->ExitNodes || !strlen(options->ExitNodes))) {
932
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
933
934
  }

935
936
  if (options->StrictEntryNodes &&
      (!options->EntryNodes || !strlen(options->EntryNodes))) {
937
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
938
939
  }

940
941
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
942
943
944
    result = -1;
  }

945
946
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
947
948
949
    result = -1;
  }

950
951
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
952
953
    result = -1;
  }
954

955
956
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
957
958
    result = -1;
  }
959

960
  if (options->FascistFirewall && !options->FirewallPorts) {
961
    options->FirewallPorts = smartlist_create();
962
963
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
964
  }
965
  if (options->FirewallPorts) {
966
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
967
968
    {
      i = atoi(cp);
969
      if (i < 1 || i > 65535) {
970
971
972
973
974
975
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
  if (options->AllowUnverifiedNodes) {
    SMARTLIST_FOREACH(options->AllowUnverifiedNodes, const char *, cp, {
        if (!strcasecmp(cp, "entry"))
          options->_AllowUnverified |= ALLOW_UNVERIFIED_ENTRY;
        else if (!strcasecmp(cp, "exit"))
          options->_AllowUnverified |= ALLOW_UNVERIFIED_EXIT;
        else if (!strcasecmp(cp, "middle"))
          options->_AllowUnverified |= ALLOW_UNVERIFIED_MIDDLE;
        else if (!strcasecmp(cp, "introduction"))
          options->_AllowUnverified |= ALLOW_UNVERIFIED_INTRODUCTION;
        else if (!strcasecmp(cp, "rendezvous"))
          options->_AllowUnverified |= ALLOW_UNVERIFIED_RENDEZVOUS;
        else {
          log(LOG_WARN, "Unrecognized value '%s' in AllowUnverifiedNodes",
              cp);
          result=-1;
        }
      });
  }

  if (options->SocksPort >= 1 &&
      (options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
    log(LOG_WARN, "PathlenCoinWeight option must be >=0.0 and <1.0.");
999
1000
    result = -1;
  }
For faster browsing, not all history is shown. View entire blame