config.c 35.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
/** 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);
38

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

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

55
    new = tor_malloc(sizeof(struct config_line_t));
56
    s = argv[i];
57

58
59
    while(*s == '-')
      s++;
60

61
62
    new->key = tor_strdup(s);
    new->value = tor_strdup(argv[i+1]);
63
64

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

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

82
83
84
85
86
87
88
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

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

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

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

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

122
  while (front) {
123
124
125
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
126
127
128
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
129
130
131
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
132
133
134
135
136
/** 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.
 */
137
138
139
140
static int
config_compare(struct config_line_t *c, const char *key,
               config_type_t type, void *arg)
{
141
  int i, ok;
142

143
  if (strncasecmp(c->key, key, strlen(c->key)))
144
145
    return 0;

146
  if (strcasecmp(c->key, key)) {
147
148
149
150
    tor_free(c->key);
    c->key = tor_strdup(key);
  }

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

  switch(type) {
156
157
158
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
  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;

192
193
194
195
196
197
198
    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;
199
200
201
202

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

205
206
207
  return 1;
}

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

218
      /* order matters here! abbreviated arguments use the first match. */
219

220
221
222
223
      /* 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) ||
224

225
226
      config_compare(list, "BandwidthRate",  CONFIG_TYPE_UINT, &options->BandwidthRate) ||
      config_compare(list, "BandwidthBurst", CONFIG_TYPE_UINT, &options->BandwidthBurst) ||
227

228
229
      config_compare(list, "ClientOnly",     CONFIG_TYPE_BOOL, &options->ClientOnly) ||
      config_compare(list, "ContactInfo",    CONFIG_TYPE_STRING, &options->ContactInfo) ||
230

231
232
233
234
235
236
      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) ||
      config_compare(list, "DirServer",      CONFIG_TYPE_LINELIST, &options->DirServers) ||
237

238
239
240
241
242
243
      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) ||
244

245
246
      config_compare(list, "FascistFirewall",CONFIG_TYPE_BOOL, &options->FascistFirewall) ||
      config_compare(list, "FirewallPorts",CONFIG_TYPE_CSV, &options->FirewallPorts) ||
247
      config_compare(list, "MyFamily",      CONFIG_TYPE_STRING, &options->MyFamily) ||
248

249
      config_compare(list, "Group",          CONFIG_TYPE_STRING, &options->Group) ||
250

251
252
253
254
255
      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)||
256

257
      config_compare(list, "IgnoreVersion",  CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
258

259
      config_compare(list, "KeepalivePeriod",CONFIG_TYPE_UINT, &options->KeepalivePeriod) ||
260

261
262
263
      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) ||
264

265
266
      config_compare(list, "MaxConn",        CONFIG_TYPE_UINT, &options->MaxConn) ||
      config_compare(list, "MaxOnionsPending",CONFIG_TYPE_UINT, &options->MaxOnionsPending) ||
267

268
269
270
      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) ||
271

272
273
274
      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) ||
275

276
277
      config_compare(list, "PidFile",        CONFIG_TYPE_STRING, &options->PidFile) ||
      config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
278

279
280
281
282
283
284
      config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
      config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
      config_compare(list, "RunTesting",     CONFIG_TYPE_BOOL, &options->RunTesting) ||
      config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) ||
      config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
      config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
285

286
287
288
      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) ||
289

290
      config_compare(list, "TrafficShaping", CONFIG_TYPE_OBSOLETE, NULL) ||
291

292
      config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User)
293

294
      ) {
295
296
      /* then we're ok. it matched something. */
    } else {
297
298
      log_fn(LOG_WARN,"Unknown keyword '%s'. Failing.",list->key);
      return -1;
299
300
301
    }

    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
302
  }
303

304
  return 0;
305
306
}

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

Nick Mathewson's avatar
Nick Mathewson committed
321
322
/** Set <b>options</b> to a reasonable default.
 *
Roger Dingledine's avatar
Roger Dingledine committed
323
 * Call this function when we can't find any torrc config file.
324
 */
