config.c 37.2 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
  /* 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))) {
541
542
543
544
    GetCurrentDirectory(MAX_PATH, path);
    is_set = 1;
    log_fn(LOG_WARN, "I couldn't find your application data folder: are you running an ancient version of Windows 95? Defaulting to '%s'", path);
    return path;
545
546
547
548
549
550
551
552
553
554
  }
  /* 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
555
    return NULL;
556
  }
557
  strlcat(path,"\\tor",MAX_PATH);
558
559
560
561
562
563
564
565
566
567
568
  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);
569
  strlcat(path,"\\torrc",MAX_PATH);
570
571
  return path;
#else
572
  return tor_strdup(CONFDIR "/torrc");
573
574
575
#endif
}

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

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

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

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

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

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

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

676
  if (config_assign_defaults(options) < 0) {
677
678
    return -1;
  }
679

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

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

/* Validate options */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

900
901
902
903
904
905
906
  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
907
908
909
910
911
912
913
914
  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;
    }
915
916
  }

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

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

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
      }
    }
  }
  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