config.c 35.4 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
      config_compare(list, "NodeFamily",    CONFIG_TYPE_LINELIST, &options->NodeFamilies) ||
249

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

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

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

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

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

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

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

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

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

280
281
282
      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) ||
283
      config_compare(list, "RecommendedVersions",CONFIG_TYPE_LINELIST, &options->RecommendedVersions) ||
284
285
      config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
      config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
286

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

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

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

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

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

305
  return 0;
306
307
}

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

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

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

Roger Dingledine's avatar
Roger Dingledine committed
336
  config_free_lines(options->ExitPolicy);
337
  options->ExitPolicy = NULL;
Roger Dingledine's avatar
Roger Dingledine committed
338

339
340
341
  return 0;
}

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

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

374
375
  tor_assert(addr);

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

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

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

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

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

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

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

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

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

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

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

  return out;
}

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

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

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

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

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

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

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

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

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

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

662
  if (config_assign_defaults(options) < 0) {
663
664
    return -1;
  }
665

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

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

/* Validate options */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

870
871
872
873
874
875
876
877
878
879
880
881
  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;
882
883
884
885
886
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
      return -1;
  }

Nick Mathewson's avatar
Nick Mathewson committed
887
888
889
890
891
892
893
894
  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;
    }
895
896
  }

897
898
899
  if (rend_config_services(options) < 0) {
    result = -1;
  }
900
  return result;
901
902
}

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

934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
      }
    }
  }
  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));
949
      return -1;
950
951
952
953
    }
    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
           file_opt->value);
  } else if (!isDaemon) {
954
    add_stream_log(levelMin, levelMax, "<stdout>", stdout);
955
    close_temp_logs();
956
  }
957
  return 0;