325
326
327
static int
config_assign_defaults(or_options_t *options)
{
328
329
330
  /* set them up as a client only */
  options->SocksPort = 9050;

331
332
333
334
  options->AllowUnverifiedNodes = smartlist_create();
  smartlist_add(options->AllowUnverifiedNodes, "middle");
  smartlist_add(options->AllowUnverifiedNodes, "rendezvous");

Roger Dingledine's avatar
Roger Dingledine committed
335
336
337
  config_free_lines(options->ExitPolicy);
  options->ExitPolicy = config_line_prepend(NULL, "ExitPolicy", "reject *:*");

338
339
340
  return 0;
}

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

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

373
374
  tor_assert(addr);

375
376
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
377
  } else { /* then we need to guess our address */
378
    explicit_ip = 0; /* it's implicit */
379

380
    if (gethostname(hostname, sizeof(hostname)) < 0) {
381
382
383
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
384
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
385
386
  }

387
  /* now we know hostname. resolve it and keep only the IP */
388

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

  if (!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
402
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
403
           "Please set the Address config option to be the IP you want to use.",
404
           hostname, inet_ntoa(in));
405
406
    return -1;
  }
407
408

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
409
  *addr = ntohl(in.s_addr);
410
411
412
  return 0;
}

413
414
static char *
get_default_nickname(void)
415
416
417
418
{
  char localhostname[256];
  char *cp, *out, *outp;

419
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
420
421
422
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
423

424
  /* Put it in lowercase; stop at the first dot. */
425
  for (cp = localhostname; *cp; ++cp) {
426
427
428
429
430
431
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
432

433
434
  /* Strip invalid characters. */
  cp = localhostname;
435
  out = outp = tor_malloc(strlen(localhostname) + 1);
436
437
438
439
440
441
442
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
443

444
445
446
447
448
449
450
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
451
/** Release storage held by <b>options</b> */
452
453
454
static void
free_options(or_options_t *options)
{
455
  config_free_lines(options->LogOptions);
456
  tor_free(options->ContactInfo);
457
458
459
460
461
462
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->RouterFile);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
463
464
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
465
  tor_free(options->ExcludeNodes);
466
467
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
468
  tor_free(options->OutboundBindAddress);
469
  tor_free(options->RecommendedVersions);
470
471
  tor_free(options->User);
  tor_free(options->Group);
472
  tor_free(options->HttpProxy);
473
  config_free_lines(options->RendConfigLines);
474
475
476
477
478
  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);
479
  config_free_lines(options->DirServers);
480
481
482
  if (options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
    smartlist_free(options->FirewallPorts);
483
    options->FirewallPorts = NULL;
484
  }
485
}
486

487
488
489
490
491
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
492
  memset(options,0,sizeof(or_options_t));
493
  options->LogOptions = NULL;
494
495
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
496
  options->StrictEntryNodes = options->StrictExitNodes = 0;
497
  options->ExcludeNodes = tor_strdup("");
498
499
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
500
501
502
503
504
  options->ExitPolicy = NULL;
  options->SocksPolicy = NULL;
  options->SocksBindAddress = NULL;
  options->ORBindAddress = NULL;
  options->DirBindAddress = NULL;
505
  options->OutboundBindAddress = NULL;
506
  options->RecommendedVersions = NULL;
507
  options->PidFile = NULL; // tor_strdup("tor.pid");
508
  options->DataDirectory = NULL;
509
  options->PathlenCoinWeight = 0.3;
510
  options->MaxConn = 900;
511
  options->DirFetchPostPeriod = 600;
512
  options->KeepalivePeriod = 300;
513
  options->MaxOnionsPending = 100;
514
  options->NewCircuitPeriod = 30; /* twice a minute */
515
516
  options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurst = 10000000; /* max burst on the token bucket */
517
  options->NumCpus = 1;
