config.c 39.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
31
32
typedef struct config_abbrev_t {
  char *abbreviated;
  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
52
  { NULL, NULL },
};
53
#undef PLURAL
54

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

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

Nick Mathewson's avatar
Nick Mathewson committed
73
74
75
76
/** Array of configuration options.  Until we disallow nonstandard
 * abbreviations, order is significant, since the first matching option will
 * be chosen first.
 */
77
78
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
static config_var_t config_vars[] = {
  VAR("Address",             STRING,   Address),
  VAR("AllowUnverifiedNodes",CSV,      AllowUnverifiedNodes),
  VAR("AuthoritativeDirectory",BOOL,   AuthoritativeDir),
  VAR("BandwidthRate",       UINT,     BandwidthRate),
  VAR("BandwidthBurst",      UINT,     BandwidthBurst),
  VAR("ClientOnly",          BOOL,     ClientOnly),
  VAR("ContactInfo",         STRING,   ContactInfo),
  VAR("DebugLogFile",        STRING,   DebugLogFile),
  VAR("DataDirectory",       STRING,   DataDirectory),
  VAR("DirPort",             UINT,     DirPort),
  VAR("DirBindAddress",      LINELIST, DirBindAddress),
  VAR("DirFetchPostPeriod",  UINT,     DirFetchPostPeriod),
  VAR("DirPolicy",           LINELIST, DirPolicy),
  VAR("DirServer",           LINELIST, DirServers),
  VAR("ExitNodes",           STRING,   ExitNodes),
  VAR("EntryNodes",          STRING,   EntryNodes),
  VAR("StrictExitNodes",     BOOL,     StrictExitNodes),
  VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes),
  VAR("ExitPolicy",          LINELIST, ExitPolicy),
  VAR("ExcludeNodes",        STRING,   ExcludeNodes),
  VAR("FascistFirewall",     BOOL,     FascistFirewall),
  VAR("FirewallPorts",       CSV,      FirewallPorts),
  VAR("MyFamily",            STRING,   MyFamily),
  VAR("NodeFamily",          LINELIST, NodeFamilies),
  VAR("Group",               STRING,   Group),
  VAR("HttpProxy",           STRING,   HttpProxy),
  VAR("HiddenServiceDir",    LINELIST, RendConfigLines),
  VAR("HiddenServicePort",   LINELIST, RendConfigLines),
  VAR("HiddenServiceNodes",  LINELIST, RendConfigLines),
  VAR("HiddenServiceExcludeNodes", LINELIST, RendConfigLines),
  VAR("IgnoreVersion",       BOOL,     IgnoreVersion),
  VAR("KeepalivePeriod",     UINT,     KeepalivePeriod),
  VAR("LogLevel",            LINELIST, LogOptions),
  VAR("LogFile",             LINELIST, LogOptions),
  OBSOLETE("LinkPadding"),
  VAR("MaxConn",             UINT,     MaxConn),
  VAR("MaxOnionsPending",    UINT,     MaxOnionsPending),
  VAR("Nickname",            STRING,   Nickname),
  VAR("NewCircuitPeriod",    UINT,     NewCircuitPeriod),
  VAR("NumCpus",             UINT,     NumCpus),
  VAR("ORPort",              UINT,     ORPort),
  VAR("ORBindAddress",       LINELIST, ORBindAddress),
  VAR("OutboundBindAddress", STRING,   OutboundBindAddress),
  VAR("PidFile",             STRING,   PidFile),
  VAR("PathlenCoinWeight",   DOUBLE,   PathlenCoinWeight),
  VAR("RedirectExit",        LINELIST, RedirectExit),
  OBSOLETE("RouterFile"),
  VAR("RunAsDaemon",         BOOL,     RunAsDaemon),
  VAR("RunTesting",          BOOL,     RunTesting),
  VAR("RecommendedVersions", LINELIST, RecommendedVersions),
  VAR("RendNodes",           STRING,   RendNodes),
  VAR("RendExcludeNodes",    STRING,   RendExcludeNodes),
  VAR("SocksPort",           UINT,     SocksPort),
  VAR("SocksBindAddress",    LINELIST, SocksBindAddress),
  VAR("SocksPolicy",         LINELIST, SocksPolicy),
  VAR("SysLog",              LINELIST, LogOptions),
  OBSOLETE("TrafficShaping"),
  VAR("User",                STRING,   User),
