config.c 38 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
/** Largest allowed config line */
30
#define CONFIG_LINE_T_MAXLEN 4096
31

32
static struct config_line_t *config_get_commandlines(int argc, char **argv);
33
static int config_get_lines(FILE *f, struct config_line_t **result);
34
static void config_free_lines(struct config_line_t *front);
35
static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg);
36
static int config_assign(or_options_t *options, struct config_line_t *list);
37
static int parse_dir_server_line(const char *line);
Roger Dingledine's avatar
Roger Dingledine committed
38
39
static int parse_redirect_line(or_options_t *options,
                               struct config_line_t *line);
40

Nick Mathewson's avatar
Nick Mathewson committed
41

Nick Mathewson's avatar
Nick Mathewson committed
42
/** Helper: Read a list of configuration options from the command line. */
43
44
45
static struct config_line_t *
config_get_commandlines(int argc, char **argv)
{
46
47
  struct config_line_t *new;
  struct config_line_t *front = NULL;
48
49
50
  char *s;
  int i = 1;

51
52
  while (i < argc-1) {
    if (!strcmp(argv[i],"-f")) {
53
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
54
      i += 2; /* this is the config file option. ignore it. */
55
56
57
      continue;
    }

58
    new = tor_malloc(sizeof(struct config_line_t));
59
    s = argv[i];
60

61
62
    while(*s == '-')
      s++;
63

64
65
    new->key = tor_strdup(s);
    new->value = tor_strdup(argv[i+1]);
66
67

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
68
        new->key, new->value);
69
70
71
72
73
74
75
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

Nick Mathewson's avatar
Nick Mathewson committed
76
77
/** Helper: allocate a new configuration option mapping 'key' to 'val',
 * prepend it to 'front', and return the newly allocated config_line_t */
78
79
80
81
82
83
static struct config_line_t *
config_line_prepend(struct config_line_t *front,
                    const char *key,
                    const char *val)
{
  struct config_line_t *newline;
84

85
86
87
88
89
90
91
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

92
/** Helper: parse the config file and strdup into key/value
93
94
95
 * 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. */
96
97
98
static int
config_get_lines(FILE *f, struct config_line_t **result)
{
99
100
  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
101
  int r;
102
  char *key, *value;
103

104
  while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) {
105
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
106
  }
107
108

  if (r < 0) {
109
110
111
112
113
114
    *result = NULL;
    return -1;
  } else {
    *result = front;
    return 0;
  }
Roger Dingledine's avatar
Roger Dingledine committed
115
116
}

Nick Mathewson's avatar
Nick Mathewson committed
117
118
119
/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
120
121
122
static void
config_free_lines(struct config_line_t *front)
{
123
  struct config_line_t *tmp;
124

125
  while (front) {
126
127
128
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
129
130
131
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
132
133
134
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
135
136
137
138
139
/** Search the linked list <b>c</b> for any option whose key is <b>key</b>.
 * If such an option is found, interpret it as of type <b>type</b>, and store
 * the result in <b>arg</b>.  If the option is misformatted, log a warning and
 * skip it.
 */
140
141
142
143
static int
config_compare(struct config_line_t *c, const char *key,
               config_type_t type, void *arg)
{
144
  int i, ok;
145

146
  if (strncasecmp(c->key, key, strlen(c->key)))
147
148
    return 0;

149
  if (strcasecmp(c->key, key)) {
150
151
152
153
    tor_free(c->key);
    c->key = tor_strdup(key);
  }

154
  /* it's a match. cast and assign. */
155
156
  log_fn(LOG_DEBUG, "Recognized keyword '%s' as %s, using value '%s'.",
         c->key, key, c->value);
157
158

  switch(type) {
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  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;
    }
    *(int *)arg = i;
    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;
    }
    *(int *)arg = i;
    break;

  case CONFIG_TYPE_STRING:
    tor_free(*(char **)arg);
    *(char **)arg = tor_strdup(c->value);
    break;

  case CONFIG_TYPE_DOUBLE:
    *(double *)arg = atof(c->value);
    break;

  case CONFIG_TYPE_CSV:
    if (*(smartlist_t**)arg == NULL)
      *(smartlist_t**)arg = smartlist_create();

    smartlist_split_string(*(smartlist_t**)arg, c->value, ",",
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

195
196
197
198
199
200
201
    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. */
      *(struct config_line_t**)arg =
        config_line_prepend(*(struct config_line_t**)arg, c->key, c->value);
      break;
202
203
204
205

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

208
209
210
  return 1;
}

Nick Mathewson's avatar
Nick Mathewson committed
211
212
/** Iterate through the linked list of options <b>list</b>.
 * For each item, convert as appropriate and assign to <b>options</b>.
213
214
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
215
216
217
218
219
static int
config_assign(or_options_t *options, struct config_line_t *list)
{
  while (list) {
    if (
220

221
      /* order matters here! abbreviated arguments use the first match. */
222

223
224
225
226
      /* string options */
      config_compare(list, "Address",        CONFIG_TYPE_STRING, &options->Address) ||
      config_compare(list, "AllowUnverifiedNodes", CONFIG_TYPE_CSV, &options->AllowUnverifiedNodes) ||
      config_compare(list, "AuthoritativeDirectory",CONFIG_TYPE_BOOL, &options->AuthoritativeDir) ||
227

228
229
      config_compare(list, "BandwidthRate",  CONFIG_TYPE_UINT, &options->BandwidthRate) ||
      config_compare(list, "BandwidthBurst", CONFIG_TYPE_UINT, &options->BandwidthBurst) ||
230

231
232
      config_compare(list, "ClientOnly",     CONFIG_TYPE_BOOL, &options->ClientOnly) ||
      config_compare(list, "ContactInfo",    CONFIG_TYPE_STRING, &options->ContactInfo) ||
233

234
235
236
237
238
      config_compare(list, "DebugLogFile",   CONFIG_TYPE_STRING, &options->DebugLogFile) ||
      config_compare(list, "DataDirectory",  CONFIG_TYPE_STRING, &options->DataDirectory) ||
      config_compare(list, "DirPort",        CONFIG_TYPE_UINT, &options->DirPort) ||
      config_compare(list, "DirBindAddress", CONFIG_TYPE_LINELIST, &options->DirBindAddress) ||
      config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_UINT, &options->DirFetchPostPeriod) ||
239
      config_compare(list, "DirPolicy",      CONFIG_TYPE_LINELIST, &options->DirPolicy) ||
240
      config_compare(list, "DirServer",      CONFIG_TYPE_LINELIST, &options->DirServers) ||
241

242
243
244
245
246
247
      config_compare(list, "ExitNodes",      CONFIG_TYPE_STRING, &options->ExitNodes) ||
      config_compare(list, "EntryNodes",     CONFIG_TYPE_STRING, &options->EntryNodes) ||
      config_compare(list, "StrictExitNodes", CONFIG_TYPE_BOOL, &options->StrictExitNodes) ||
      config_compare(list, "StrictEntryNodes", CONFIG_TYPE_BOOL, &options->StrictEntryNodes) ||
      config_compare(list, "ExitPolicy",     CONFIG_TYPE_LINELIST, &options->ExitPolicy) ||
      config_compare(list, "ExcludeNodes",   CONFIG_TYPE_STRING, &options->ExcludeNodes) ||
248

249
250
      config_compare(list, "FascistFirewall",CONFIG_TYPE_BOOL, &options->FascistFirewall) ||
      config_compare(list, "FirewallPorts",CONFIG_TYPE_CSV, &options->FirewallPorts) ||
251
      config_compare(list, "MyFamily",      CONFIG_TYPE_STRING, &options->MyFamily) ||
252
      config_compare(list, "NodeFamily",    CONFIG_TYPE_LINELIST, &options->NodeFamilies) ||
253

254
      config_compare(list, "Group",          CONFIG_TYPE_STRING, &options->Group) ||
255

256
257
258
259
260
      config_compare(list, "HttpProxy",      CONFIG_TYPE_STRING, &options->HttpProxy) ||
      config_compare(list, "HiddenServiceDir", CONFIG_TYPE_LINELIST, &options->RendConfigLines)||
      config_compare(list, "HiddenServicePort", CONFIG_TYPE_LINELIST, &options->RendConfigLines)||
      config_compare(list, "HiddenServiceNodes", CONFIG_TYPE_LINELIST, &options->RendConfigLines)||
      config_compare(list, "HiddenServiceExcludeNodes", CONFIG_TYPE_LINELIST, &options->RendConfigLines)||
261

262
      config_compare(list, "IgnoreVersion",  CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
263

264
      config_compare(list, "KeepalivePeriod",CONFIG_TYPE_UINT, &options->KeepalivePeriod) ||
265

266
267
268
      config_compare(list, "LogLevel",       CONFIG_TYPE_LINELIST, &options->LogOptions) ||
      config_compare(list, "LogFile",        CONFIG_TYPE_LINELIST, &options->LogOptions) ||
      config_compare(list, "LinkPadding",    CONFIG_TYPE_OBSOLETE, NULL) ||
269

270
271
      config_compare(list, "MaxConn",        CONFIG_TYPE_UINT, &options->MaxConn) ||
      config_compare(list, "MaxOnionsPending",CONFIG_TYPE_UINT, &options->MaxOnionsPending) ||
272

273
274
275
      config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
      config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_UINT, &options->NewCircuitPeriod) ||
      config_compare(list, "NumCpus",        CONFIG_TYPE_UINT, &options->NumCpus) ||
276

277
278
279
      config_compare(list, "ORPort",         CONFIG_TYPE_UINT, &options->ORPort) ||
      config_compare(list, "ORBindAddress",  CONFIG_TYPE_LINELIST, &options->ORBindAddress) ||
      config_compare(list, "OutboundBindAddress",CONFIG_TYPE_STRING, &options->OutboundBindAddress) ||
280

281
282
      config_compare(list, "PidFile",        CONFIG_TYPE_STRING, &options->PidFile) ||
      config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
283

284
      config_compare(list, "RedirectExit",   CONFIG_TYPE_LINELIST, &options->RedirectExit) ||
285
      config_compare(list, "RouterFile",     CONFIG_TYPE_OBSOLETE, NULL) ||
286
287
      config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
      config_compare(list, "RunTesting",     CONFIG_TYPE_BOOL, &options->RunTesting) ||
288
      config_compare(list, "RecommendedVersions",CONFIG_TYPE_LINELIST, &options->RecommendedVersions) ||
289
290
      config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
      config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
291

292
293
294
      config_compare(list, "SocksPort",      CONFIG_TYPE_UINT, &options->SocksPort) ||
      config_compare(list, "SocksBindAddress",CONFIG_TYPE_LINELIST,&options->SocksBindAddress) ||
      config_compare(list, "SocksPolicy",     CONFIG_TYPE_LINELIST,&options->SocksPolicy) ||
295
      config_compare(list, "SysLog",          CONFIG_TYPE_LINELIST,&options->LogOptions) ||
296
      config_compare(list, "TrafficShaping", CONFIG_TYPE_OBSOLETE, NULL) ||
297

298
      config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User)
299

300
      ) {
301
302
      /* then we're ok. it matched something. */
    } else {
303
304
      log_fn(LOG_WARN,"Unknown keyword '%s'. Failing.",list->key);
      return -1;
305
306
307
    }

    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
