config.c 33.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"
Roger Dingledine's avatar
Roger Dingledine committed
13

Nick Mathewson's avatar
Nick Mathewson committed
14
15
16
17
18
19
/** Enumeration of types which option values can take */
typedef enum config_type_t {
  CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */
  CONFIG_TYPE_INT, /**< An integer */
  CONFIG_TYPE_DOUBLE, /**< A floating-point value */
  CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */
20
21
  CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and optional
                    * whitespace. */
Nick Mathewson's avatar
Nick Mathewson committed
22
23
  CONFIG_TYPE_LINELIST, /**< Uninterpreted config lines */
} config_type_t;
24

Nick Mathewson's avatar
Nick Mathewson committed
25
/** Largest allowed config line */
26
#define CONFIG_LINE_T_MAXLEN 4096
27
28
29

static FILE *config_open(const unsigned char *filename);
static int config_close(FILE *f);
30
31
32
static struct config_line_t *config_get_commandlines(int argc, char **argv);
static struct config_line_t *config_get_lines(FILE *f);
static void config_free_lines(struct config_line_t *front);
33
static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg);
34
static int config_assign(or_options_t *options, struct config_line_t *list);
35

Nick Mathewson's avatar
Nick Mathewson committed
36
/** Open a configuration file for reading */
37
static FILE *config_open(const unsigned char *filename) {
Roger Dingledine's avatar
Roger Dingledine committed
38
  tor_assert(filename);
39
40
41
42
43
44
45
  if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) {
    /* filename has illegal letters */
    return NULL;
  }
  return fopen(filename, "r");
}

Nick Mathewson's avatar
Nick Mathewson committed
46
/** Close the configuration file */
47
static int config_close(FILE *f) {
Roger Dingledine's avatar
Roger Dingledine committed
48
  tor_assert(f);
49
50
51
  return fclose(f);
}

Nick Mathewson's avatar
Nick Mathewson committed
52
/** Helper: Read a list of configuration options from the command line. */
53
54
55
static struct config_line_t *config_get_commandlines(int argc, char **argv) {
  struct config_line_t *new;
  struct config_line_t *front = NULL;
56
57
58
  char *s;
  int i = 1;

Roger Dingledine's avatar
Roger Dingledine committed
59
  while(i < argc-1) {
60
61
62
63
64
65
    if(!strcmp(argv[i],"-f")) {
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
      i+=2; /* this is the config file option. ignore it. */
      continue;
    }

66
    new = tor_malloc(sizeof(struct config_line_t));
67
68
69
    s = argv[i];
    while(*s == '-')
      s++;
70
71
    new->key = tor_strdup(s);
    new->value = tor_strdup(argv[i+1]);
72
73
74
75
76
77
78
79
80
81

    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
      new->key, new->value);
    new->next = front;
    front = new;
    i += 2;
  }
  return front;
}

Nick Mathewson's avatar
Nick Mathewson committed
82
83
/** Helper: allocate a new configuration option mapping 'key' to 'val',
 * prepend it to 'front', and return the newly allocated config_line_t */
84
85
86
87
88
89
90
91
92
93
94
95
96
static struct config_line_t *
config_line_prepend(struct config_line_t *front,
                    const char *key,
                    const char *val)
{
  struct config_line_t *newline;
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

97
/** Helper: parse the config file and strdup into key/value
Nick Mathewson's avatar
Nick Mathewson committed
98
99
 * strings. Return list, or NULL if parsing the file failed.  Warn and
 * ignore any misformatted lines. */
100
101
102
103
static struct config_line_t *config_get_lines(FILE *f) {

  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
104
105
  int result;
  char *key, *value;
106

107
  while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) {
108
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
109
  }
110
111
  if(result < 0)
    return NULL;
112
  return front;
Roger Dingledine's avatar
Roger Dingledine committed
113
114
}

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

  while(front) {
    tmp = front;
    front = tmp->next;

    free(tmp->key);
    free(tmp->value);
    free(tmp);
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
131
132
133
134
135
/** 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.
 */
136
static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg) {
137
  int i;
138
139
140
141

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

142
143
144
145
146
  if(strcasecmp(c->key,key)) {
    tor_free(c->key);
    c->key = tor_strdup(key);
  }

147
  /* it's a match. cast and assign. */
148
  log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value);