518
  options->RendConfigLines = NULL;
519
  options->FirewallPorts = NULL;
520
  options->DirServers = NULL;
521
  options->MyFamily = NULL;
522
523
}

524
525
static char *
get_default_conf_file(void)
526
527
{
#ifdef MS_WINDOWS
528
529
530
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
531
  char *path = tor_malloc(MAX_PATH);
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  /* 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))) {
    tor_free(path);
    return NULL;
  }
  /* 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)) {
549
    tor_free(path);
Nick Mathewson's avatar
Nick Mathewson committed
550
    return NULL;
551
552
553
554
  }
  strlcat(path,"\\tor\\torrc",MAX_PATH);
  return path;
#else
555
  return tor_strdup(CONFDIR "/torrc");
556
557
558
#endif
}

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
/** 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)
{ 
  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));
  smartlist_free(sl); 
  return r;
}

Nick Mathewson's avatar
Nick Mathewson committed
583
584
585
/** 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. */
586
587
588
int
getconfig(int argc, char **argv, or_options_t *options)
{
589
  struct config_line_t *cl;
590
591
592
593
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
594
595
596
597
598
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
599
  int previous_orport = -1;
600
  int using_default_torrc;
601

602
  if (first_load) { /* first time we're called. save commandline args */
603
604
605
606
607
608
609
610
    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 */
611
    if (options->PidFile)
612
      previous_pidfile = tor_strdup(options->PidFile);
613
    previous_runasdaemon = options->RunAsDaemon;
614
    previous_orport = options->ORPort;
615
616
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
617
  init_options(options);
618

619
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
620
621
622
623
    print_usage();
    exit(0);
  }

624
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
625
626
627
628
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

629
630
/* learn config file name, get config lines, assign them */
  i = 1;
631
  while (i < argc-1 && strcmp(argv[i],"-f")) {
632
633
    i++;
  }
634
635

  if (i < argc-1) { /* we found one */
636
    fname = tor_strdup(argv[i+1]);
637
    using_default_torrc = 0;
638
  } else {
639
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
640
641
642
    char *fn;
    using_default_torrc = 1;
    fn = get_default_conf_file();
643
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
644
645
646
647
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
648
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
649
650
651
652
653
654
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
655
  }
656
  tor_assert(fname);
657
  log(LOG_DEBUG, "Opening config file '%s'", fname);
658

659
  if (config_assign_defaults(options) < 0) {
660
661
    return -1;
  }
662

663
  cf = fopen(fname, "r");
664
665
666
667
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
668
      tor_free(fname);
669
    } else {
670
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
671
      tor_free(fname);
672
673
674
      return -1;
    }
  } else { /* it opened successfully. use it. */
675
    tor_free(fname);
676
677
    if (config_get_lines(cf, &cl)<0)
      return -1;
678
    if (config_assign(options,cl) < 0)
679
      return -1;
680
    config_free_lines(cl);
681
    fclose(cf);
682
  }
683

684
685
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
686
  if (config_assign(options,cl) < 0)
687
    return -1;
688
689
690
691
  config_free_lines(cl);

/* Validate options */

692
  /* first check if any of the previous options have changed but aren't allowed to */
693
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
694
695
696
697
698
699
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

700
  if (previous_runasdaemon && !options->RunAsDaemon) {
701
702
703
704
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

705
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
706
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
707
708
709
    return -1;
  }

710
711
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
712
713
714
    result = -1;
  }

715
  if (options->Nickname == NULL) {
716
    if (server_mode()) {
717
718
719
720
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
721
722
723
724
725
726
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
727
728
729
730
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
731
732
733
734
    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;
735
    }
736
737
  }

738
  if (server_mode()) {
739
740
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
741
    if (resolve_my_address(options->Address, &tmp) < 0)
742
      result = -1;
743
744
  }

745
746
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
747
748
749
    result = -1;
  }

750
751
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
752
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
753
  }
754

