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

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

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

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

Nick Mathewson's avatar
Nick Mathewson committed
29
/* An abbreviation for a configuration option allowed on the command line */
30
typedef struct config_abbrev_t {
31
32
  const char *abbreviated;
  const char *full;
33
  int commandline_only;
34
35
} config_abbrev_t;

36
37
38
39
/* Handy macro for declaring "In the config file or on the command line,
 * you can abbreviate <b>tok</b>s as <b>tok</b>". */
#define PLURAL(tok) { #tok, #tok "s", 0 }

Nick Mathewson's avatar
Nick Mathewson committed
40
/* A list of command-line abbreviations. */
41
static config_abbrev_t config_abbrevs[] = {
42
43
44
45
46
47
48
49
  PLURAL(ExitNode),
  PLURAL(EntryNodes),
  PLURAL(ExcludeNode),
  PLURAL(FirewallPort),
  PLURAL(HiddenServiceNode),
  PLURAL(HiddenServiceExcludeNode),
  PLURAL(RendNode),
  PLURAL(RendExcludeNode),
50
51
52
  { "l", "LogLevel", 1},
  { "BandwidthRate", "BandwidthRateBytes", 1},
  { "BandwidthBurst", "BandwidthBurstBytes", 1},
53
  { NULL, NULL , 0},
54
};
55
#undef PLURAL
56

57
/** A variable allowed in the configuration file or on the command line. */
58
typedef struct config_var_t {
59
60
61
62
  const char *name; /**< The full keyword (case insensitive). */
  config_type_t type; /**< How to interpret the type and turn it into a value. */
  off_t var_offset; /**< Offset of the corresponding member of or_options_t. */
  const char *initvalue; /**< String (or null) describing initial value. */
63
64
} config_var_t;

Nick Mathewson's avatar
Nick Mathewson committed
65
/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
Nick Mathewson's avatar
Nick Mathewson committed
66
#define STRUCT_OFFSET(tp, member) ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
Nick Mathewson's avatar
Nick Mathewson committed
67
68
69
70
/** An entry for config_vars: "The option <b>name</b> has type
 * CONFIG_TYPE_<b>conftype</b>, and corresponds to
 * or_options_t.<b>member</b>"
 */
71
72
#define VAR(name,conftype,member,initvalue) \
  { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), initvalue }
Nick Mathewson's avatar
Nick Mathewson committed
73
/** An entry for config_vars: "The option <b>name</b> is obsolete." */
74
#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL }
75

Nick Mathewson's avatar
Nick Mathewson committed
76
77
78
79
/** Array of configuration options.  Until we disallow nonstandard
 * abbreviations, order is significant, since the first matching option will
 * be chosen first.
 */