Nick Mathewson's avatar
Nick Mathewson committed
136
  { NULL, CONFIG_TYPE_OBSOLETE, 0 }
137
138
139
140
};
#undef VAR
#undef OBSOLETE

Nick Mathewson's avatar
Nick Mathewson committed
141
/** Largest allowed config line */
142
#define CONFIG_LINE_T_MAXLEN 4096
143

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

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

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

182
183
  while (i < argc-1) {
    if (!strcmp(argv[i],"-f")) {
184
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
185
      i += 2; /* this is the config file option. ignore it. */
186
187
188
      continue;
    }

189
    new = tor_malloc(sizeof(struct config_line_t));
190
    s = argv[i];
191

192
193
    while(*s == '-')
      s++;
194

195
    new->key = tor_strdup(expand_abbrev(s, 1));
196
    new->value = tor_strdup(argv[i+1]);
197
198

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
199
        new->key, new->value);
200
201
202
203
204
205
206
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

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

216
217
218
219
220
221
222
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

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

235
  while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) {
236
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
237
  }
238
239

  if (r < 0) {
240
241
242
243
244
245
    *result = NULL;
    return -1;
  } else {
    *result = front;
    return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
246
247
}

Nick Mathewson's avatar
Nick Mathewson committed
248
249
250
/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
251
252
253
static void
config_free_lines(struct config_line_t *front)
{
254
  struct config_line_t *tmp;
255

256
  while (front) {
257
258
259
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
260
261
262
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
263
264
265
  }
}

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

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

300
301
302
303
304
  var = config_find_option(c->key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", c->key);
    return -1;
  }
305

306
307
  /* Put keyword into canonical case. */
  if (strcmp(var->name, c->key)) {
308
    tor_free(c->key);
309
    c->key = tor_strdup(var->name);
310
311
  }

Nick Mathewson's avatar
Nick Mathewson committed
312
  lvalue = ((char*)options) + var->var_offset;
313
  switch(var->type) {
314

315
316
317
318
319
320
321
  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;
    }
322
    *(int *)lvalue = i;
323
324
325
326
327
328
329
330
    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;
    }
331
    *(int *)lvalue = i;
332
333
334
    break;

  case CONFIG_TYPE_STRING:
335
336
    tor_free(*(char **)lvalue);
    *(char **)lvalue = tor_strdup(c->value);
337
338
339
    break;

  case CONFIG_TYPE_DOUBLE:
340
    *(double *)lvalue = atof(c->value);
341
342
343
    break;

  case CONFIG_TYPE_CSV:
344
345
    if (*(smartlist_t**)lvalue == NULL)
      *(smartlist_t**)lvalue = smartlist_create();
346

347
    smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
348
349
350
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

351
352
353
354
    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. */
355
356
      *(struct config_line_t**)lvalue =
        config_line_prepend(*(struct config_line_t**)lvalue, c->key, c->value);
357
      break;
358
359
360
361

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

364
  return 0;
365
366
}

Nick Mathewson's avatar
Nick Mathewson committed
367
368
/** Iterate through the linked list of options <b>list</b>.
 * For each item, convert as appropriate and assign to <b>options</b>.
369
370
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
371
372
373
374
static int
config_assign(or_options_t *options, struct config_line_t *list)
{
  while (list) {
375
376
377
378
379
380
    const char *full = expand_abbrev(list->key, 0);
    if (strcmp(full,list->key)) {
      tor_free(list->key);
      list->key = tor_strdup(full);
    }

381
    if (config_assign_line(options, list))
382
      return -1;
383
    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
384
  }
385

386
  return 0;
387
388
}

389
390
391
static void
add_default_trusted_dirservers(void)
{
Nick Mathewson's avatar
Nick Mathewson committed
392
393
394
395
  /* moria1 */
  parse_dir_server_line("18.244.0.188:9031 "
                        "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441");
  /* moria2 */