149
150

  switch(type) {
Roger Dingledine's avatar
Roger Dingledine committed
151
    case CONFIG_TYPE_INT:
152
      *(int *)arg = atoi(c->value);
153
      break;
154
155
156
    case CONFIG_TYPE_BOOL:
      i = atoi(c->value);
      if (i != 0 && i != 1) {
Roger Dingledine's avatar
Roger Dingledine committed
157
        log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1", c->key);
158
        return 0;
159
160
161
      }
      *(int *)arg = i;
      break;
162
    case CONFIG_TYPE_STRING:
163
      tor_free(*(char **)arg);
164
      *(char **)arg = tor_strdup(c->value);
165
      break;
166
167
    case CONFIG_TYPE_DOUBLE:
      *(double *)arg = atof(c->value);
168
      break;
169
    case CONFIG_TYPE_CSV:
170
171
      if(*(smartlist_t**)arg == NULL)
        *(smartlist_t**)arg = smartlist_create();
172
      smartlist_split_string(*(smartlist_t**)arg, c->value, ",", 1);
173
      break;
174
175
176
177
178
179
180
    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;
181
182
183
184
  }
  return 1;
}

Nick Mathewson's avatar
Nick Mathewson committed
185
186
/** Iterate through the linked list of options <b>list</b>.
 * For each item, convert as appropriate and assign to <b>options</b>.
187
188
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
189
static int config_assign(or_options_t *options, struct config_line_t *list) {
190
191
192
193
194
195
196

  while(list) {
    if(

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

    /* string options */
197
    config_compare(list, "Address",        CONFIG_TYPE_STRING, &options->Address) ||
198
    config_compare(list, "AllowUnverifiedNodes", CONFIG_TYPE_CSV, &options->AllowUnverifiedNodes) ||
199
    config_compare(list, "AuthoritativeDirectory",CONFIG_TYPE_BOOL, &options->AuthoritativeDir) ||
200

201
202
203
    config_compare(list, "BandwidthRate",  CONFIG_TYPE_INT, &options->BandwidthRate) ||
    config_compare(list, "BandwidthBurst", CONFIG_TYPE_INT, &options->BandwidthBurst) ||

204
    config_compare(list, "ClientOnly",     CONFIG_TYPE_BOOL, &options->ClientOnly) ||
205
    config_compare(list, "ContactInfo",    CONFIG_TYPE_STRING, &options->ContactInfo) ||
206

207
    config_compare(list, "DebugLogFile",   CONFIG_TYPE_STRING, &options->DebugLogFile) ||
208
    config_compare(list, "DataDirectory",  CONFIG_TYPE_STRING, &options->DataDirectory) ||
209
    config_compare(list, "DirPort",        CONFIG_TYPE_INT, &options->DirPort) ||
210
    config_compare(list, "DirBindAddress", CONFIG_TYPE_LINELIST, &options->DirBindAddress) ||
211
212
    config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_INT, &options->DirFetchPostPeriod) ||

213
214
    config_compare(list, "ExitNodes",      CONFIG_TYPE_STRING, &options->ExitNodes) ||
    config_compare(list, "EntryNodes",     CONFIG_TYPE_STRING, &options->EntryNodes) ||
215
216
    config_compare(list, "StrictExitNodes", CONFIG_TYPE_BOOL, &options->StrictExitNodes) ||
    config_compare(list, "StrictEntryNodes", CONFIG_TYPE_BOOL, &options->StrictEntryNodes) ||
217
    config_compare(list, "ExitPolicy",     CONFIG_TYPE_LINELIST, &options->ExitPolicy) ||
218
    config_compare(list, "ExcludeNodes",   CONFIG_TYPE_STRING, &options->ExcludeNodes) ||
219

220
    config_compare(list, "FascistFirewall",CONFIG_TYPE_BOOL, &options->FascistFirewall) ||
221
    config_compare(list, "FirewallPorts",CONFIG_TYPE_CSV, &options->FirewallPorts) ||
222

223
    config_compare(list, "Group",          CONFIG_TYPE_STRING, &options->Group) ||
224

225
226
227
228
229
    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)||