80
static config_var_t config_vars[] = {
81
  VAR("Address",             STRING,   Address,              NULL),
82
  VAR("AllowUnverifiedNodes",CSV,      AllowUnverifiedNodes, "middle,rendezvous"),
83
  VAR("AuthoritativeDirectory",BOOL,   AuthoritativeDir,     "0"),
84
85
  VAR("BandwidthRateBytes",  UINT,     BandwidthRateBytes,   "800000"),
  VAR("BandwidthBurstBytes", UINT,     BandwidthBurstBytes,  "50000000"),
86
87
  VAR("ClientOnly",          BOOL,     ClientOnly,           "0"),
  VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
88
  VAR("ControlPort",         UINT,     ControlPort,          "0"),
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  VAR("DebugLogFile",        STRING,   DebugLogFile,         NULL),
  VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
  VAR("DirPort",             UINT,     DirPort,              "0"),
  VAR("DirBindAddress",      LINELIST, DirBindAddress,       NULL),
  VAR("DirFetchPostPeriod",  UINT,     DirFetchPostPeriod,   "600"),
  VAR("DirPolicy",           LINELIST, DirPolicy,            NULL),
  VAR("DirServer",           LINELIST, DirServers,           NULL),
  VAR("ExitNodes",           STRING,   ExitNodes,            NULL),
  VAR("EntryNodes",          STRING,   EntryNodes,           NULL),
  VAR("StrictExitNodes",     BOOL,     StrictExitNodes,      "0"),
  VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
  VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
  VAR("ExcludeNodes",        STRING,   ExcludeNodes,         NULL),
  VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
  VAR("FirewallPorts",       CSV,      FirewallPorts,        NULL),
  VAR("MyFamily",            STRING,   MyFamily,             NULL),
  VAR("NodeFamily",          LINELIST, NodeFamilies,         NULL),
  VAR("Group",               STRING,   Group,                NULL),
107
  VAR("HashedControlPassword",STRING,  HashedControlPassword, NULL),
108
109
110
111
112
113
114
115
116
  VAR("HttpProxy",           STRING,   HttpProxy,            NULL),
  VAR("HiddenServiceDir",    LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServicePort",   LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServiceNodes",  LINELIST, RendConfigLines,      NULL),
  VAR("HiddenServiceExcludeNodes", LINELIST, RendConfigLines,NULL),
  VAR("IgnoreVersion",       BOOL,     IgnoreVersion,        "0"),
  VAR("KeepalivePeriod",     UINT,     KeepalivePeriod,      "300"),
  VAR("LogLevel",            LINELIST, LogOptions,           NULL),
  VAR("LogFile",             LINELIST, LogOptions,           NULL),
117
  OBSOLETE("LinkPadding"),
118
119
120
121
122
123
124
125
126
127
128
129
130
  VAR("MaxConn",             UINT,     MaxConn,              "1024"),
  VAR("MaxOnionsPending",    UINT,     MaxOnionsPending,     "100"),
  VAR("MonthlyAccountingStart",UINT,   AccountingStart,      "0"),
  VAR("AccountingMaxKB",     UINT,     AccountingMaxKB,      "0"),
  VAR("Nickname",            STRING,   Nickname,             NULL),
  VAR("NewCircuitPeriod",    UINT,     NewCircuitPeriod,     "30"),
  VAR("NumCpus",             UINT,     NumCpus,              "1"),
  VAR("ORPort",              UINT,     ORPort,               "0"),
  VAR("ORBindAddress",       LINELIST, ORBindAddress,        NULL),
  VAR("OutboundBindAddress", STRING,   OutboundBindAddress,  NULL),
  VAR("PidFile",             STRING,   PidFile,              NULL),
  VAR("PathlenCoinWeight",   DOUBLE,   PathlenCoinWeight,    "0.3"),
  VAR("RedirectExit",        LINELIST, RedirectExit,         NULL),
131
  OBSOLETE("RouterFile"),
132
133
134
135
136
  VAR("RunAsDaemon",         BOOL,     RunAsDaemon,          "0"),
  VAR("RunTesting",          BOOL,     RunTesting,           "0"),
  VAR("RecommendedVersions", LINELIST, RecommendedVersions,  NULL),
  VAR("RendNodes",           STRING,   RendNodes,            NULL),
  VAR("RendExcludeNodes",    STRING,   RendExcludeNodes,     NULL),
137
  VAR("SocksPort",           UINT,     SocksPort,            "9050"),
138
139
140
  VAR("SocksBindAddress",    LINELIST, SocksBindAddress,     NULL),
  VAR("SocksPolicy",         LINELIST, SocksPolicy,          NULL),
  VAR("SysLog",              LINELIST, LogOptions,           NULL),
141
  OBSOLETE("TrafficShaping"),
142
143
  VAR("User",                STRING,   User,                 NULL),
  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
144
145
146
147
};
#undef VAR
#undef OBSOLETE

Nick Mathewson's avatar
Nick Mathewson committed
148
/** Largest allowed config line */
149
#define CONFIG_LINE_T_MAXLEN 4096
150

151
static struct config_line_t *config_get_commandlines(int argc, char **argv);
152
static int config_get_lines(FILE *f, struct config_line_t **result);
153
static void config_free_lines(struct config_line_t *front);
Nick Mathewson's avatar
Nick Mathewson committed
154
static int config_assign_line(or_options_t *options, struct config_line_t *c);
155
static int config_assign(or_options_t *options, struct config_line_t *list);
156
static int parse_dir_server_line(const char *line);
Roger Dingledine's avatar
Roger Dingledine committed
157
158
static int parse_redirect_line(or_options_t *options,
                               struct config_line_t *line);