396
  parse_dir_server_line("18.244.0.114:80 "
Nick Mathewson's avatar
Nick Mathewson committed
397
398
399
400
                        "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");
401
402
}

Nick Mathewson's avatar
Nick Mathewson committed
403
404
/** Set <b>options</b> to a reasonable default.
 *
405
 * Call this function before we parse the torrc file.
406
 */
407
408
409
static int
config_assign_defaults(or_options_t *options)
{
410
411
412
  /* set them up as a client only */
  options->SocksPort = 9050;

413
414
415
416
  options->AllowUnverifiedNodes = smartlist_create();
  smartlist_add(options->AllowUnverifiedNodes, "middle");
  smartlist_add(options->AllowUnverifiedNodes, "rendezvous");

Roger Dingledine's avatar
Roger Dingledine committed
417
  config_free_lines(options->ExitPolicy);
418
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
419

420
421
422
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
423
/** Print a usage message for tor. */
424
425
426
static void
print_usage(void)
{
427
  printf("tor -f <torrc> [args]\n"
428
         "See man page for more options. This -h is probably obsolete.\n\n"
429
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
430
         "-d <file>\t\tDebug file\n"
431
//         "-m <max>\t\tMax number of connections\n"
432
433
434
435
         "-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"
436
         "-s <IP>\t\t\tPort to bind to for Socks\n");
437
438
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
439
         "-o <port>\t\tOR port to bind to\n"
440
         "-p <file>\t\tPID file\n");
441
442
}

Nick Mathewson's avatar
Nick Mathewson committed
443
/**
444
445
 * Based on <b>address</b>, guess our public IP address and put it
 * in <b>addr</b>.
Nick Mathewson's avatar
Nick Mathewson committed
446
 */
447
448
449
int
resolve_my_address(const char *address, uint32_t *addr)
{
450
451
  struct in_addr in;
  struct hostent *rent;
452
  char hostname[256];
453
  int explicit_ip=1;
454

455
456
  tor_assert(addr);

457
458
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
459
  } else { /* then we need to guess our address */
460
    explicit_ip = 0; /* it's implicit */
461

462
    if (gethostname(hostname, sizeof(hostname)) < 0) {
463
464
465
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
466
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
467
468
  }

469
  /* now we know hostname. resolve it and keep only the IP */
470

471
  if (tor_inet_aton(hostname, &in) == 0) {
472
473
    /* then we have to resolve it */
    explicit_ip = 0;
474
    rent = (struct hostent *)gethostbyname(hostname);
475
    if (!rent) {
476
      log_fn(LOG_WARN,"Could not resolve local Address %s. Failing.", hostname);
477
478
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
479
    tor_assert(rent->h_length == 4);
480
    memcpy(&in.s_addr, rent->h_addr, rent->h_length);
481
  }
482
483

  if (!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
484
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
485
           "Please set the Address config option to be the IP you want to use.",
486
           hostname, inet_ntoa(in));
487
488
    return -1;
  }
489
490

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
491
  *addr = ntohl(in.s_addr);
492
493
494
  return 0;
}

