config.c 34 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

303
  return 0;
304
305
}

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

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

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

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

337
338
339
  return 0;
}

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

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

372
373
  tor_assert(addr);

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

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

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

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

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

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

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

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

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

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

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

  return out;
}

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

486
487
488
489
490
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
491
  memset(options,0,sizeof(or_options_t));
492
  options->LogOptions = NULL;
493
494
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
495
  options->StrictEntryNodes = options->StrictExitNodes = 0;
496
  options->ExcludeNodes = tor_strdup("");
497
498
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
499
500
501
502
503
  options->ExitPolicy = NULL;
  options->SocksPolicy = NULL;
  options->SocksBindAddress = NULL;
  options->ORBindAddress = NULL;
  options->DirBindAddress = NULL;
504
  options->OutboundBindAddress = NULL;
505
  options->RecommendedVersions = NULL;
506
  options->PidFile = NULL; // tor_strdup("tor.pid");
507
  options->DataDirectory = NULL;
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
  options->RendConfigLines = NULL;
518
  options->FirewallPorts = NULL;
519
  options->DirServers = NULL;
520
521
}

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

Nick Mathewson's avatar
Nick Mathewson committed
557
558
559
/** 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. */
560
561
562
int
getconfig(int argc, char **argv, or_options_t *options)
{
563
  struct config_line_t *cl;
564
565
566
567
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
568
569
570
571
572
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
573
  int previous_orport = -1;
574
  int using_default_torrc;
575

576
  if (first_load) { /* first time we're called. save commandline args */
577
578
579
580
581
582
583
584
    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 */
585
    if (options->PidFile)
586
      previous_pidfile = tor_strdup(options->PidFile);
587
    previous_runasdaemon = options->RunAsDaemon;
588
    previous_orport = options->ORPort;
589
590
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
591
  init_options(options);
592

593
  if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1],"--help"))) {
594
595
596
597
    print_usage();
    exit(0);
  }

598
  if (argc > 1 && (!strcmp(argv[1],"--version"))) {
599
600
601
602
    printf("Tor version %s.\n",VERSION);
    exit(0);
  }

603
604
/* learn config file name, get config lines, assign them */
  i = 1;
605
  while (i < argc-1 && strcmp(argv[i],"-f")) {
606
607
    i++;
  }
608
609

  if (i < argc-1) { /* we found one */
610
    fname = tor_strdup(argv[i+1]);
611
    using_default_torrc = 0;
612
  } else {
613
    /* didn't find one, try CONFDIR */
Nick Mathewson's avatar
Nick Mathewson committed
614
615
616
    char *fn;
    using_default_torrc = 1;
    fn = get_default_conf_file();
617
    if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
618
619
620
621
      fname = fn;
    } else {
      tor_free(fn);
      fn = expand_filename("~/.torrc");
622
      if (fn && file_status(fn) == FN_FILE) {
Nick Mathewson's avatar
Nick Mathewson committed
623
624
625
626
627
628
        fname = fn;
      } else {
        tor_free(fn);
        fname = get_default_conf_file();
      }
    }
629
  }
630
  tor_assert(fname);
631
  log(LOG_DEBUG, "Opening config file '%s'", fname);
632

633
  if (config_assign_defaults(options) < 0) {
634
635
    return -1;
  }
636

637
  cf = fopen(fname, "r");
638
639
640
641
  if (!cf) {
    if (using_default_torrc == 1) {
      log(LOG_NOTICE, "Configuration file '%s' not present, "
          "using reasonable defaults.", fname);
642
      tor_free(fname);
643
    } else {
644
      log(LOG_WARN, "Unable to open configuration file '%s'.", fname);
645
      tor_free(fname);
646
647
648
      return -1;
    }
  } else { /* it opened successfully. use it. */
649
    tor_free(fname);
650
651
    if (config_get_lines(cf, &cl)<0)
      return -1;
652
    if (config_assign(options,cl) < 0)
653
      return -1;
654
    config_free_lines(cl);
655
    fclose(cf);
656
  }
657

658
659
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
660
  if (config_assign(options,cl) < 0)
661
    return -1;
662
663
664
665
  config_free_lines(cl);

/* Validate options */

666
  /* first check if any of the previous options have changed but aren't allowed to */
667
  if (previous_pidfile && strcmp(previous_pidfile,options->PidFile)) {
668
669
670
671
672
673
    log_fn(LOG_WARN,"During reload, PidFile changed from %s to %s. Failing.",
           previous_pidfile, options->PidFile);
    return -1;
  }
  tor_free(previous_pidfile);

674
  if (previous_runasdaemon && !options->RunAsDaemon) {
675
676
677
678
    log_fn(LOG_WARN,"During reload, change from RunAsDaemon=1 to =0 not allowed. Failing.");
    return -1;
  }

679
  if (previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
680
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
681
682
683
    return -1;
  }

684
685
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
686
687
688
    result = -1;
  }