308
  }
309

310
  return 0;
311
312
}

313
314
315
static void
add_default_trusted_dirservers(void)
{
Nick Mathewson's avatar
Nick Mathewson committed
316
317
318
319
  /* moria1 */
  parse_dir_server_line("18.244.0.188:9031 "
                        "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441");
  /* moria2 */
320
  parse_dir_server_line("18.244.0.114:80 "
Nick Mathewson's avatar
Nick Mathewson committed
321
322
323
324
                        "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");
325
326
}

Nick Mathewson's avatar
Nick Mathewson committed
327
328
/** Set <b>options</b> to a reasonable default.
 *
329
 * Call this function before we parse the torrc file.
330
 */
331
332
333
static int
config_assign_defaults(or_options_t *options)
{
334
335
336
  /* set them up as a client only */
  options->SocksPort = 9050;

337
338
339
340
  options->AllowUnverifiedNodes = smartlist_create();
  smartlist_add(options->AllowUnverifiedNodes, "middle");
  smartlist_add(options->AllowUnverifiedNodes, "rendezvous");

Roger Dingledine's avatar
Roger Dingledine committed
341
  config_free_lines(options->ExitPolicy);
342
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
343

344
345
346
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
347
/** Print a usage message for tor. */
348
349
350
static void
print_usage(void)
{
351
  printf("tor -f <torrc> [args]\n"
352
         "See man page for more options. This -h is probably obsolete.\n\n"
353
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
354
         "-d <file>\t\tDebug file\n"
355
//         "-m <max>\t\tMax number of connections\n"
356
357
358
359
         "-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"
360
         "-s <IP>\t\t\tPort to bind to for Socks\n");
361
362
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
363
         "-o <port>\t\tOR port to bind to\n"
364
         "-p <file>\t\tPID file\n");
365
366
}