495
496
static char *
get_default_nickname(void)
497
498
499
500
{
  char localhostname[256];
  char *cp, *out, *outp;

501
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
502
503
504
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
505

506
  /* Put it in lowercase; stop at the first dot. */
507
  for (cp = localhostname; *cp; ++cp) {
508
509
510
511
512
513
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
514

515
516
  /* Strip invalid characters. */
  cp = localhostname;
517
  out = outp = tor_malloc(strlen(localhostname) + 1);
518
519
520
521
522
523
524
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
525

526
527
528
529
530
531
532
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
533
/** Release storage held by <b>options</b> */
534
535
536
static void
free_options(or_options_t *options)
{
537
  config_free_lines(options->LogOptions);
538
  tor_free(options->ContactInfo);
539
540
541
542
543
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
544
545
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
546
  tor_free(options->ExcludeNodes);
547
548
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
549
  tor_free(options->OutboundBindAddress);
550
551
  tor_free(options->User);
  tor_free(options->Group);
552
  tor_free(options->HttpProxy);
553
  config_free_lines(options->RendConfigLines);
554
555
556
557
558
  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);
559
  config_free_lines(options->DirPolicy);
560
  config_free_lines(options->DirServers);
561
  config_free_lines(options->RecommendedVersions);
562
  config_free_lines(options->NodeFamilies);
563
564
  config_free_lines(options->RedirectExit);
  if (options->RedirectExitList) {
Nick Mathewson's avatar
Nick Mathewson committed
565
566
    SMARTLIST_FOREACH(options->RedirectExitList,
                      exit_redirect_t *, p, tor_free(p));
Roger Dingledine's avatar
Roger Dingledine committed
567
    smartlist_free(options->RedirectExitList);
568
    options->RedirectExitList = NULL;
569
  }
570
571
572
  if (options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
    smartlist_free(options->FirewallPorts);
573
    options->FirewallPorts = NULL;
574
  }
575
}
576

577
578
579
580
581
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
582
  memset(options,0,sizeof(or_options_t));
583
584
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
585
  options->StrictEntryNodes = options->StrictExitNodes = 0;
586
  options->ExcludeNodes = tor_strdup("");
587
588
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
589
  /* options->PidFile = tor_strdup("tor.pid"); */
590
  options->PathlenCoinWeight = 0.3;
591
  options->MaxConn = 900;
592
  options->DirFetchPostPeriod = 600;
593
  options->KeepalivePeriod = 300;
594
  options->MaxOnionsPending = 100;
595
  options->NewCircuitPeriod = 30; /* twice a minute */
596
597
  options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurst = 10000000; /* max burst on the token bucket */
598
  options->NumCpus = 1;
599
600
}

601
#ifdef MS_WINDOWS
602
603
604
605
606
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

607
608
609
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
610
611
612

  if (is_set)
    return path;
613

614
615
616
617
618
  /* 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))) {
619
620
621
622
    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;
623
624
625
626
627
628
629
630
631
632
  }
  /* 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
633
    return NULL;
634
  }
635
  strlcat(path,"\\tor",MAX_PATH);
636
637
638
639
640
641
642
643
644
645
646
  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);
647
  strlcat(path,"\\torrc",MAX_PATH);
648
649
  return path;
#else
650
  return tor_strdup(CONFDIR "/torrc");
651
652
653
#endif
}

654
655
656
657
/** 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)
658
{
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
  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));
674
  smartlist_free(sl);
675
676
677
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
678
679
680
/** 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. */
681
682
683
int
getconfig(int argc, char **argv, or_options_t *options)
{
684
  struct config_line_t *cl;
685
686
687
688
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
689
690
691
692
693
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
694
  int previous_orport = -1;
695
  int using_default_torrc;
696

697
  if (first_load) { /* first time we're called. save commandline args */
698
699
700
701
702
703
704
705
    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 */
706
    if (options->PidFile)
707
      previous_pidfile = tor_strdup(options->PidFile);
708
    previous_runasdaemon = options->RunAsDaemon;
709
    previous_orport = options->ORPort;
710
711
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
712
  init_options(options);
713

714
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
715
716
717
718
    print_usage();
    exit(0);
  }

719
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
720
721
722
723
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

724
725
/* learn config file name, get config lines, assign them */
  i = 1;
726
  while (i < argc-1 && strcmp(argv[i],"-f")) {
727
728
    i++;
  }
729
730

  if (i < argc-1) { /* we found one */
731
    fname = tor_strdup(argv[i+1]);
732
    using_default_torrc = 0;
733
  } else {
734
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
735
736
737
    char *fn;
    using_default_torrc = 1;
    fn = get_default_conf_file();
738
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
739
740
741
742
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
743
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
744
745
746
747
748
749
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
750
  }
751
  tor_assert(fname);
752
  log(LOG_DEBUG, "Opening config file '%s'", fname);
753

754
  if (config_assign_defaults(options) < 0) {
755
756
    return -1;
  }
757

758
  cf = fopen(fname, "r");
759
760
761
762
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
763
      tor_free(fname);
764
    } else {
765
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
766
      tor_free(fname);
767
768
769
      return -1;
    }
  } else { /* it opened successfully. use it. */
770
    tor_free(fname);
771
772
    if (config_get_lines(cf, &cl)<0)
      return -1;
773
    if (config_assign(options,cl) < 0)
774
      return -1;
775
    config_free_lines(cl);
776
    fclose(cf);
777
  }