230
231
232
233
    config_compare(list, "IgnoreVersion",  CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||

    config_compare(list, "KeepalivePeriod",CONFIG_TYPE_INT, &options->KeepalivePeriod) ||

234
235
    config_compare(list, "LogLevel",       CONFIG_TYPE_LINELIST, &options->LogOptions) ||
    config_compare(list, "LogFile",        CONFIG_TYPE_LINELIST, &options->LogOptions) ||
236
237
238
    config_compare(list, "LinkPadding",    CONFIG_TYPE_BOOL, &options->LinkPadding) ||

    config_compare(list, "MaxConn",        CONFIG_TYPE_INT, &options->MaxConn) ||
239
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
240
241

    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
242
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
243
    config_compare(list, "NumCpus",        CONFIG_TYPE_INT, &options->NumCpus) ||
244

245
    config_compare(list, "ORPort",         CONFIG_TYPE_INT, &options->ORPort) ||
246
    config_compare(list, "ORBindAddress",  CONFIG_TYPE_LINELIST, &options->ORBindAddress) ||
247
    config_compare(list, "OutboundBindAddress",CONFIG_TYPE_STRING, &options->OutboundBindAddress) ||
248

249
    config_compare(list, "PidFile",        CONFIG_TYPE_STRING, &options->PidFile) ||
250
    config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
251
252
253

    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
    config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
254
    config_compare(list, "RunTesting",     CONFIG_TYPE_BOOL, &options->RunTesting) ||
255
    config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) ||
256
257
    config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
    config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
258

259
    config_compare(list, "SocksPort",      CONFIG_TYPE_INT, &options->SocksPort) ||
260
261
    config_compare(list, "SocksBindAddress",CONFIG_TYPE_LINELIST,&options->SocksBindAddress) ||
    config_compare(list, "SocksPolicy",     CONFIG_TYPE_LINELIST,&options->SocksPolicy) ||
262
263
264

    config_compare(list, "TrafficShaping", CONFIG_TYPE_BOOL, &options->TrafficShaping) ||

265
266
267
    config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User)


268
269
270
    ) {
      /* then we're ok. it matched something. */
    } else {
271
272
      log_fn(LOG_WARN,"Unknown keyword '%s'. Failing.",list->key);
      return -1;
273
274
275
    }

    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
276
  }
277
  return 0;
278
279
}