Nick Mathewson's avatar
Nick Mathewson committed
367
/**
368
369
 * Based on <b>address</b>, guess our public IP address and put it
 * in <b>addr</b>.
Nick Mathewson's avatar
Nick Mathewson committed
370
 */
371
372
373
int
resolve_my_address(const char *address, uint32_t *addr)
{
374
375
  struct in_addr in;
  struct hostent *rent;
376
  char hostname[256];
377
  int explicit_ip=1;
378

379
380
  tor_assert(addr);

381
382
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
383
  } else { /* then we need to guess our address */
384
    explicit_ip = 0; /* it's implicit */
385

386
    if (gethostname(hostname, sizeof(hostname)) < 0) {
387
388
389
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
390
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
391
392
  }

393
  /* now we know hostname. resolve it and keep only the IP */
394

395
  if (tor_inet_aton(hostname, &in) == 0) {
396
397
    /* then we have to resolve it */
    explicit_ip = 0;
398
    rent = (struct hostent *)gethostbyname(hostname);
399
    if (!rent) {
400
      log_fn(LOG_WARN,"Could not resolve local Address %s. Failing.", hostname);
401
402
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
403
    tor_assert(rent->h_length == 4);
404
    memcpy(&in.s_addr, rent->h_addr, rent->h_length);
405
  }
406
407

  if (!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
408
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
409
           "Please set the Address config option to be the IP you want to use.",
410
           hostname, inet_ntoa(in));
411
412
    return -1;
  }
413
414

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
415
  *addr = ntohl(in.s_addr);
416
417
418
  return 0;
}