778

779
780
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
781
  if (config_assign(options,cl) < 0)
782
    return -1;
783
784
785
786
  config_free_lines(cl);

/* Validate options */

787
  /* first check if any of the previous options have changed but aren't allowed to */
788
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
789
790
791
792
793
794
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

795
  if (previous_runasdaemon && !options->RunAsDaemon) {
796
797
798
799
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

800
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
801
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
802
803
804
    return -1;
  }

805
806
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
807
808
809
    result = -1;
  }

810
  if (options->Nickname == NULL) {
811
    if (server_mode()) {
812
813
814
815
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
816
817
818
819
820
821
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
822
823
824
825
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
826
827
828
829
    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;
830
    }
831
832
  }

833
  if (server_mode()) {
834
835
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
836
    if (resolve_my_address(options->Address, &tmp) < 0)
837
      result = -1;
838
839
  }

840
841
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
842
843
844
    result = -1;
  }

845
846
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
847
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
848
  }
849

850
851
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
852
853
854
    result = -1;
  }

855
856
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
857
858
  }

859
860
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
861
862
  }

863
864
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
865
866
867
    result = -1;
  }

868
869
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
870
871
872
    result = -1;
  }

873
874
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
875
876
    result = -1;
  }
877

878
879
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
880
881
    result = -1;
  }
882

883
  if (options->FascistFirewall && !options->FirewallPorts) {
884
    options->FirewallPorts = smartlist_create();
885
886
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
887
  }
888
  if (options->FirewallPorts) {
889
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
890
891
    {
      i = atoi(cp);
892
      if (i < 1 || i > 65535) {
893
894
895
896
897
898
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
  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.");
922
923
924
    result = -1;
  }

925
926
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
927
928
929
    result = -1;
  }

930
931
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
932
933
934
    result = -1;
  }

935
#define MIN_DIRFETCHPOSTPERIOD 60
936
937
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
938
939
    result = -1;
  }
940
941
942
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
943
  }
944

945
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
946
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
947
948
949
    result = -1;
  }

950
951
952
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
953
954
955
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
956
    if (options->HttpProxyPort == 0) { /* give it a default */
957
958
959
960
      options->HttpProxyPort = 80;
    }
  }

961
962
963
964
965
966
967
968
969
970
971
972
  if (check_nickname_list(options->ExitNodes, "ExitNodes"))
    return -1;
  if (check_nickname_list(options->EntryNodes, "EntryNodes"))
    return -1;
  if (check_nickname_list(options->ExcludeNodes, "ExcludeNodes"))
    return -1;
  if (check_nickname_list(options->RendNodes, "RendNodes"))
    return -1;
  if (check_nickname_list(options->RendNodes, "RendExcludeNodes"))
    return -1;
  if (check_nickname_list(options->MyFamily, "MyFamily"))
    return -1;
973
974
975
976
977
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
      return -1;
  }