config.c 40.2 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

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 {
57
  const char *name; /**< The full keyword (case insensitive) */
Nick Mathewson's avatar
Nick Mathewson committed
58
59
  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
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),
115
116
  VAR("MonthlyAccountingStart",UINT,   AccountingStart),
  VAR("AccountingMaxKB",     UINT,     AccountingMaxKB),
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  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
138
  { NULL, CONFIG_TYPE_OBSOLETE, 0 }
139
140
141
142
};
#undef VAR
#undef OBSOLETE

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

368
  return 0;
369
370
}

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

385
    if (config_assign_line(options, list))
386
      return -1;
387
    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
388
  }
389
  
390
  return 0;
391
392
}

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

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

417
  options->AllowUnverifiedNodes = smartlist_create();
418
419
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("middle"));
  smartlist_add(options->AllowUnverifiedNodes, tor_strdup("rendezvous"));
420

Roger Dingledine's avatar
Roger Dingledine committed
421
  config_free_lines(options->ExitPolicy);
422
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
423

424
425
426
  return 0;
}

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

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

459
460
  tor_assert(addr);

461
462
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
463
  } else { /* then we need to guess our address */
464
    explicit_ip = 0; /* it's implicit */
465

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

473
  /* now we know hostname. resolve it and keep only the IP */
474

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

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

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
495
  *addr = ntohl(in.s_addr);
496
497
498
  return 0;
}

499
500
static char *
get_default_nickname(void)
501
502
503
504
{
  char localhostname[256];
  char *cp, *out, *outp;

505
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
506
507
508
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
509

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

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

530
531
532
533
534
535
536
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

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

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

610
#ifdef MS_WINDOWS
611
612
613
614
615
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

616
617
618
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
619
620
621

  if (is_set)
    return path;
622

623
624
625
626
627
  /* 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))) {
628
629
630
631
    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;
632
633
634
635
636
637
638
639
640
641
  }
  /* 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
642
    return NULL;
643
  }
644
  strlcat(path,"\\tor",MAX_PATH);
645
646
647
648
649
650
651
652
653
654
655
  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);
656
  strlcat(path,"\\torrc",MAX_PATH);
657
658
  return path;
#else
659
  return tor_strdup(CONFDIR "/torrc");
660
661
662
#endif
}

663
664
665
666
/** 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)
667
{
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  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));
683
  smartlist_free(sl);
684
685
686
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
687
688
689
/** 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. */
690
691
692
int
getconfig(int argc, char **argv, or_options_t *options)
{
693
  struct config_line_t *cl;
694
695
696
697
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
698
699
700
701
702
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
703
  int previous_orport = -1;
704
  int using_default_torrc;
705

706
  if (first_load) { /* first time we're called. save commandline args */
707
708
709
710
711
712
713
714
    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 */
715
    if (options->PidFile)
716
      previous_pidfile = tor_strdup(options->PidFile);
717
    previous_runasdaemon = options->RunAsDaemon;
718
    previous_orport = options->ORPort;
719
720
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
721
  init_options(options);
722

723
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
724
725
726
727
    print_usage();
    exit(0);
  }

728
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
729
730
731
732
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
  /* 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;
    }      
749
  }
750

751
  if (using_default_torrc) {
752
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
753
754
    char *fn;
    fn = get_default_conf_file();
755
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
756
757
758
759
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
760
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
761
762
763
764
765
766
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
767
  }
768
  tor_assert(fname);
769
  log(LOG_DEBUG, "Opening config file '%s'", fname);
770

771
  if (config_assign_defaults(options) < 0) {
772
773
    return -1;
  }
774

775
  cf = fopen(fname, "r");
776
777
778
779
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
780
      tor_free(fname);
781
    } else {
782
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
783
      tor_free(fname);
784
785
786
      return -1;
    }
  } else { /* it opened successfully. use it. */
787
    tor_free(fname);
788
789
    if (config_get_lines(cf, &cl)<0)
      return -1;
790
    if (config_assign(options,cl) < 0)
791
      return -1;
792
    config_free_lines(cl);
793
    fclose(cf);
794
  }
795

796
797
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
798
  if (config_assign(options,cl) < 0)
799
    return -1;
800
801
802
803
  config_free_lines(cl);

/* Validate options */

804
  /* first check if any of the previous options have changed but aren't allowed to */
805
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
806
807
808
809
810
811
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

812
  if (previous_runasdaemon && !options->RunAsDaemon) {
813
814
815
816
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

817
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
818
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
819
820
821
    return -1;
  }

822
823
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
824
825
826
    result = -1;
  }

827
  if (options->Nickname == NULL) {
828
    if (server_mode()) {
829
830
831
832
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
833
834
835
836
837
838
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
839
840
841
842
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
843
844
845
846
    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;
847
    }
848
849
  }

850
  if (server_mode()) {
851
852
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
853
    if (resolve_my_address(options->Address, &tmp) < 0)
854
      result = -1;
855
856
  }

857
858
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
859
860
861
    result = -1;
  }

862
863
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
864
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
865
  }
866

867
868
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
869
870
871
    result = -1;
  }

872
873
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
874
875
  }

876
877
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
878
879
  }

880
881
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
882
883
884
    result = -1;
  }

885
886
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
887
888
889
    result = -1;
  }

890
891
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
892
893
    result = -1;
  }
894

895
896
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
897
898
    result = -1;
  }
899

900
  if (options->FascistFirewall && !options->FirewallPorts) {
901
    options->FirewallPorts = smartlist_create();
902
903
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
904
  }
905
  if (options->FirewallPorts) {
906
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
907
908
    {
      i = atoi(cp);
909
      if (i < 1 || i > 65535) {
910
911
912
913
914
915
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  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.");
939
940
941
    result = -1;
  }

942
943
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
944
945
946
    result = -1;
  }

947
948
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
949
950
951
    result = -1;
  }

952
#define MIN_DIRFETCHPOSTPERIOD 60
953
954
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
955
956
    result = -1;
  }
957
958
959
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
960
  }
961

962
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
963
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
964
965
966
    result = -1;
  }

967
968
969
<