config.c 37 KB
Newer Older
1
/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2
3
4
/* See LICENSE for licensing information */
/* $Id$ */

Nick Mathewson's avatar
Nick Mathewson committed
5
6
7
8
9
10
11
/**
 * /file config.c
 *
 * /brief Code to parse and interpret configuration files.
 *
 **/

Roger Dingledine's avatar
Roger Dingledine committed
12
#include "or.h"
13
14
15
#ifdef MS_WINDOWS
#include <shlobj.h>
#endif
Roger Dingledine's avatar
Roger Dingledine committed
16

Nick Mathewson's avatar
Nick Mathewson committed
17
18
/** Enumeration of types which option values can take */
typedef enum config_type_t {
19
20
21
22
23
24
25
26
  CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
  CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
  CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
  CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
  CONFIG_TYPE_CSV,          /**< A list of strings, separated by commas and optional
                              * whitespace. */
  CONFIG_TYPE_LINELIST,     /**< Uninterpreted config lines */
  CONFIG_TYPE_OBSOLETE,     /**< Obsolete (ignored) option. */
Nick Mathewson's avatar
Nick Mathewson committed
27
} config_type_t;
28

Nick Mathewson's avatar
Nick Mathewson committed
29
/** Largest allowed config line */
30
#define CONFIG_LINE_T_MAXLEN 4096
31

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

Nick Mathewson's avatar
Nick Mathewson committed
41

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

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

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

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

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

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

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

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

92
/** Helper: parse the config file and strdup into key/value
93
94
95
 * strings. Set *result to the list, or NULL if parsing the file
 * failed.  Return 0 on success, -1 on failure. Warn and ignore any
 * misformatted lines. */
96
97
98
static int
config_get_lines(FILE *f, struct config_line_t **result)
{
99
100
  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
101
  int r;
102
  char *key, *value;
103

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

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

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

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

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

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

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

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

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

  switch(type) {
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  case CONFIG_TYPE_UINT:
    i = tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
    if (!ok) {
      log(LOG_WARN, "Int keyword '%s %s' is malformed or out of bounds. Skipping.",
          c->key,c->value);
      return 0;
    }
    *(int *)arg = i;
    break;

  case CONFIG_TYPE_BOOL:
    i = tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
    if (!ok) {
      log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1. Skipping.", c->key);
      return 0;
    }
    *(int *)arg = i;
    break;

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

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

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

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

195
196
197
198
199
200
201
    case CONFIG_TYPE_LINELIST:
      /* Note: this reverses the order that the lines appear in.  That's
       * just fine, since we build up the list of lines reversed in the
       * first place. */
      *(struct config_line_t**)arg =
        config_line_prepend(*(struct config_line_t**)arg, c->key, c->value);
      break;
202
203
204
205

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

208
209
210
  return 1;
}

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

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

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

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

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

234
235
236
237
238
239
      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) ||
240

241
242
243
244
245
246
      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) ||
247

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

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

255
256
257
258
259
      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)||
260

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

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

265
266
267
      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) ||
268

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

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

276
277
278
      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) ||
279

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

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

291
292
293
      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) ||
294

295
      config_compare(list, "TrafficShaping", CONFIG_TYPE_OBSOLETE, NULL) ||
296

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

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

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

309
  return 0;
310
311
}

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

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

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

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

343
344
345
  return 0;
}

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

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

378
379
  tor_assert(addr);

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

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

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

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

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

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

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

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

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

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

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

  return out;
}

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

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

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

529
530
531
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
532
533
534
535

  if (is_set)
    return path;
  
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))) {
    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)) {
Nick Mathewson's avatar
Nick Mathewson committed
552
    return NULL;
553
  }
554
555
556
557
558
559
560
561
562
563
564
  is_set = 1;
  return path;
}
#endif

static char *
get_default_conf_file(void)
{
#ifdef MS_WINDOWS
  char *path = tor_malloc(MAX_PATH);
  strlcpy(path, get_windows_conf_root(), MAX_PATH);
565
566
567
  strlcat(path,"\\tor\\torrc",MAX_PATH);
  return path;
#else
568
  return tor_strdup(CONFDIR "/torrc");
569
570
571
#endif
}

572
573
574
575
/** 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)
576
{
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
  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));
592
  smartlist_free(sl);
593
594
595
  return r;
}

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

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

632
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
633
634
635
636
    print_usage();
    exit(0);
  }

637
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
638
639
640
641
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

642
643
/* learn config file name, get config lines, assign them */
  i = 1;
644
  while (i < argc-1 && strcmp(argv[i],"-f")) {
645
646
    i++;
  }
647
648

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

672
  if (config_assign_defaults(options) < 0) {
673
674
    return -1;
  }
675

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

697
698
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
699
  if (config_assign(options,cl) < 0)
700
    return -1;
701
702
703
704
  config_free_lines(cl);

/* Validate options */

705
  /* first check if any of the previous options have changed but aren't allowed to */
706
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
707
708
709
710
711
712
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

713
  if (previous_runasdaemon && !options->RunAsDaemon) {
714
715
716
717
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

718
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
719
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
720
721
722
    return -1;
  }

723
724
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
725
726
727
    result = -1;
  }

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

751
  if (server_mode()) {
752
753
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
754
    if (resolve_my_address(options->Address, &tmp) < 0)
755
      result = -1;
756
757
  }

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

763
764
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
765
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
766
  }
767

768
769
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
770
771
772
    result = -1;
  }

773
774
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
775
776
  }

777
778
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
779
780
  }

781
782
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
783
784
785
    result = -1;
  }

786
787
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
788
789
790
    result = -1;
  }

791
792
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
793
794
    result = -1;
  }
795

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

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

843
844
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
845
846
847
    result = -1;
  }

848
849
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
850
851
852
    result = -1;
  }

853
#define MIN_DIRFETCHPOSTPERIOD 60
854
855
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
856
857
    result = -1;
  }
858
859
860
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
861
  }
862

863
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
864
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
865
866
867
    result = -1;
  }

868
869
870
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
871
872
873
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
874
    if (options->HttpProxyPort == 0) { /* give it a default */
875
876
877
878
      options->HttpProxyPort = 80;
    }
  }

879
880
881
882
883
884
885
886
887
888
889
890
  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;
891
892
893
894
895
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
      return -1;
  }

896
897
898
899
900
901
902
  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
903
904
905
906
907
908
909
910
  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;
    }
911
912
  }

913
914
915
  if (rend_config_services(options) < 0) {
    result = -1;
  }
916
  return result;
917
918
}

919
920
921
static int
add_single_log(struct config_line_t *level_opt,
               struct config_line_t *file_opt, int isDaemon)
922
{
923
  int levelMin = -1, levelMax = -1;
924
925
926
927
928
929
930
  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);
931
932
933
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", tmp_sev);
934
        return -1;
935
936
937
      }
      tor_free(tmp_sev);
      levelMax = parse_log_level(cp+1);
938
939
940
      if (levelMax < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", cp+1);
941
        return -1;
942
943
944
      }
    } else {
      levelMin = parse_log_level(level_opt->value);
945
946
947
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", level_opt->value);
948
949
        return -1;

950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
      }
    }
  }
  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));
965
      return -1;
966
967
968
969
    }
    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
           file_opt->value);
  } else if (!isDaemon) {
970
    add_stream_log(levelMin, levelMax, "<stdout>", stdout);