280
const char default_dirservers_string[] =
281
282
283
284
"router moria1 18.244.0.188 9001 9021 9031\n"
"platform Tor 0.0.6rc1 on Linux moria.mit.edu i686\n"
"published 2004-04-25 21:54:28\n"
"bandwidth 800000 10000000\n"
285
286
287
288
289
290
291
292
293
294
295
296
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBANoIvHieyHUTzIacbnWOnyTyzGrLOdXqbcjz2GGMxyHEd5K1bO1ZBNHP\n"
"9i5qLQpN5viFk2K2rEGuG8tFgDEzSWZEtBqv3NVfUdiumdERWMBwlaQ0MVK4C+jf\n"
"y5gZ8KI3o9ZictgPS1AQF+Kk932/vIHTuRIUKb4ILTnQilNvID0NAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAMHa0ZC/jo2Q2DrwKYF/6ZbmZ27PFYG91u4gUzzmZ/VXLpZ8wNzEV3oW\n"
"nt+I61048fBiC1frT1/DZ351n2bLSk9zJbB6jyGZJn0380FPRX3+cXyXS0Gq8Ril\n"
"xkhMQf5XuNFUb8UmYPSOH4WErjvYjKvU+gfjbK/82Jo9SuHpYz+BAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
"reject 0.0.0.0/255.0.0.0:*\n"
"reject 169.254.0.0/255.255.0.0:*\n"
"reject 127.0.0.0/255.0.0.0:*\n"
"reject 192.168.0.0/255.255.0.0:*\n"
"reject 10.0.0.0/255.0.0.0:*\n"
"reject 172.16.0.0/255.240.0.0:*\n"
"accept *:20-22\n"
"accept *:53\n"
"accept *:79-80\n"
"accept *:110\n"
"accept *:143\n"
"accept *:443\n"
"accept *:873\n"
"accept *:993\n"
"accept *:995\n"
"accept *:1024-65535\n"
"reject *:*\n"
314
315
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
316
317
318
"o1eAoRHDAEAXsnh5wN++vIwrupd+DbAJ2p3wxHDrmqxTpygzxxCnyQyhMfX03ua2\n"
"4iplyNlwyFwzWcw0sk31otlO2HBYXT1V9G0YxGtKMOeOBMHjfGbUjGvEALHzWi4z\n"
"8DXGJp13zgnUyP4ZA6xaGROwcT6oB5e7UlztvvpGxTg=\n"
319
320
"-----END SIGNATURE-----\n"
"\n"
321
322
323
324
"router moria2 18.244.0.188 9002 9022 9032\n"
"platform Tor 0.0.6rc1 on Linux moria.mit.edu i686\n"
"published 2004-04-25 21:54:30\n"
"bandwidth 800000 10000000\n"
325
326
327
328
329
330
331
332
333
334
335
336
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAM4Cc/npgYC54XrYLC+grVxJp7PDmNO2DRRJOxKttBBtvLpnR1UaueTi\n"
"kyknT5kmlx+ihgZF/jmye//2dDUp2+kK/kSkpRV4xnDLXZmed+sNSQxqmm9TtZQ9\n"
"/hjpxhp5J9HmUTYhntBs+4E4CUKokmrI6oRLoln4SA39AX9QLPcnAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAOcrht/y5rkaahfX7sMe2qnpqoPibsjTSJaDvsUtaNP/Bq0MgNDGOR48\n"
"rtwfqTRff275Edkp/UYw3G3vSgKCJr76/bqOHCmkiZrnPV1zxNfrK18gNw2Cxre0\n"
"nTA+fD8JQqpPtb8b0SnG9kwy75eS//sRu7TErie2PzGMxrf9LH0LAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
"reject 0.0.0.0/255.0.0.0:*\n"
"reject 169.254.0.0/255.255.0.0:*\n"
"reject 127.0.0.0/255.0.0.0:*\n"
"reject 192.168.0.0/255.255.0.0:*\n"
"reject 10.0.0.0/255.0.0.0:*\n"
"reject 172.16.0.0/255.240.0.0:*\n"
"accept *:20-22\n"
"accept *:53\n"
"accept *:79-80\n"
"accept *:110\n"
"accept *:143\n"
"accept *:443\n"
"accept *:873\n"
"accept *:993\n"
"accept *:995\n"
"accept *:1024-65535\n"
"reject *:*\n"
354
355
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
356
357
358
"RKROLwP1ExjTZeg6wuN0pzYqed9IJUd5lAe9hp4ritbnmJAgS6qfww6jgx61CfUR\n"
"6SElhOLE7Q77jAdoL45Ji5pn/Y+Q+E+5lJm1E/ed9ha+YsOPaOc7z6GQ7E4mihCL\n"
"gI1vsw92+P1Ty4RHj6fyD9DhbV19nh2Qs+pvGJOS2FY=\n"
359
360
"-----END SIGNATURE-----\n"
"\n"
361
362
363
364
"router tor26 62.116.124.106 9001 9050 9030\n"
"platform Tor 0.0.6 on Linux seppia i686\n"
"published 2004-05-06 21:33:23\n"
"bandwidth 500000 10000000\n"
365
366
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
367
368
369
"MIGJAoGBAMEHdDnpj3ik1AF1xe/VqjoguH2DbANifYqXXfempu0fS+tU9FGo6dU/\n"
"fnVHAZwL9Ek9k2rMzumShi1RduK9p035R/Gk+PBBcLfvwYJ/Nat+ZO/L8jn/3bZe\n"
"ieQd9CKj2LjNGKpRNry37vkwMGIOIlegwK+2us8aXJ7sIvlNts0TAgMBAAE=\n"
370
371
372
"-----END RSA PUBLIC KEY-----\n"
"signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
373
374
375
"MIGJAoGBAMQgV2gXLbXgesWgeAsj8P1Uvm/zibrFXqwDq27lLKNgWGYGX2ax3LyT\n"
"3nzI1Y5oLs4kPKTsMM5ft9aokwf417lKoCRlZc9ptfRbgxDx90c9GtWVmkrmDvCK\n"
"ae59TMoXIiGfZiwWT6KKq5Zm9/Fu2Il3B2vHGkKJYKixmiBJRKp/AgMBAAE=\n"
376
"-----END RSA PUBLIC KEY-----\n"
377
378
379
"accept 62.245.184.24:25\n"
"accept 62.116.124.106:6666-6670\n"
"accept *:48099\n"
380
"reject *:*\n"
381
382
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
383
384
385
"qh/xRoqfLNFzPaB8VdpbdMAwRyuk5qjx4LeLVQ2pDwTZ55PqmG99+VKUNte2WTTD\n"
"7dZEA7um2rueohGe4nYmvbhJWr20/I0ZxmWDRDvFy0b5nwzDMGvLvDw95Zu/XJQ2\n"
"md32NE3y9VZCfbCN+GlvETX3fdR3Svzcm8Kzesg2/s4=\n"
386
387
388
"-----END SIGNATURE-----\n"
;