419
420
static char *
get_default_nickname(void)
421
422
423
424
{
  char localhostname[256];
  char *cp, *out, *outp;

425
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
426
427
428
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
429

430
  /* Put it in lowercase; stop at the first dot. */
431
  for (cp = localhostname; *cp; ++cp) {
432
433
434
435
436
437
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
438

439
440
  /* Strip invalid characters. */
  cp = localhostname;
441
  out = outp = tor_malloc(strlen(localhostname) + 1);
442
443
444
445
446
447
448
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
449

450
451
452
453
454
455
456
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
457
/** Release storage held by <b>options</b> */
458
459
460
static void
free_options(or_options_t *options)
{
461
  config_free_lines(options->LogOptions);
462
  tor_free(options->ContactInfo);
463
464
465
466
467
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
468
469
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
470
  tor_free(options->ExcludeNodes);
471
472
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
473
  tor_free(options->OutboundBindAddress);
474
475
  tor_free(options->User);
  tor_free(options->Group);
476
  tor_free(options->HttpProxy);
477
  config_free_lines(options->RendConfigLines);
478
479
480
481
482
  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);
483
  config_free_lines(options->DirPolicy);
484
  config_free_lines(options->DirServers);
485
  config_free_lines(options->RecommendedVersions);
486
  config_free_lines(options->NodeFamilies);
487
488
  config_free_lines(options->RedirectExit);
  if (options->RedirectExitList) {
Nick Mathewson's avatar
Nick Mathewson committed
489
490
    SMARTLIST_FOREACH(options->RedirectExitList,
                      exit_redirect_t *, p, tor_free(p));
Roger Dingledine's avatar
Roger Dingledine committed
491
    smartlist_free(options->RedirectExitList);
492
    options->RedirectExitList = NULL;
493
  }
494
495
496
  if (options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
    smartlist_free(options->FirewallPorts);
497
    options->FirewallPorts = NULL;
498
  }
499
}
500

501
502
503
504
505
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
506
  memset(options,0,sizeof(or_options_t));
507
508
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
509
  options->StrictEntryNodes = options->StrictExitNodes = 0;
510
  options->ExcludeNodes = tor_strdup("");
511
512
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
513
  /* options->PidFile = tor_strdup("tor.pid"); */
514
  options->PathlenCoinWeight = 0.3;
515
  options->MaxConn = 900;
516
  options->DirFetchPostPeriod = 600;
517
  options->KeepalivePeriod = 300;
518
  options->MaxOnionsPending = 100;
519
  options->NewCircuitPeriod = 30; /* twice a minute */
520
521
  options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurst = 10000000; /* max burst on the token bucket */
522
  options->NumCpus = 1;
523
524
}