755
756
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
757
758
759
    result = -1;
  }

760
761
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
762
763
  }

764
765
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
766
767
  }

768
769
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
770
771
772
    result = -1;
  }

773
774
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
775
776
777
    result = -1;
  }

778
779
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
780
781
    result = -1;
  }
782

783
784
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
785
786
    result = -1;
  }
787

788
  if (options->FascistFirewall && !options->FirewallPorts) {
789
    options->FirewallPorts = smartlist_create();
790
791
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
792
  }
793
  if (options->FirewallPorts) {
794
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
795
796
    {
      i = atoi(cp);
797
      if (i < 1 || i > 65535) {
798
799
800
801
802
803
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
  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.");
827
828
829
    result = -1;
  }

830
831
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
832
833
834
    result = -1;
  }

835
836
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
837
838
839
    result = -1;
  }

840
#define MIN_DIRFETCHPOSTPERIOD 60
841
842
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
843
844
    result = -1;
  }
845
846
847
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
848
  }
849

850
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
851
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
852
853
854
    result = -1;
  }

855
856
857
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
858
859
860
861
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
    options->HttpProxyAddr = ntohl(options->HttpProxyAddr); /* switch to host-order */
862
    if (options->HttpProxyPort == 0) { /* give it a default */
863
864
865
866
      options->HttpProxyPort = 80;
    }
  }

867
868
869
870
871
872
873
874
875
876
877
878
879
  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;
  
Nick Mathewson's avatar
Nick Mathewson committed
880
881
882
883
884
885
886
887
  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;
    }
888
889
  }

890
891
892
893
  if (rend_config_services(options) < 0) {
    result = -1;
  }

894
  return result;
895
896
}

897
898
899
static int
add_single_log(struct config_line_t *level_opt,
               struct config_line_t *file_opt, int isDaemon)
900
{
901
  int levelMin = -1, levelMax = -1;
902
903
904
905
906
907
908
  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);
909
910
911
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", tmp_sev);
912
        return -1;
913
914
915
      }
      tor_free(tmp_sev);
      levelMax = parse_log_level(cp+1);
916
917
918
      if (levelMax < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", cp+1);
919
        return -1;
920
921
922
      }
    } else {
      levelMin = parse_log_level(level_opt->value);
923
924
925
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", level_opt->value);
926
927
        return -1;

928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
      }
    }
  }
  if (levelMin < 0 && levelMax < 0) {
    levelMin = LOG_NOTICE;
    levelMax = LOG_ERR;
  } else if (levelMin < 0) {
    levelMin = levelMax;
  } else {
    levelMax = LOG_ERR;
  }
  if (file_opt) {
    if (add_file_log(levelMin, levelMax, file_opt->value) < 0) {
      log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value,
             strerror(errno));
943
      return -1;
944
945
946
947
    }
    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
           file_opt->value);
  } else if (!isDaemon) {
948
    add_stream_log(levelMin, levelMax, "<stdout>", stdout);
949
    close_temp_logs();
950
  }
951
  return 0;
952
953
954
955
956
}

/**
 * Initialize the logs based on the configuration file.
 */
957
958
int
config_init_logs(or_options_t *options)
959
960
961
962
963
{
  /* The order of options is:  Level? (File Level?)+
   */
  struct config_line_t *opt = options->LogOptions;

964
965
966
967
  /* Special case if no options are given. */
  if (!opt) {
    add_stream_log(LOG_NOTICE, LOG_ERR, "<stdout>", stdout);
    close_temp_logs();
Roger Dingledine's avatar
Roger Dingledine committed
968
969
    /* don't return yet, in case we want to do a debuglogfile below */
  }
970

971
972
973
  /* Special case for if first option is LogLevel. */
  if (opt && !strcasecmp(opt->key, "LogLevel")) {
    if (opt->next && !strcasecmp(opt->next->key, "LogFile")) {
974
      if (add_single_log(opt, opt->next, options->RunAsDaemon) < 0)