389
int config_assign_default_dirservers(void) {
390
  if(router_load_routerlist_from_string(default_dirservers_string, 1) < 0) {
391
392
393
394
395
396
    log_fn(LOG_WARN,"Bug: the default dirservers internal string is corrupt.");
    return -1;
  }
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
397
398
/** Set <b>options</b> to a reasonable default.
 *
Roger Dingledine's avatar
Roger Dingledine committed
399
 * Call this function when we can't find any torrc config file.
400
 */
Roger Dingledine's avatar
Roger Dingledine committed
401
static int config_assign_defaults(or_options_t *options) {
402
403
404
405

  /* set them up as a client only */
  options->SocksPort = 9050;

406
407
408
409
  options->AllowUnverifiedNodes = smartlist_create();
  smartlist_add(options->AllowUnverifiedNodes, "middle");
  smartlist_add(options->AllowUnverifiedNodes, "rendezvous");

Roger Dingledine's avatar
Roger Dingledine committed
410
411
412
  config_free_lines(options->ExitPolicy);
  options->ExitPolicy = config_line_prepend(NULL, "ExitPolicy", "reject *:*");

413
  /* plus give them a dirservers file */
414
  if(config_assign_default_dirservers() < 0)
415
416
417
418
    return -1;
  return 0;
}

Nick Mathewson's avatar
Nick Mathewson committed
419
/** Print a usage message for tor. */
420
static void print_usage(void) {
421
  printf("tor -f <torrc> [args]\n"
422
         "See man page for more options. This -h is probably obsolete.\n\n"
423
         "-b <bandwidth>\t\tbytes/second rate limiting\n"
424
         "-d <file>\t\tDebug file\n"
425
//         "-m <max>\t\tMax number of connections\n"
426
427
428
429
         "-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"
430
         "-s <IP>\t\t\tPort to bind to for Socks\n"
431
         );
432
433
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
434
435
436
         "-o <port>\t\tOR port to bind to\n"
         "-p <file>\t\tPID file\n"
         );
437
438
}

Nick Mathewson's avatar
Nick Mathewson committed
439
440
441
/**
 * Adjust <b>options</b> to contain a reasonable value for Address.
 */
442
int resolve_my_address(const char *address, uint32_t *addr) {
443
444
  struct in_addr in;
  struct hostent *rent;
445
  char hostname[256];
446
  int explicit_ip=1;
447

448
449
450
451
452
  tor_assert(addr);

  if(address) {
    strlcpy(hostname,address,sizeof(hostname));
  } else { /* then we need to guess our address */
453
    explicit_ip = 0; /* it's implicit */
454

455
    if(gethostname(hostname,sizeof(hostname)) < 0) {
456
457
458
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
459
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",hostname);
460
461
  }

462
  /* now we know hostname. resolve it and keep only the IP */
463

464
  if(tor_inet_aton(hostname, &in) == 0) {
465
466
    /* then we have to resolve it */
    explicit_ip = 0;
467
    rent = (struct hostent *)gethostbyname(hostname);
468
    if (!rent) {
469
      log_fn(LOG_WARN,"Could not resolve local Address %s. Failing.", hostname);
470
471
      return -1;
    }
Roger Dingledine's avatar
Roger Dingledine committed
472
    tor_assert(rent->h_length == 4);
473
    memcpy(&in.s_addr, rent->h_addr,rent->h_length);
474
  }
475
  if(!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
476
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
477
           "Please set the Address config option to be the IP you want to use.",
478
           hostname, inet_ntoa(in));
479
480
    return -1;
  }
481
482
  log_fn(LOG_DEBUG,"Resolved Address to %s.", inet_ntoa(in));
  *addr = ntohl(in.s_addr);
483
484
485
  return 0;
}

486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
static char *get_default_nickname(void)
{
  char localhostname[256];
  char *cp, *out, *outp;

  if(gethostname(localhostname,sizeof(localhostname)) < 0) {
    log_fn(LOG_WARN,"Error obtaining local hostname");
    return NULL;
  }
  /* Put it in lowercase; stop at the first dot. */
  for(cp = localhostname; *cp; ++cp) {
    if (*cp == '.') {
      *cp = '\0';
      break;
    }
    *cp = tolower(*cp);
  }
  /* Strip invalid characters. */
  cp = localhostname;
  out = outp = tor_malloc(strlen(localhostname)+1);
  while (*cp) {
    if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp))
      *outp++ = *cp++;
    else
      cp++;
  }
  *outp = '\0';
  /* Enforce length. */
  if (strlen(out) > MAX_NICKNAME_LEN)
    out[MAX_NICKNAME_LEN]='\0';

  return out;
}

Nick Mathewson's avatar
Nick Mathewson committed
520
/** Release storage held by <b>options</b> */
521
static void free_options(or_options_t *options) {
522
  config_free_lines(options->LogOptions);
523
  tor_free(options->ContactInfo);
524
525
526
527
528
529
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->RouterFile);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
530
531
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
532
  tor_free(options->ExcludeNodes);