525
#ifdef MS_WINDOWS
526
527
528
529
530
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

531
532
533
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
534
535
536
537

  if (is_set)
    return path;
  
538
539
540
541
542
  /* 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))) {
543
544
545
546
    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;
547
548
549
550
551
552
553
554
555
556
  }
  /* 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
557
    return NULL;
558
  }
559
  strlcat(path,"\\tor",MAX_PATH);
560
561
562
563
564
565
566
567
568
569
570
  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);
571
  strlcat(path,"\\torrc",MAX_PATH);
572
573
  return path;
#else
574
  return tor_strdup(CONFDIR "/torrc");
575
576
577
#endif
}

578
579
580
581
/** 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)
582
{
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
  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));
598
  smartlist_free(sl);
599
600
601
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
602
603
604
/** 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. */
605
606
607
int
getconfig(int argc, char **argv, or_options_t *options)
{
608
  struct config_line_t *cl;
609
610
611
612
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
613
614
615
616
617
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
618
  int previous_orport = -1;
619
  int using_default_torrc;
620

621
  if (first_load) { /* first time we're called. save commandline args */
622
623
624
625
626
627
628
629
    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 */
630
    if (options->PidFile)
631
      previous_pidfile = tor_strdup(options->PidFile);
632
    previous_runasdaemon = options->RunAsDaemon;
633
    previous_orport = options->ORPort;
634
635
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
636
  init_options(options);
637

638
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
639
640
641
642
    print_usage();
    exit(0);
  }

643
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
644
645
646
647
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

648
649
/* learn config file name, get config lines, assign them */
  i = 1;
650
  while (i < argc-1 && strcmp(argv[i],"-f")) {
651
652
    i++;
  }
653
654

  if (i < argc-1) { /* we found one */
655
    fname = tor_strdup(argv[i+1]);
656
    using_default_torrc = 0;
657
  } else {
658
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
659
660
661
    char *fn;
    using_default_torrc = 1;
    fn = get_default_conf_file();
662
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
663
664
665
666
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
667
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
668
669
670
671
672
673
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
674
  }
675
  tor_assert(fname);
676
  log(LOG_DEBUG, "Opening config file '%s'", fname);
677

678
  if (config_assign_defaults(options) < 0) {
679
680
    return -1;
  }
681

682
  cf = fopen(fname, "r");
683
684
685
686
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
687
      tor_free(fname);
688
    } else {
689
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
690
      tor_free(fname);
691
692
693
      return -1;
    }
  } else { /* it opened successfully. use it. */
694
    tor_free(fname);
695
696
    if (config_get_lines(cf, &cl)<0)
      return -1;
697
    if (config_assign(options,cl) < 0)
698
      return -1;
699
    config_free_lines(cl);
700
    fclose(cf);
701
  }
702