159
static const char *expand_abbrev(const char *option, int commandline_only);
160
static config_var_t *config_find_option(const char *key);
161

Nick Mathewson's avatar
Nick Mathewson committed
162
/** If <b>option</b> is an official abbreviation for a longer option,
163
164
165
 * return the longer option.  Otherwise return <b>option</b>.
 * If <b>command_line</b> is set, apply all abbreviations.  Otherwise, only
 * apply abbreviations that work for the config file and the command line. */
166
static const char *
167
expand_abbrev(const char *option, int command_line)
168
169
170
171
{
  int i;
  for (i=0; config_abbrevs[i].abbreviated; ++i) {
    /* Abbreviations aren't casei. */
172
173
    if (!strcmp(option,config_abbrevs[i].abbreviated) &&
        (command_line || !config_abbrevs[i].commandline_only)) {
174
      return config_abbrevs[i].full;
175
    }
176
177
178
  }
  return option;
}
Nick Mathewson's avatar
Nick Mathewson committed
179

Nick Mathewson's avatar
Nick Mathewson committed
180
/** Helper: Read a list of configuration options from the command line. */
181
182
183
static struct config_line_t *
config_get_commandlines(int argc, char **argv)
{
184
185
  struct config_line_t *new;
  struct config_line_t *front = NULL;
186
187
188
  char *s;
  int i = 1;

189
  while (i < argc-1) {
190
191
192
    if (!strcmp(argv[i],"-f") ||
        !strcmp(argv[i],"--hash-password")) {
      i += 2; /* command-line option with argument. ignore them. */
193
      continue;
194
195
    } else if (!strcmp(argv[i],"--list-fingerprint")) {
      i += 1; /* command-line option. ignore it. */
196
      continue;
197
198
    }

199
    new = tor_malloc(sizeof(struct config_line_t));
200
    s = argv[i];
201

202
203
    while(*s == '-')
      s++;
204

205
    new->key = tor_strdup(expand_abbrev(s, 1));
206
    new->value = tor_strdup(argv[i+1]);
207
208

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
209
        new->key, new->value);
210
211
212
213
214
215
216
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

Nick Mathewson's avatar
Nick Mathewson committed
217
218
/** Helper: allocate a new configuration option mapping 'key' to 'val',
 * prepend it to 'front', and return the newly allocated config_line_t */
219
220
221
222
223
224
static struct config_line_t *
config_line_prepend(struct config_line_t *front,
                    const char *key,
                    const char *val)
{
  struct config_line_t *newline;
225

226
227
228
229
230
231
232
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

233
/** Helper: parse the config file and strdup into key/value
234
235
236
 * 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. */
237
238
239
static int
config_get_lines(FILE *f, struct config_line_t **result)
{
240
241
  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
242
  int r;
243
  char *key, *value;
244

245
  while ((r = parse_line_from_file(line, sizeof(line), f, &key, &value)) > 0) {
246
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
247
  }
248
249

  if (r < 0) {
250
251
252
    *result = NULL;
    return -1;
  }
253
254
  *result = front;
  return 0;
Roger Dingledine's avatar
Roger Dingledine committed
255
256
}

Nick Mathewson's avatar
Nick Mathewson committed
257
258
259
/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
260
261
262
static void
config_free_lines(struct config_line_t *front)
{
263
  struct config_line_t *tmp;
264

265
  while (front) {
266
267
268
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
269
270
271
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
272
273
274
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
275
276
277
278
/** If <b>key</b> is a configuration option, return the corresponding
 * config_var_t.  Otherwise, if <b>key</b> is a non-standard abbreviation,
 * warn, and return the corresponding config_var_t.  Otherwise return NULL.
 */
279
280
281
static config_var_t *config_find_option(const char *key)
{
  int i;
Nick Mathewson's avatar
Nick Mathewson committed
282
  /* First, check for an exact (case-insensitive) match */
283
  for (i=0; config_vars[i].name; ++i) {
Nick Mathewson's avatar
Nick Mathewson committed
284
    if (!strcasecmp(key, config_vars[i].name))
285
      return &config_vars[i];
Nick Mathewson's avatar
Nick Mathewson committed
286
287
288
289
  }
  /* If none, check for an abbreviated match */
  for (i=0; config_vars[i].name; ++i) {
    if (!strncasecmp(key, config_vars[i].name, strlen(key))) {
290
291
292
293
294
295
      log_fn(LOG_WARN, "The abbreviation '%s' is deprecated. "
          "Tell Nick and Roger to make it official, or just use '%s' instead",
             key, config_vars[i].name);
      return &config_vars[i];
    }
  }
Nick Mathewson's avatar
Nick Mathewson committed
296
  /* Okay, unrecogized options */
297
298
299
  return NULL;
}

Nick Mathewson's avatar
Nick Mathewson committed
300
301
/** If <b>c</b> is a syntactically valid configuration line, update
 * <b>options</b> with its value and return 0.  Otherwise return -1. */
302
static int
303
config_assign_line(or_options_t *options, struct config_line_t *c)
304
{
305
  int i, ok;
306
307
  config_var_t *var;
  void *lvalue;
308

309
310
311
312
313
  var = config_find_option(c->key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", c->key);
    return -1;
  }
314

315
316
  /* Put keyword into canonical case. */
  if (strcmp(var->name, c->key)) {
317
    tor_free(c->key);
318
    c->key = tor_strdup(var->name);
319
320
  }

Nick Mathewson's avatar
Nick Mathewson committed
321
  lvalue = ((char*)options) + var->var_offset;
322
  switch(var->type) {
323

324
325
326
327
328
329
330
  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;
    }
331
    *(int *)lvalue = i;
332
333
334
335
336
337
338
339
    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;
    }
340
    *(int *)lvalue = i;
341
342
343
    break;

  case CONFIG_TYPE_STRING:
344
345
    tor_free(*(char **)lvalue);
    *(char **)lvalue = tor_strdup(c->value);
346
347
348
    break;

  case CONFIG_TYPE_DOUBLE:
349
    *(double *)lvalue = atof(c->value);
350
351
352
    break;

  case CONFIG_TYPE_CSV:
353
354
    if (*(smartlist_t**)lvalue == NULL)
      *(smartlist_t**)lvalue = smartlist_create();
355

356
    smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
357
358
359
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

360
361
362
363
    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. */
364
365
      *(struct config_line_t**)lvalue =
        config_line_prepend(*(struct config_line_t**)lvalue, c->key, c->value);
366
      break;
367
368
369
370

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

373
  return 0;
374
375
}

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/** Return a canonicalized list of the options assigned for key.
 */
struct config_line_t *
config_get_assigned_option(or_options_t *options, const char *key)
{
  config_var_t *var;
  const void *value;
  char buf[32];
  struct config_line_t *result;

  var = config_find_option(key);
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", key);
    return NULL;
  }
  value = ((char*)options) + var->var_offset;

  if (var->type == CONFIG_TYPE_LINELIST) {
    /* Linelist requires special handling: we just copy and return it. */
    const struct config_line_t *next_in = value;
    struct config_line_t **next_out = &result;
    while (next_in) {
      *next_out = tor_malloc(sizeof(struct config_line_t));
      (*next_out)->key = tor_strdup(next_in->key);
      (*next_out)->value = tor_strdup(next_in->value);
      next_in = next_in->next;
      next_out = &((*next_out)->next);
    }
    (*next_out) = NULL;
    return result;
  }

  result = tor_malloc_zero(sizeof(struct config_line_t));
  result->key = tor_strdup(var->name);
  switch(var->type)
    {
    case CONFIG_TYPE_STRING:
      result->value = tor_strdup(value ? (char*)value : "");
      break;
    case CONFIG_TYPE_UINT:
      tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
      result->value = tor_strdup(buf);
      break;
    case CONFIG_TYPE_DOUBLE:
      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
      result->value = tor_strdup(buf);
      break;
    case CONFIG_TYPE_BOOL:
      result->value = tor_strdup(*(int*)value ? "1" : "0");
      break;
    case CONFIG_TYPE_CSV:
      if (value)
        result->value = smartlist_join_strings((smartlist_t*)value,",",0,NULL);
      else
        result->value = tor_strdup("");
      break;
    default:
      tor_free(result->key);
      tor_free(result);
435
      log_fn(LOG_WARN,"Bug: unknown type %d for known key %s", var->type, key);
436
437
438
439
440
441
      return NULL;
    }

  return result;
}

442
/** Iterate through the linked list of requested options <b>list</b>.
Nick Mathewson's avatar
Nick Mathewson committed
443
 * For each item, convert as appropriate and assign to <b>options</b>.
444
445
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
446
447
448
449
static int
config_assign(or_options_t *options, struct config_line_t *list)
{
  while (list) {
450
451
452
453
454
455
    const char *full = expand_abbrev(list->key, 0);
    if (strcmp(full,list->key)) {
      tor_free(list->key);
      list->key = tor_strdup(full);
    }

456
    if (config_assign_line(options, list))
457
      return -1;
458
    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
459
  }
460
  return 0;
461
462
}

463
464
465
static void
add_default_trusted_dirservers(void)
{
Nick Mathewson's avatar
Nick Mathewson committed
466
467
468
469
  /* moria1 */
  parse_dir_server_line("18.244.0.188:9031 "
                        "FFCB 46DB 1339 DA84 674C 70D7 CB58 6434 C437 0441");
  /* moria2 */
470
  parse_dir_server_line("18.244.0.114:80 "
Nick Mathewson's avatar
Nick Mathewson committed
471
472
473
474
                        "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");
475
476
}

Nick Mathewson's avatar
Nick Mathewson committed
477
/** Print a usage message for tor. */
478
479
480
static void
print_usage(void)
{
481
  printf("tor -f <torrc> [args]\n"
482
483
         "See man page for more options. This -h is obsolete.\n");
#if 0
484
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
485
         "-d <file>\t\tDebug file\n"
486
487
488
489
         "-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"
490
         "-s <IP>\t\t\tPort to bind to for Socks\n");
491
492
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
493
         "-o <port>\t\tOR port to bind to\n"
494
         "-p <file>\t\tPID file\n");