533
534
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
535
  tor_free(options->OutboundBindAddress);
536
  tor_free(options->RecommendedVersions);
537
538
  tor_free(options->User);
  tor_free(options->Group);
539
  config_free_lines(options->RendConfigLines);
540
541
542
543
544
  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);
545
546
  SMARTLIST_FOREACH(options->FirewallPorts, char *, cp, tor_free(cp));
  smartlist_free(options->FirewallPorts);
547
}
548

Nick Mathewson's avatar
Nick Mathewson committed
549
/** Set <b>options</b> to hold reasonable defaults for most options. */
550
static void init_options(or_options_t *options) {
551
/* give reasonable values for each option. Defaults to zero. */
552
  memset(options,0,sizeof(or_options_t));
553
  options->LogOptions = NULL;
554
555
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
556
  options->StrictEntryNodes = options->StrictExitNodes = 0;
557
  options->ExcludeNodes = tor_strdup("");
558
559
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
560
561
562
563
564
  options->ExitPolicy = NULL;
  options->SocksPolicy = NULL;
  options->SocksBindAddress = NULL;
  options->ORBindAddress = NULL;
  options->DirBindAddress = NULL;
565
  options->OutboundBindAddress = NULL;
566
  options->RecommendedVersions = NULL;
567
  options->PidFile = NULL; // tor_strdup("tor.pid");
568
  options->DataDirectory = NULL;
569
  options->PathlenCoinWeight = 0.3;
570
  options->MaxConn = 900;
571
  options->DirFetchPostPeriod = 600;
572
  options->KeepalivePeriod = 300;
573
  options->MaxOnionsPending = 100;
574
  options->NewCircuitPeriod = 30; /* twice a minute */
575
576
  options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurst = 10000000; /* max burst on the token bucket */
577
  options->NumCpus = 1;
578
  options->RendConfigLines = NULL;
579
  options->FirewallPorts = NULL;
580
581
}

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

  if(first_load) { /* first time we're called. save commandline args */
    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 */
608
609
    if(options->PidFile)
      previous_pidfile = tor_strdup(options->PidFile);
610
    previous_runasdaemon = options->RunAsDaemon;
611
    previous_orport = options->ORPort;
612
613
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
614
  init_options(options);
615

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

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

626
627
628
629
630
631
/* learn config file name, get config lines, assign them */
  i = 1;
  while(i < argc-1 && strcmp(argv[i],"-f")) {
    i++;
  }
  if(i < argc-1) { /* we found one */
632
    fname = tor_strdup(argv[i+1]);
633
    using_default_torrc = 0;
634
635
636
637
638
639
  } else if (file_status(CONFDIR "/torrc")==FN_FILE) {
    /* didn't find one, try CONFDIR */
    fname = tor_strdup(CONFDIR "/torrc");
    using_default_torrc = 1;
  } else {
    char *fn = expand_filename("~/.torrc");
640
    if (fn && file_status(fn)==FN_FILE) {
641
642
643
644
645
      fname = fn;
    } else {
      tor_free(fn);
      fname = tor_strdup(CONFDIR "/torrc");
    }
646
    using_default_torrc = 1;
647
648
649
650
  }
  log(LOG_DEBUG,"Opening config file '%s'",fname);

  cf = config_open(fname);
651
  if(!cf) {
652
    if(using_default_torrc == 1) {
653
      log(LOG_NOTICE, "Configuration file '%s' not present, using reasonable defaults.",fname);
654
655
      tor_free(fname);
      if(config_assign_defaults(options) < 0) {
656
        return -1;
657
      }
658
659
    } else {
      log(LOG_WARN, "Unable to open configuration file '%s'.",fname);
660
      tor_free(fname);
661
662
663
      return -1;
    }
  } else { /* it opened successfully. use it. */
664
    tor_free(fname);
665
666
    cl = config_get_lines(cf);
    if(!cl) return -1;
667
668
    if(config_assign(options,cl) < 0)
      return -1;
669
670
    config_free_lines(cl);
    config_close(cf);
671
  }
672

673
674
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
675
676
  if(config_assign(options,cl) < 0)
    return -1;
677
678
679
680
  config_free_lines(cl);

/* Validate options */

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

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

694
  if(previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
695
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
696
697
698
    return -1;
  }

699
  if(options->ORPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
700
    log(LOG_WARN,"ORPort option can't be negative.");
701
702
703
    result = -1;
  }

704
  if (options->Nickname == NULL) {
705
706
707
708
709
    if(server_mode()) {
      if (!(options->Nickname = get_default_nickname()))
        return -1;
      log_fn(LOG_NOTICE, "Choosing default nickname %s", options->Nickname);
    }
710
711
712
713
714
715
716
717
718
719
  } else {
    if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
        strlen(options->Nickname)) {
      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
      result = -1;
    }
    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;
720
    }