689
  if (options->Nickname == NULL) {
690
    if (server_mode()) {
691
692
693
694
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
695
696
697
698
699
700
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
701
702
703
704
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
705
706
707
708
    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;
709
    }
710
711
  }

712
  if (server_mode()) {
713
714
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
715
    if (resolve_my_address(options->Address, &tmp) < 0)
716
      result = -1;
717
718
  }

719
720
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
721
722
723
    result = -1;
  }

724
725
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
726
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
727
  }
728

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

734
735
  if (options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
736
737
  }

738
739
  if (options->StrictEntryNodes && !strlen(options->EntryNodes)) {
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
740
741
  }

742
743
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
744
745
746
    result = -1;
  }

747
748
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
749
750
751
    result = -1;
  }

752
753
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
754
755
    result = -1;
  }
756

757
758
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
759
760
    result = -1;
  }
761

762
  if (options->FascistFirewall && !options->FirewallPorts) {
763
    options->FirewallPorts = smartlist_create();
764
765
    smartlist_add(options->FirewallPorts, tor_strdup("80"));
    smartlist_add(options->FirewallPorts, tor_strdup("443"));
766
  }
767
  if (options->FirewallPorts) {
768
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
769
770
    {
      i = atoi(cp);
771
      if (i < 1 || i > 65535) {
772
773
774
775
776
777
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
  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.");
801
802
803
    result = -1;
  }

804
805
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
806
807
808
    result = -1;
  }

809
810
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
811
812
813
    result = -1;
  }

814
#define MIN_DIRFETCHPOSTPERIOD 60
815
816
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
817
818
    result = -1;
  }
819
820
821
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
822
  }
823

824
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
825
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
826
827
828
    result = -1;
  }

829
830
831
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
832
833
834
835
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
    options->HttpProxyAddr = ntohl(options->HttpProxyAddr); /* switch to host-order */
836
    if (options->HttpProxyPort == 0) { /* give it a default */
837
838
839
840
      options->HttpProxyPort = 80;
    }
  }

Nick Mathewson's avatar
Nick Mathewson committed
841
842
843
844
845
846
847
848
  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;
    }
849
850
  }

851
852
853
854
  /* XXX look at the various nicknamelists and make sure they're
   * valid and don't have hostnames that are too long.
   */

855
856
857
858
  if (rend_config_services(options) < 0) {
    result = -1;
  }

859
  return result;
860
861
}

862
863
864
static int
add_single_log(struct config_line_t *level_opt,
               struct config_line_t *file_opt, int isDaemon)
865
{
866
  int levelMin = -1, levelMax = -1;
867
868
869
870
871
872
873
  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);
874
875
876
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", tmp_sev);
877
        return -1;
878
879
880
      }
      tor_free(tmp_sev);
      levelMax = parse_log_level(cp+1);
881
882
883
      if (levelMax < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", cp+1);
884
        return -1;
885
886
887
      }
    } else {
      levelMin = parse_log_level(level_opt->value);
888
889
890
      if (levelMin < 0) {
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of "
               "err|warn|notice|info|debug", level_opt->value);
891
892
        return -1;

893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
      }
    }
  }
  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));
908
      return -1;
909
910
911
912
    }
    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
           file_opt->value);
  } else if (!isDaemon) {
913
    add_stream_log(levelMin, levelMax, "<stdout>", stdout);
914
    close_temp_logs();
915
  }
916
  return 0;
917
918
919
920
921
}

/**
 * Initialize the logs based on the configuration file.
 */
922
923
int
config_init_logs(or_options_t *options)
924
925
926
927
928
{
  /* The order of options is:  Level? (File Level?)+
   */
  struct config_line_t *opt = options->LogOptions;

929
930
931
932
  /* Special case if no options are given. */
  if (!opt) {
    add_stream_log(LOG_NOTICE, LOG_ERR, "<stdout>", stdout);
    close_temp_logs();
Roger Dingledine's avatar
Roger Dingledine committed
933
934
    /* don't return yet, in case we want to do a debuglogfile below */
  }
935

936
937
938
  /* Special case for if first option is LogLevel. */
  if (opt && !strcasecmp(opt->key, "LogLevel")) {
    if (opt->next && !strcasecmp(opt->next->key, "LogFile")) {
939
      if (add_single_log(opt, opt->next, options->RunAsDaemon) < 0)
940
        return -1;
941
942
      opt = opt->next->next;
    } else if (!opt->next) {
943
      if (add_single_log(opt, NULL, options->RunAsDaemon) < 0)
944
        return -1;
945
      opt = opt->next;
946
947
    } else {
      ; /* give warning below */
948
949
950
951
952
953
954
955
    }
  }

  while (opt) {
    if (!strcasecmp(opt->key, "LogLevel")) {
      log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile");
      opt = opt->next;
    } else {
956
      tor_assert(!strcasecmp(opt->key, "LogFile"));
957
958
      if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) {
        /* LogFile followed by LogLevel */