495
#endif
496
497
}

Nick Mathewson's avatar
Nick Mathewson committed
498
/**
499
500
 * Based on <b>address</b>, guess our public IP address and put it
 * in <b>addr</b>.
Nick Mathewson's avatar
Nick Mathewson committed
501
 */
502
503
504
int
resolve_my_address(const char *address, uint32_t *addr)
{
505
506
  struct in_addr in;
  struct hostent *rent;
507
  char hostname[256];
508
  int explicit_ip=1;
509

510
511
  tor_assert(addr);

512
513
  if (address) {
    strlcpy(hostname, address, sizeof(hostname));
514
  } else { /* then we need to guess our address */
515
    explicit_ip = 0; /* it's implicit */
516

517
    if (gethostname(hostname, sizeof(hostname)) < 0) {
518
519
520
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
521
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
522
523
  }

524
  /* now we know hostname. resolve it and keep only the IP */
525

526
  if (tor_inet_aton(hostname, &in) == 0) {
527
528
    /* then we have to resolve it */
    explicit_ip = 0;
529
    rent = (struct hostent *)gethostbyname(hostname);
530
    if (!rent) {
531
      log_fn(LOG_WARN,"Could not resolve local Address %s. Failing.", hostname);
532
533
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
534
    tor_assert(rent->h_length == 4);
535
    memcpy(&in.s_addr, rent->h_addr, rent->h_length);
536
  }
537
538

  if (!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
539
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
540
           "Please set the Address config option to be the IP you want to use.",
541
           hostname, inet_ntoa(in));
542
543
    return -1;
  }
544
545

  log_fn(LOG_DEBUG, "Resolved Address to %s.", inet_ntoa(in));
546
  *addr = ntohl(in.s_addr);
547
548
549
  return 0;
}

550
551
static char *
get_default_nickname(void)
552
553
554
555
{
  char localhostname[256];
  char *cp, *out, *outp;

556
  if (gethostname(localhostname, sizeof(localhostname)) < 0) {
557
558
559
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
560

561
  /* Put it in lowercase; stop at the first dot. */
562
  for (cp = localhostname; *cp; ++cp) {
563
564
565
566
567
568
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
569

570
571
  /* Strip invalid characters. */
  cp = localhostname;
572
  out = outp = tor_malloc(strlen(localhostname) + 1);
573
574
575
576
577
578
579
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
580

581
582
583
584
585
586
587
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
588
/** Release storage held by <b>options</b> */
589
590
591
static void
free_options(or_options_t *options)
{
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  int i;
  void *lvalue;

  for (i=0; config_vars[i].name; ++i) {
    lvalue = ((char*)options) + config_vars[i].var_offset;
    switch(config_vars[i].type) {
      case CONFIG_TYPE_UINT:
      case CONFIG_TYPE_BOOL:
      case CONFIG_TYPE_DOUBLE:
      case CONFIG_TYPE_OBSOLETE:
        break; /* nothing to free for these config types */
      case CONFIG_TYPE_STRING:
        tor_free(*(char **)lvalue);
        break;
      case CONFIG_TYPE_LINELIST:
        config_free_lines(*(struct config_line_t**)lvalue);
        *(struct config_line_t**)lvalue = NULL;
        break;
      case CONFIG_TYPE_CSV:
        if (*(smartlist_t**)lvalue) {
          SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp));
          smartlist_free(*(smartlist_t**)lvalue);
          *(smartlist_t**)lvalue = NULL;
        }
        break;
    }
  }
  /* XXX this last part is an exception. can we make it not needed? */
620
  if (options->RedirectExitList) {
Nick Mathewson's avatar
Nick Mathewson committed
621
622
    SMARTLIST_FOREACH(options->RedirectExitList,
                      exit_redirect_t *, p, tor_free(p));
Roger Dingledine's avatar
Roger Dingledine committed
623
    smartlist_free(options->RedirectExitList);
624
    options->RedirectExitList = NULL;
625
  }
626
}
627

628
629
630
631
632
/** Set <b>options</b> to hold reasonable defaults for most options.
 * Each option defaults to zero. */
static void
init_options(or_options_t *options)
{
633
634
635
636
  int i;
  struct config_line_t *c;
  config_var_t *var;

637
  memset(options,0,sizeof(or_options_t));
638
639
640
641
642
643
644
645
646
647
  for (i=0; config_vars[i].name; ++i) {
    var = &config_vars[i];
    if(!var->initvalue)
      continue; /* defaults to NULL or 0 */
    c = tor_malloc(sizeof(struct config_line_t));
    c->key = tor_strdup(var->name);
    c->value = tor_strdup(var->initvalue);
    config_assign_line(options,c);
    config_free_lines(c);
  }
648
649
}

650
#ifdef MS_WINDOWS
651
652
653
654
655
static char *get_windows_conf_root(void)
{
  static int is_set = 0;
  static char path[MAX_PATH+1];

656
657
658
  LPITEMIDLIST idl;
  IMalloc *m;
  HRESULT result;
659
660
661

  if (is_set)
    return path;
662

663
664
665
666
667
  /* 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))) {
668
669
670
671
    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;
672
673
674
675
676
677
678
679
680
681
  }
  /* 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
682
    return NULL;
683
  }
684
  strlcat(path,"\\tor",MAX_PATH);
685
686
687
688
689
690
691
692
693
694
695
  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);
696
  strlcat(path,"\\torrc",MAX_PATH);
697
698
  return path;
#else
699
  return tor_strdup(CONFDIR "/torrc");
700
701
702
#endif
}

703
704
705
706
/** 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)
707
{
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
  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));
723
  smartlist_free(sl);
724
725
726
  return r;
}

Roger Dingledine's avatar
Roger Dingledine committed
727
728
smartlist_t *config_get_default_firewallports(void) {
  static smartlist_t *answer;
729

Roger Dingledine's avatar
Roger Dingledine committed
730
731
732
733
  if(!answer) {
    answer = smartlist_create();
    smartlist_add(answer, tor_strdup("80"));
    smartlist_add(answer, tor_strdup("443"));
734
  }
Roger Dingledine's avatar
Roger Dingledine committed
735
736
  return answer;
}
737

Roger Dingledine's avatar
Roger Dingledine committed
738
739
740
741
742
743
static int
validate_options(or_options_t *options)
{
  int i;
  int result = 0;
  struct config_line_t *cl;
744

745
746
  if (options->ORPort < 0 || options->ORPort > 65535) {
    log(LOG_WARN, "ORPort option out of bounds.");
747
748
749
    result = -1;
  }

750
  if (options->Nickname == NULL) {
751
    if (server_mode()) {
752
753
754
755
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
756
757
758
759
760
761
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
762
763
764
765
    if (strlen(options->Nickname) == 0) {
      log_fn(LOG_WARN, "Nickname must have at least one character");
      result = -1;
    }
766
767
768
769
    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;
770
    }
771
772
  }

773
  if (server_mode()) {
774
775
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
776
    if (resolve_my_address(options->Address, &tmp) < 0)
777
      result = -1;
778
779
  }

780
781
  if (options->SocksPort < 0 || options->SocksPort > 65535) {
    log(LOG_WARN, "SocksPort option out of bounds.");
782
783
784
    result = -1;
  }

785
786
  if (options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN, "SocksPort and ORPort are both undefined? Quitting.");
787
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
788
  }
789

790
791
792
793
794
  if (options->ControlPort < 0 || options->ControlPort > 65535) {
    log(LOG_WARN, "ControlPort option out of bounds.");
    result = -1;
  }

795
796
  if (options->DirPort < 0 || options->DirPort > 65535) {
    log(LOG_WARN, "DirPort option out of bounds.");
797
798
799
    result = -1;
  }

800
801
  if (options->StrictExitNodes &&
      (!options->ExitNodes || !strlen(options->ExitNodes))) {
802
    log(LOG_WARN, "StrictExitNodes set, but no ExitNodes listed.");
803
804
  }

805
806
  if (options->StrictEntryNodes &&
      (!options->EntryNodes || !strlen(options->EntryNodes))) {
807
    log(LOG_WARN, "StrictEntryNodes set, but no EntryNodes listed.");
808
809
  }

810
811
  if (options->AuthoritativeDir && options->RecommendedVersions == NULL) {
    log(LOG_WARN, "Directory servers must configure RecommendedVersions.");
812
813
814
    result = -1;
  }

815
816
  if (options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN, "Running as authoritative directory, but no DirPort set.");
817
818
819
    result = -1;
  }

820
821
  if (options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN, "Running as authoritative directory, but no ORPort set.");
Roger Dingledine's avatar
Roger Dingledine committed
822
823
    result = -1;
  }
824

825
826
  if (options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN, "Running as authoritative directory, but ClientOnly also set.");
Roger Dingledine's avatar
Roger Dingledine committed
827
828
    result = -1;
  }
829

830
  if (options->FirewallPorts) {
831
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
832
833
    {
      i = atoi(cp);
834
      if (i < 1 || i > 65535) {
835
836
837
838
839
840
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
  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.");
864
865
866
    result = -1;
  }

867
868
  if (options->MaxConn < 1) {
    log(LOG_WARN, "MaxConn option must be a non-zero positive integer.");
869
870
871
    result = -1;
  }

872
873
  if (options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_WARN, "MaxConn option must be less than %d.", MAXCONNECTIONS);
874
875
876
    result = -1;
  }

877
#define MIN_DIRFETCHPOSTPERIOD 60
878
879
  if (options->DirFetchPostPeriod < MIN_DIRFETCHPOSTPERIOD) {
    log(LOG_WARN, "DirFetchPostPeriod option must be at least %d.", MIN_DIRFETCHPOSTPERIOD);
880
881
    result = -1;
  }
882
883
884
  if (options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME / 2) {
    log(LOG_WARN, "DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME / 2;
885
  }
886

887
  if (options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
888
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
889
890
891
    result = -1;
  }

892
  if (options->AccountingStart < 0 || options->AccountingStart > 31) {
893
    log(LOG_WARN,"Monthly accounting must start on a day of the month, and no months have %d days.",
894
895
896
897
898
899
900
        options->AccountingStart);
    result = -1;
  } else if (options->AccountingStart > 28) {
    log(LOG_WARN,"Not every month has %d days.",options->AccountingStart);
    result = -1;
  }

901
902
903
  if (options->HttpProxy) { /* parse it now */
    if (parse_addr_port(options->HttpProxy, NULL,
                        &options->HttpProxyAddr, &options->HttpProxyPort) < 0) {
904
905
906
      log(LOG_WARN,"HttpProxy failed to parse or resolve. Please fix.");
      result = -1;
    }
907
    if (options->HttpProxyPort == 0) { /* give it a default */
908
909
910
911
      options->HttpProxyPort = 80;
    }
  }

912
  if (check_nickname_list(options->ExitNodes, "ExitNodes"))
Roger Dingledine's avatar
Roger Dingledine committed
913
    result = -1;
914
  if (check_nickname_list(options->EntryNodes, "EntryNodes"))
Roger Dingledine's avatar
Roger Dingledine committed
915
    result = -1;
916
  if (check_nickname_list(options->ExcludeNodes, "ExcludeNodes"))
Roger Dingledine's avatar
Roger Dingledine committed
917
    result = -1;
918
  if (check_nickname_list(options->RendNodes, "RendNodes"))
Roger Dingledine's avatar
Roger Dingledine committed
919
    result = -1;
920
  if (check_nickname_list(options->RendNodes, "RendExcludeNodes"))
Roger Dingledine's avatar
Roger Dingledine committed
921
    result = -1;
922
  if (check_nickname_list(options->MyFamily, "MyFamily"))
Roger Dingledine's avatar
Roger Dingledine committed
923
    result = -1;
924
925
  for (cl = options->NodeFamilies; cl; cl = cl->next) {
    if (check_nickname_list(cl->value, "NodeFamily"))
Roger Dingledine's avatar
Roger Dingledine committed
926
      result = -1;
927
928
  }

929
930
  if (!options->RedirectExitList)
    options->RedirectExitList = smartlist_create();
Roger Dingledine's avatar
Roger Dingledine committed
931
/* XXX need to free the old one if it's there, else they just keep piling up */
932
933
  for (cl = options->RedirectExit; cl; cl = cl->next) {
    if (parse_redirect_line(options, cl)<0)
Roger Dingledine's avatar
Roger Dingledine committed
934
      result = -1;
935
936
  }

Nick Mathewson's avatar
Nick Mathewson committed
937
938
939
940
941
942
  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)
Roger Dingledine's avatar
Roger Dingledine committed
943
        result = -1;
Nick Mathewson's avatar
Nick Mathewson committed
944
    }
945
946
  }

Roger Dingledine's avatar
Roger Dingledine committed
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
  return result;
}

/** 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. */
int
getconfig(int argc, char **argv, or_options_t *options)
{
  struct config_line_t *cl;
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
  int using_default_torrc;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
  int previous_orport = -1;

  if (argv) { /* first time we're called. save commandline args */
    backup_argv = argv;
    backup_argc = argc;
  } else { /* we're reloading. need to clean up old options first. */
    argv = backup_argv;
    argc = backup_argc;

    /* record some previous values, so we can fail if they change */
    if (options->PidFile)
      previous_pidfile = tor_strdup(options->PidFile);
    previous_runasdaemon = options->RunAsDaemon;
    previous_orport = options->ORPort;
    free_options(options);
  }
  init_options(options);

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

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

  /* learn config file name, get config lines, assign them */
  fname = NULL;
  using_default_torrc = 1;
  options->command = CMD_RUN_TOR;
  for (i = 1; i < argc; ++i) {
    if (i < argc-1 && !strcmp(argv[i],"-f")) {
      if (fname) {