721
722
  }

723
724
725
726
  if(server_mode()) {
    /* confirm that our address isn't broken, so we can complain now */
    uint32_t tmp;
    if(resolve_my_address(options->Address, &tmp) < 0)
727
      result = -1;
728
729
  }

730
731
  if(options->SocksPort < 0) {
    log(LOG_WARN,"SocksPort option can't be negative.");
732
733
734
    result = -1;
  }

735
736
737
  if(options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN,"SocksPort and ORPort are both undefined? Quitting.");
    result = -1;
Roger Dingledine's avatar
Roger Dingledine committed
738
  }
739

740
  if(options->DirPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
741
    log(LOG_WARN,"DirPort option can't be negative.");
742
743
744
    result = -1;
  }

745
746
747
748
749
750
751
752
  if(options->StrictExitNodes && !strlen(options->ExitNodes)) {
    log(LOG_WARN,"StrictExitNodes set, but no ExitNodes listed.");
  }

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

Roger Dingledine's avatar
Roger Dingledine committed
753
  if(options->AuthoritativeDir && options->RecommendedVersions == NULL) {
754
755
756
757
    log(LOG_WARN,"Directory servers must configure RecommendedVersions.");
    result = -1;
  }

758
759
760
761
762
  if(options->AuthoritativeDir && !options->DirPort) {
    log(LOG_WARN,"Running as authoritative directory, but no DirPort set.");
    result = -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
763
764
765
766
  if(options->AuthoritativeDir && !options->ORPort) {
    log(LOG_WARN,"Running as authoritative directory, but no ORPort set.");
    result = -1;
  }
767

Roger Dingledine's avatar
Roger Dingledine committed
768
769
770
771
  if(options->AuthoritativeDir && options->ClientOnly) {
    log(LOG_WARN,"Running as authoritative directory, but ClientOnly also set.");
    result = -1;
  }
772

773
774
775
776
777
778
779
  if(options->FascistFirewall && !options->FirewallPorts) {
    options->FirewallPorts = smartlist_create();
    smartlist_add(options->FirewallPorts, "80");
    smartlist_add(options->FirewallPorts, "443");
  }
  if(options->FirewallPorts) {
    SMARTLIST_FOREACH(options->FirewallPorts, const char *, cp,
780
    { i = atoi(cp);
781
      if (i < 1 || i > 65535) {
782
783
784
785
786
787
788
789
790
791
792
793
794
795
        log(LOG_WARN, "Port '%s' out of range in FirewallPorts", cp);
        result=-1;
      }
    });
  }
  options->_AllowUnverified = 0;
  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;
796
797
798
799
      else if (!strcasecmp(cp, "introduction"))
        options->_AllowUnverified |= ALLOW_UNVERIFIED_INTRODUCTION;
      else if (!strcasecmp(cp, "rendezvous"))
        options->_AllowUnverified |= ALLOW_UNVERIFIED_RENDEZVOUS;
800
801
802
      else {
        log(LOG_WARN, "Unrecognized value '%s' in AllowUnverifiedNodes",
            cp);
803
804
805
806
807
        result=-1;
      }
    });
  }