703
704
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
705
  if (config_assign(options,cl) < 0)
706
    return -1;
707
708
709
710
  config_free_lines(cl);

/* Validate options */

711
  /* first check if any of the previous options have changed but aren't allowed to */
712
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
713
714
715
716
717
718
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

719
  if (previous_runasdaemon && !options->RunAsDaemon) {
720
721
722
723
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

724
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
725
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
726
727
728
    return -1;
  }

729
730
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
731
732
733
    result = -1;
  }

734
  if (options->Nickname == NULL) {
735
    if (server_mode()) {
736
737
738
739
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
740
741
742
743
744
745
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
746
747
748
749
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
750
751
752
753
    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;
754
    }
755
756
  }

757
  if (server_mode()) {
758
759
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
760
    if (resolve_my_address(options->Address, &tmp) < 0)
761
      result = -1;
762
763
  }

764
765
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
766
767
768
    result = -1;
  }

769
770
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
771
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
772
  }
773

774
775
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
776
777
778
    result = -1;
  }

779
780
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
781
782
  }

783
784
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
785
786
  }

787
788
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
789
790
791
    result = -1;
  }

792
793
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
794
795
796
    result = -1;
  }

797
798
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
799
800
    result = -1;
  }
801

802
803
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
804
805
    result = -1;
  }
806

807
  if (options->FascistFirewall && !options->FirewallPorts) {
808
    options->FirewallPorts = smartlist_create();
809
810
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
811
  }
812
  if (options->FirewallPorts) {
813
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
814
815
    {
      i = atoi(cp);
816
      if (i < 1 || i > 65535) {
817
818
819
820
821
822
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
  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.");
846
847
848
    result = -1;
  }

849
850
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
851
852
853
    result = -1;
  }

854
855
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
856
857
858
    result = -1;
  }

859
#define MIN_DIRFETCHPOSTPERIOD 60
860
861
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
862
863
    result = -1;
  }
864
865
866
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
867
  }
868

869
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
870
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
871
872
873
    result = -1;
  }

874
875
876
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
877
878
879
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
880
    if (options->HttpProxyPort == 0) { /* give it a default */
881
882
883
884
      options->HttpProxyPort = 80;
    }
  }

885
886
887
888
889
890
891
892
893
894
895
896
  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;
897
898
899
900
901
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
      return -1;
  }

902
903
904
905
906
907
908
  if (!options->RedirectExitList)
    options->RedirectExitList = smartlist_create();
  for (cl = options->RedirectExit; cl; cl = cl->next) {
    if (parse_redirect_line(options, cl)<0)
      return -1;
  }

Nick Mathewson's avatar
Nick Mathewson committed
909
910
911
912
913
914
915
916
  clear_trusted_dir_servers();
  if (!options->DirServers) {
    add_default_trusted_dirservers();
  } else {
    for (cl = options->DirServers; cl; cl = cl->next) {
      if (parse_dir_server_line(cl->value)<0)
        return -1;
    }
917
918
  }

919
920
921
  if (rend_config_services(options) < 0) {
    result = -1;
  }
922
  return result;
923
924
}

925
926
927
static int
add_single_log(struct config_line_t *level_opt,
               struct config_line_t *file_opt, int isDaemon)
928
{
929
  int levelMin = -1, levelMax = -1;
930
931
932
933
934
935
936
  char *cp, *tmp_sev;

  if (level_opt) {
    cp = strchr(level_opt->value, '-');
    if (cp) {
      tmp_sev = tor_strndup(level_opt->value, cp - level_opt->value);
      levelMin = parse_log_level(tmp_sev);
937
938
939
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", tmp_sev);
940
        return -1;
941
942
943
      }
      tor_free(tmp_sev);
      levelMax = parse_log_level(cp+1);
944
945
946
      if (levelMax < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", cp+1);
947
        return -1;
948
949
950
      }
    } else {
      levelMin = parse_log_level(level_opt->value);
951
952
953
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", level_opt->value);
954
955
        return -1;

956
957
958
959
960
961
962
963
964
965
966
      }
    }
  }
  if (levelMin