config.c 36.5 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
      config_compare(list, "RedirectExit",   CONFIG_TYPE_LINELIST, &options->RedirectExit) ||
281
      config_compare(list, "RouterFile",     CONFIG_TYPE_OBSOLETE, NULL) ||
282
283
      config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
      config_compare(list, "RunTesting",     CONFIG_TYPE_BOOL, &options->RunTesting) ||
284
      config_compare(list, "RecommendedVersions",CONFIG_TYPE_LINELIST, &options->RecommendedVersions) ||
285
286
      config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
      config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
287

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

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

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

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

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

306
  return 0;
307
308
}

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

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

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

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

340
341
342
  return 0;
}

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

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

375
376
  tor_assert(addr);

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

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

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

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

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

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

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

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

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

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

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

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
453
/** Release storage held by <b>options</b> */
454
455
456
static void
free_options(or_options_t *options)
{
457
  config_free_lines(options->LogOptions);
458
  tor_free(options->ContactInfo);
459
460
461
462
463
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  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
485
486
487
  config_free_lines(options->RedirectExit);
  if (options->RedirectExitList) {
    SMARTLIST_FOREACH(options->RedirectExit,exit_redirect_t *, p, tor_free(p));
    smartlist_free(options->RedirectExit);
    options->RedirectExit = NULL;                      
  }
488
489
490
  if (options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
    smartlist_free(options->FirewallPorts);
491
    options->FirewallPorts = NULL;
492
  }
493
}
494

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

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

554
555
556
557
/** 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)
558
{
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  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));
574
  smartlist_free(sl);
575
576
577
  return r;
}

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

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

614
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
615
616
617
618
    print_usage();
    exit(0);
  }

619
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
620
621
622
623
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

624
625
/* learn config file name, get config lines, assign them */
  i = 1;
626
  while (i < argc-1 && strcmp(argv[i],"-f")) {
627
628
    i++;
  }
629
630

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

654
  if (config_assign_defaults(options) < 0) {
655
656
    return -1;
  }
657

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

679
680
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
681
  if (config_assign(options,cl) < 0)
682
    return -1;
683
684
685
686
  config_free_lines(cl);

/* Validate options */

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

695
  if (previous_runasdaemon && !options->RunAsDaemon) {
696
697
698
699
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

700
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
701
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
702
703
704
    return -1;
  }

705
706
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
707
708
709
    result = -1;
  }

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

733
  if (server_mode()) {
734
735
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
736
    if (resolve_my_address(options->Address, &tmp) < 0)
737
      result = -1;
738
739
  }

740
741
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
742
743
744
    result = -1;
  }

745
746
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
747
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
748
  }
749

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

755
756
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
757
758
  }

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

763
764
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
765
766
767
    result = -1;
  }

768
769
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
770
771
772
    result = -1;
  }

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

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

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

825
826
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
827
828
829
    result = -1;
  }

830
831
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
832
833
834
    result = -1;
  }

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

845
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
846
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
847
848
849
    result = -1;
  }

850
851
852
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
853
854
855
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
856
    if (options->HttpProxyPort == 0) { /* give it a default */
857
858
859
860
      options->HttpProxyPort = 80;
    }
  }

861
862
863
864
865
866
867
868
869
870
871
872
  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;
873
874
875
876
877
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
      return -1;
  }

878
879
880
881
882
883
884
  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
885
886
887
888
889
890
891
892
  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;
    }
893
894
  }

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

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

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

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

968
969
970
971
  /* Special