808
  if(options->SocksPort >= 1 &&
809
810
     (options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
    log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
811
812
813
    result = -1;
  }

814
  if(options->MaxConn < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
815
    log(LOG_WARN,"MaxConn option must be a non-zero positive integer.");
816
817
818
819
    result = -1;
  }

  if(options->MaxConn >= MAXCONNECTIONS) {
Roger Dingledine's avatar
Roger Dingledine committed
820
    log(LOG_WARN,"MaxConn option must be less than %d.", MAXCONNECTIONS);
821
822
823
    result = -1;
  }

824
  if(options->DirFetchPostPeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
825
    log(LOG_WARN,"DirFetchPostPeriod option must be positive.");
826
827
    result = -1;
  }
828
829
830
831
  if(options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME/2) {
    log(LOG_WARN,"DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME/2;
  }
832
833

  if(options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
834
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
835
836
837
    result = -1;
  }

838
839
840
841
  /* XXX look at the various nicknamelists and make sure they're
   * valid and don't have hostnames that are too long.
   */

842
843
844
845
  if (rend_config_services(options) < 0) {
    result = -1;
  }

846
  return result;
847
848
}

849
static int add_single_log(struct config_line_t *level_opt,
850
851
852
853
854
855
856
857
858
859
860
861
                           struct config_line_t *file_opt,
                           int isDaemon)
{
  int levelMin=-1, levelMax=-1;
  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);
      if (levelMin<0) {
862
863
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", tmp_sev);
        return -1;
864
865
866
867
      }
      tor_free(tmp_sev);
      levelMax = parse_log_level(cp+1);
      if (levelMax<0) {
868
869
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", cp+1);
        return -1;
870
871
872
873
      }
    } else {
      levelMin = parse_log_level(level_opt->value);
      if (levelMin<0) {
874
875
876
        log_fn(LOG_WARN, "Unrecognized log severity '%s': must be one of err|warn|notice|info|debug", level_opt->value);
        return -1;

877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
      }
    }
  }
  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));
892
      return -1;
893
894
895
896
    }
    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
           file_opt->value);
  } else if (!isDaemon) {
897
    add_stream_log(levelMin, levelMax, "<stdout>", stdout);
898
    close_temp_logs();
899
  }
900
  return 0;
901
902
903
904
905
}

/**
 * Initialize the logs based on the configuration file.
 */
906
int config_init_logs(or_options_t *options)
907
908
909
910
911
{
  /* The order of options is:  Level? (File Level?)+
   */
  struct config_line_t *opt = options->LogOptions;

912
913
914
915
  /* 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
916
917
    /* don't return yet, in case we want to do a debuglogfile below */
  }
918

919
920
921
  /* Special case for if first option is LogLevel. */
  if (opt && !strcasecmp(opt->key, "LogLevel")) {
    if (opt->next && !strcasecmp(opt->next->key, "LogFile")) {
922
923
      if (add_single_log(opt, opt->next, options->RunAsDaemon)<0)
        return -1;
924
925
      opt = opt->next->next;
    } else if (!opt->next) {
926
927
      if (add_single_log(opt, NULL, options->RunAsDaemon)<0)
        return -1;
928
      opt = opt->next;
929
930
    } else {
      ; /* give warning below */
931
932
933
934
935
936
937
938
    }
  }

  while (opt) {
    if (!strcasecmp(opt->key, "LogLevel")) {
      log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile");
      opt = opt->next;
    } else {
939
      tor_assert(!strcasecmp(opt->key, "LogFile"));
940
941
      if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) {
        /* LogFile followed by LogLevel */
942
943
        if (add_single_log(opt->next, opt, options->RunAsDaemon)<0)
          return -1;
944
945
946
        opt = opt->next->next;
      } else {
        /* LogFile followed by LogFile or end of list. */
947
948
        if (add_single_log(NULL, opt, options->RunAsDaemon)<0)
          return -1;
949
950
951
952
953
954
955
        opt = opt->next;
      }
    }
  }

  if (options->DebugLogFile) {
    log_fn(LOG_WARN, "DebugLogFile is deprecated; use LogFile and LogLevel instead");
956
957
    if (add_file_log(LOG_DEBUG, LOG_ERR, options->DebugLogFile)<0)
      return -1;
958
  }
959
  return 0;
960
961
}

962
/** XXX008 DOCDOC */
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
void
config_parse_exit_policy(struct config_line_t *cfg,
                         struct exit_policy_t **dest)
{
  struct exit_policy_t **nextp;
  char *e, *s;
  int last=0;
  char line[1024];

  if (!cfg)
    return;
  nextp = dest;
  while (*nextp)
    nextp = &((*nextp)->next);

  for (; cfg; cfg = cfg->next) {
    s = cfg->value;
    for (;;) {
      e = strchr(s,',');
      if(!e) {
        last = 1;
        strncpy(line,s,1023);
      } else {
        memcpy(line,s, ((e-s)<1023)?(e-s):1023);
      line[e-s] = 0;
      }
      line[1023]=0;
      log_fn(LOG_DEBUG,"Adding new entry '%s'",line);
      *nextp = router_parse_exit_policy_from_string(line);
      if(*nextp) {
        nextp = &((*nextp)->next);
      } else {
        log_fn(LOG_WARN,"Malformed exit policy %s; skipping.", line);
      }
      if (last)
        break;
      s = e+1;
    }
For faster browsing, not all history is shown. View entire blame