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

Roger Dingledine's avatar
Roger Dingledine committed
5
#include "or.h"
Roger Dingledine's avatar
Roger Dingledine committed
6

7
8
9
10
11
12
13
/* enumeration of types which option values can take */
#define CONFIG_TYPE_STRING  0
#define CONFIG_TYPE_CHAR    1
#define CONFIG_TYPE_INT     2
#define CONFIG_TYPE_LONG    3
#define CONFIG_TYPE_DOUBLE  4
#define CONFIG_TYPE_BOOL    5
14
#define CONFIG_TYPE_LINELIST 6
15

16
#define CONFIG_LINE_T_MAXLEN 4096
17
18
19

static FILE *config_open(const unsigned char *filename);
static int config_close(FILE *f);
20
21
22
23
24
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);
static int config_compare(struct config_line_t *c, char *key, int type, void *arg);
static int config_assign(or_options_t *options, struct config_line_t *list);
25

26
/* open configuration file for reading */
27
static FILE *config_open(const unsigned char *filename) {
28
29
30
31
32
33
34
35
36
  assert(filename);
  if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(filename)) {
    /* filename has illegal letters */
    return NULL;
  }
  return fopen(filename, "r");
}

/* close configuration file */
37
static int config_close(FILE *f) {
38
39
40
41
  assert(f);
  return fclose(f);
}

42
43
44
static struct config_line_t *config_get_commandlines(int argc, char **argv) {
  struct config_line_t *new;
  struct config_line_t *front = NULL;
45
46
47
  char *s;
  int i = 1;

Roger Dingledine's avatar
Roger Dingledine committed
48
  while(i < argc-1) {
49
50
51
52
53
54
    if(!strcmp(argv[i],"-f")) {
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
      i+=2; /* this is the config file option. ignore it. */
      continue;
    }

55
    new = tor_malloc(sizeof(struct config_line_t));
56
57
58
    s = argv[i];
    while(*s == '-')
      s++;
59
60
    new->key = tor_strdup(s);
    new->value = tor_strdup(argv[i+1]);
61
62
63
64
65
66
67
68
69
70

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

71
72
73
74
75
76
77
78
79
80
81
82
83
static struct config_line_t *
config_line_prepend(struct config_line_t *front,
                    const char *key,
                    const char *val)
{
  struct config_line_t *newline;
  newline = tor_malloc(sizeof(struct config_line_t));
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
  newline->next = front;
  return newline;
}

84
85
/* parse the config file and strdup into key/value strings. Return list,
 * or NULL if parsing the file failed.
86
 * Warn and ignore mangled lines. */
87
88
89
90
static struct config_line_t *config_get_lines(FILE *f) {

  struct config_line_t *front = NULL;
  char line[CONFIG_LINE_T_MAXLEN];
91
92
  int result;
  char *key, *value;
93

94
  while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) {
95
    front = config_line_prepend(front, key, value);
Roger Dingledine's avatar
Roger Dingledine committed
96
  }
97
98
  if(result < 0)
    return NULL;
99
  return front;
Roger Dingledine's avatar
Roger Dingledine committed
100
101
}

102
103
static void config_free_lines(struct config_line_t *front) {
  struct config_line_t *tmp;
104
105
106
107
108
109
110
111
112
113
114

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

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

115
static int config_compare(struct config_line_t *c, char *key, int type, void *arg) {
116
  int i;
117
118
119
120
121

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

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

  switch(type) {
Roger Dingledine's avatar
Roger Dingledine committed
125
    case CONFIG_TYPE_INT:
126
      *(int *)arg = atoi(c->value);
127
      break;
128
129
130
    case CONFIG_TYPE_BOOL:
      i = atoi(c->value);
      if (i != 0 && i != 1) {
Roger Dingledine's avatar
Roger Dingledine committed
131
        log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1", c->key);
132
        return 0;
133
134
135
      }
      *(int *)arg = i;
      break;
136
    case CONFIG_TYPE_STRING:
137
      tor_free(*(char **)arg);
138
      *(char **)arg = tor_strdup(c->value);
139
      break;
140
141
    case CONFIG_TYPE_DOUBLE:
      *(double *)arg = atof(c->value);
142
      break;
143
144
145
146
147
148
149
    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;
150
151
152
153
  }
  return 1;
}

154
155
156
157
/* Iterate through list.
 * For each item, convert as appropriate and assign to 'options'.
 * If an item is unrecognized, return -1 immediately,
 * else return 0 for success. */
158
static int config_assign(or_options_t *options, struct config_line_t *list) {
159
160
161
162
163
164
165

  while(list) {
    if(

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

    /* string options */
166
167
    config_compare(list, "Address",        CONFIG_TYPE_STRING, &options->Address) ||

168
169
170
    config_compare(list, "BandwidthRate",  CONFIG_TYPE_INT, &options->BandwidthRate) ||
    config_compare(list, "BandwidthBurst", CONFIG_TYPE_INT, &options->BandwidthBurst) ||

171
    config_compare(list, "DebugLogFile",   CONFIG_TYPE_STRING, &options->DebugLogFile) ||
172
    config_compare(list, "DataDirectory",  CONFIG_TYPE_STRING, &options->DataDirectory) ||
173
174
175
176
    config_compare(list, "DirPort",        CONFIG_TYPE_INT, &options->DirPort) ||
    config_compare(list, "DirBindAddress", CONFIG_TYPE_STRING, &options->DirBindAddress) ||
    config_compare(list, "DirFetchPostPeriod",CONFIG_TYPE_INT, &options->DirFetchPostPeriod) ||

177
178
    config_compare(list, "ExitNodes",      CONFIG_TYPE_STRING, &options->ExitNodes) ||
    config_compare(list, "EntryNodes",     CONFIG_TYPE_STRING, &options->EntryNodes) ||
179
    config_compare(list, "ExitPolicy",     CONFIG_TYPE_STRING, &options->ExitPolicy) ||
180
    config_compare(list, "ExcludeNodes",   CONFIG_TYPE_STRING, &options->ExcludeNodes) ||
181

182
    config_compare(list, "Group",          CONFIG_TYPE_STRING, &options->Group) ||
183

184
185
186
187
188
189
190
191
192
    config_compare(list, "IgnoreVersion",  CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||

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

    config_compare(list, "LogLevel",       CONFIG_TYPE_STRING, &options->LogLevel) ||
    config_compare(list, "LogFile",        CONFIG_TYPE_STRING, &options->LogFile) ||
    config_compare(list, "LinkPadding",    CONFIG_TYPE_BOOL, &options->LinkPadding) ||

    config_compare(list, "MaxConn",        CONFIG_TYPE_INT, &options->MaxConn) ||
193
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
194
195

    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
196
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
197
    config_compare(list, "NumCpus",        CONFIG_TYPE_INT, &options->NumCpus) ||
198

199
200
    config_compare(list, "ORPort",         CONFIG_TYPE_INT, &options->ORPort) ||
    config_compare(list, "ORBindAddress",  CONFIG_TYPE_STRING, &options->ORBindAddress) ||
201

202
    config_compare(list, "PidFile",        CONFIG_TYPE_STRING, &options->PidFile) ||
203
    config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
204
205
206

    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
    config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
207
    config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) ||
208
209
    config_compare(list, "RendNodes",      CONFIG_TYPE_STRING, &options->RendNodes) ||
    config_compare(list, "RendExcludeNodes",CONFIG_TYPE_STRING, &options->RendExcludeNodes) ||
210

211
212
213
214
215
    config_compare(list, "SocksPort",      CONFIG_TYPE_INT, &options->SocksPort) ||
    config_compare(list, "SocksBindAddress",CONFIG_TYPE_STRING,&options->SocksBindAddress) ||

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

216
    config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User) ||
217
    config_compare(list, "RunTesting",     CONFIG_TYPE_BOOL, &options->RunTesting) ||
218
219
220
221
    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)
222
223
224
    ) {
      /* then we're ok. it matched something. */
    } else {
225
226
      log_fn(LOG_WARN,"Unknown keyword '%s'. Failing.",list->key);
      return -1;
227
228
229
    }

    list = list->next;
Roger Dingledine's avatar
Roger Dingledine committed
230
  }
231
  return 0;
232
233
}

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/* XXX are there any other specifiers we want to give so making
 * a several-thousand-byte string is less painful? */
const char default_dirservers_string[] =
"router moria1 moria.mit.edu 9001 9021 9031 800000\n"
"platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686\n"
"published 2003-09-30 23:14:08\n"
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBANoIvHieyHUTzIacbnWOnyTyzGrLOdXqbcjz2GGMxyHEd5K1bO1ZBNHP\n"
"9i5qLQpN5viFk2K2rEGuG8tFgDEzSWZEtBqv3NVfUdiumdERWMBwlaQ0MVK4C+jf\n"
"y5gZ8KI3o9ZictgPS1AQF+Kk932/vIHTuRIUKb4ILTnQilNvID0NAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"link-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAPt97bGDd9siVjPd7Xuq2s+amMEOLIj9961aSdP6/OT+BS1Q4TX2dNOX\n"
"ZNAl63Z2fQISsR81+nfoqRLYCKxhajsD7LRvRTaRwUrWemVqFevmZ4nJrHw6FoU3\n"
"xNUIHRMA8X2DZ+l5qgnWZb7JU50ohhX5OpMSyysXnik51J8hD5mBAgMBAAE=\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"
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
"Td3zb5d6uxO8oYGlmEHGzIdLuVm9s1Afqtm29JvRnnviQ36j6FZPlzPUaMVOUayn\n"
"Wtz/CbaMj7mHSufpQ68wCLb1lQrtQkn7MkAWcQPIvZjpYh3UrcWrpfm7f/D+nKeN\n"
"Z7UovF36xhCacjATNHhQNHHZHH6yONwN+Rf/N4kyPHw=\n"
"-----END SIGNATURE-----\n"
"\n"
"router moria2 moria.mit.edu 9002 9022 9032 800000\n"
"platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686\n"
"published 2003-09-30 23:14:05\n"
"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"
"link-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAN7JVeCIJ7+0ZJew5ScOU58rTUqjGt1Z1Rkursc7WabEb8jno45VZwIs\n"
"dkjnl31i36KHyyS7kQdHgkvG5EiyZiRipFAcoTaYv3Gvf1No9cXL6IhT3y/37dJ/\n"
"kFPEMb/G2wdkJCC+D8fMwHBwMuqAg0JGuhoBOz0ArCgK3fq0BLilAgMBAAE=\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"
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
"X10a9Oc0LKNYKLDVzjRTIVT3NnE0y+xncllDDHSJSXR97fz3MBHGDqhy0Vgha/fe\n"
"H/Y2E59oG01lYQ73j3JN+ibsCMtkzJDx2agCpV0LmakAD9ekHrYDWm/S41Ru6kf+\n"
"PsyHpXlh7cZuGEX4U1pblSDFrQZ9L1vTkpfW+COzEvI=\n"
"-----END SIGNATURE-----\n"
"\n"
"router moria3 moria.mit.edu 9003 9023 9033 800000\n"
"platform Tor 0.0.2pre8 on Linux moria.mit.edu 2.4.18-27.7.xbigmem #1 SMP Fri Mar 14 05:08:50 EST 2003 i686\n"
"published 2003-09-30 23:14:07\n"
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBANS6J/Er9fYo03fjUUVesc7We9Z6xIevyDJH39pYS4NUlcr5ExYgSVFJ\n"
"95aLCNx1x8Rf5YtiBKYuT3plBO/+rfuX+0iAGNkz/y3SlJVGz6aeptU3wN8CkvCL\n"
"zATEcnl4QSPhHX0wFB9A3t7wZ+Bat1PTI029lax/BkoS9JG5onHPAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"link-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAKUMY8p+7LBu7dEJnOR9HqbfcD6c4/f9GqJt3o29uu4XJPD8z2XGVBik\n"
"pZBLijhYS6U7GFg0NLR4zBlsLyB8TxHeaz5KJidJjy+BfC01jz1xwVTYDlmGVpc1\n"
"0mw0Ag0ND6aOQKKhelxhTI3Bf0R9olEXuSUKEWx3EMIz2qhLd9oDAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"signing-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAMqgq83cwzSid2LSvzsn2rvkD8U0tWvqF6PuQAsKP3QHFqtBO+66pnIm\n"
"CbiY2e6o01tmR47t557LuUCodEc8Blggxjg3ZEzvP42hsGB9LwQbcrU7grPRk0G0\n"
"IltsOF9TZ+66gCeU7LxExLdAMqT2Tx6VT4IREPJMeNxSiceEjbABAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n"
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
"GWpK2Ux/UwDaNUHwq+Xn7denyYFGS8SIWwqiMgHyUzc5wj1t2gWubJ/rMyGL59U3\n"
"o6L/9qV34aa5UyNNBHXwYkxy7ixgPURaRYpAbkQKPU3ew8BgNXG/MNLYllIUkrbb\n"
"h6G5u8RGbto+Nby/OjIh9TqdgK/B1sOdwAHI/IXiDoY=\n"
"-----END SIGNATURE-----\n"
;

322
323
324
325
326
327
328
329
int config_assign_default_dirservers(void) {
  if(router_set_routerlist_from_string(default_dirservers_string) < 0) {
    log_fn(LOG_WARN,"Bug: the default dirservers internal string is corrupt.");
    return -1;
  }
  return 0;
}

330
331
332
333
334
335
336
337
338
339
/* Call this function when they're using the default torrc but
 * we can't find it. For now, just hard-code what comes in the
 * default torrc.
 */
static int config_assign_default(or_options_t *options) {

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

  /* plus give them a dirservers file */
340
  if(config_assign_default_dirservers() < 0)
341
342
343
344
    return -1;
  return 0;
}

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

365
static int resolve_my_address(or_options_t *options) {
366
367
368
  struct in_addr in;
  struct hostent *rent;
  char localhostname[256];
369
  int explicit_ip=1;
370
371

  if(!options->Address) { /* then we need to guess our address */
372
    explicit_ip = 0; /* it's implicit */
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390

    if(gethostname(localhostname,sizeof(localhostname)) < 0) {
      log_fn(LOG_WARN,"Error obtaining local hostname");
      return -1;
    }
#if 0 /* don't worry about complaining, as long as it resolves */
    if(!strchr(localhostname,'.')) {
      log_fn(LOG_WARN,"fqdn '%s' has only one element. Misconfigured machine?",address);
      log_fn(LOG_WARN,"Try setting the Address line in your config file.");
      return -1;
    }
#endif
    options->Address = tor_strdup(localhostname);
    log_fn(LOG_DEBUG,"Guessed local host name as '%s'",options->Address);
  }

  /* now we know options->Address is set. resolve it and keep only the IP */

391
392
393
394
395
396
397
398
399
400
  if(tor_inet_aton(options->Address, &in) == 0) {
    /* then we have to resolve it */
    explicit_ip = 0;
    rent = (struct hostent *)gethostbyname(options->Address);
    if (!rent) {
      log_fn(LOG_WARN,"Could not resolve Address %s. Failing.", options->Address);
      return -1;
    }
    assert(rent->h_length == 4);
    memcpy(&in.s_addr, rent->h_addr,rent->h_length);
401
  }
402
  if(!explicit_ip && is_internal_IP(htonl(in.s_addr))) {
403
    log_fn(LOG_WARN,"Address '%s' resolves to private IP '%s'. "
404
           "Please set the Address config option to be the IP you want to use.",
405
406
407
408
409
410
411
412
413
           options->Address, inet_ntoa(in));
    return -1;
  }
  tor_free(options->Address);
  options->Address = tor_strdup(inet_ntoa(in));
  log_fn(LOG_DEBUG,"Resolved Address to %s.", options->Address);
  return 0;
}

414
static void free_options(or_options_t *options) {
415
416
417
418
419
420
421
422
  tor_free(options->LogLevel);
  tor_free(options->LogFile);
  tor_free(options->DebugLogFile);
  tor_free(options->DataDirectory);
  tor_free(options->RouterFile);
  tor_free(options->Nickname);
  tor_free(options->Address);
  tor_free(options->PidFile);
423
424
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
425
  tor_free(options->ExcludeNodes);
426
427
  tor_free(options->RendNodes);
  tor_free(options->RendExcludeNodes);
428
  tor_free(options->ExitPolicy);
429
430
  tor_free(options->SocksBindAddress);
  tor_free(options->ORBindAddress);
431
  tor_free(options->DirBindAddress);
432
  tor_free(options->RecommendedVersions);
433
434
  tor_free(options->User);
  tor_free(options->Group);
435
  config_free_lines(options->RendConfigLines);
436
}
437

438
static void init_options(or_options_t *options) {
439
/* give reasonable values for each option. Defaults to zero. */
440
  memset(options,0,sizeof(or_options_t));
441
  options->LogLevel = tor_strdup("notice");
442
443
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
444
  options->ExcludeNodes = tor_strdup("");
445
446
  options->RendNodes = tor_strdup("");
  options->RendExcludeNodes = tor_strdup("");
447
  options->ExitPolicy = tor_strdup("");
448
449
  options->SocksBindAddress = tor_strdup("127.0.0.1");
  options->ORBindAddress = tor_strdup("0.0.0.0");
450
  options->DirBindAddress = tor_strdup("0.0.0.0");
451
  options->RecommendedVersions = NULL;
452
  options->loglevel = LOG_INFO;
453
  options->PidFile = NULL; // tor_strdup("tor.pid");
454
  options->DataDirectory = NULL;
455
  options->PathlenCoinWeight = 0.3;
456
  options->MaxConn = 900;
457
  options->DirFetchPostPeriod = 600;
458
  options->KeepalivePeriod = 300;
459
  options->MaxOnionsPending = 100;
460
  options->NewCircuitPeriod = 30; /* twice a minute */
461
462
  options->BandwidthRate = 800000; /* at most 800kB/s total sustained incoming */
  options->BandwidthBurst = 10000000; /* max burst on the token bucket */
463
  options->NumCpus = 1;
464
  options->RendConfigLines = NULL;
465
466
467
468
}

/* return 0 if success, <0 if failure. */
int getconfig(int argc, char **argv, or_options_t *options) {
469
  struct config_line_t *cl;
470
471
472
473
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
474
475
476
477
478
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
479
  int previous_orport = -1;
480
  int using_default_torrc;
481
482
483
484
485
486
487
488
489
490

  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 */
491
492
    if(options->PidFile)
      previous_pidfile = tor_strdup(options->PidFile);
493
    previous_runasdaemon = options->RunAsDaemon;
494
    previous_orport = options->ORPort;
495
496
    free_options(options);
  }
Roger Dingledine's avatar
Roger Dingledine committed
497
  init_options(options);
498

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

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

509
510
511
512
513
514
/* 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 */
515
    fname = argv[i+1];
516
    using_default_torrc = 0;
517
518
  } else { /* didn't find one, try CONFDIR */
    fname = CONFDIR "/torrc";
519
    using_default_torrc = 1;
520
521
522
523
  }
  log(LOG_DEBUG,"Opening config file '%s'",fname);

  cf = config_open(fname);
524
  if(!cf) {
525
    if(using_default_torrc == 1) {
526
      log(LOG_NOTICE, "Configuration file '%s' not present, using reasonable defaults.",fname);
527
528
529
530
531
532
533
534
535
      if(config_assign_default(options) < 0)
        return -1;
    } else {
      log(LOG_WARN, "Unable to open configuration file '%s'.",fname);
      return -1;
    }
  } else { /* it opened successfully. use it. */
    cl = config_get_lines(cf);
    if(!cl) return -1;
536
537
    if(config_assign(options,cl) < 0)
      return -1;
538
539
    config_free_lines(cl);
    config_close(cf);
540
  }
541

542
543
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
544
545
  if(config_assign(options,cl) < 0)
    return -1;
546
547
548
549
  config_free_lines(cl);

/* Validate options */

550
  /* first check if any of the previous options have changed but aren't allowed to */
551
552
553
554
555
556
557
558
559
560
561
562
  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;
  }

563
  if(previous_orport == 0 && options->ORPort > 0) {
Roger Dingledine's avatar
Roger Dingledine committed
564
    log_fn(LOG_WARN,"During reload, change from ORPort=0 to >0 not allowed. Failing.");
565
566
567
    return -1;
  }

568
  if(options->LogLevel) {
569
    if(!strcmp(options->LogLevel,"err"))
570
      options->loglevel = LOG_ERR;
Roger Dingledine's avatar
Roger Dingledine committed
571
572
    else if(!strcmp(options->LogLevel,"warn"))
      options->loglevel = LOG_WARN;
573
574
    else if(!strcmp(options->LogLevel,"notice"))
      options->loglevel = LOG_NOTICE;
575
576
577
578
579
    else if(!strcmp(options->LogLevel,"info"))
      options->loglevel = LOG_INFO;
    else if(!strcmp(options->LogLevel,"debug"))
      options->loglevel = LOG_DEBUG;
    else {
580
      log(LOG_WARN,"LogLevel must be one of err|warn|notice|info|debug.");
581
582
583
584
      result = -1;
    }
  }

585
  if(options->ORPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
586
    log(LOG_WARN,"ORPort option can't be negative.");
587
588
589
    result = -1;
  }

590
591
  if(options->ORPort && options->DataDirectory == NULL) {
    log(LOG_WARN,"DataDirectory option required if ORPort is set, but not found.");
592
593
594
    result = -1;
  }

595
596
597
598
  if (options->ORPort) {
    if (options->Nickname == NULL) {
      log_fn(LOG_WARN,"Nickname required if ORPort is set, but not found.");
      result = -1;
599
600
601
602
603
604
605
606
607
608
609
    } 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;
      }
610
    }
611
612
  }

613
  if(options->ORPort) { /* get an IP for ourselves */
614
615
    if(resolve_my_address(options) < 0)
      result = -1;
616
617
  }

618
619
  if(options->SocksPort < 0) {
    log(LOG_WARN,"SocksPort option can't be negative.");
620
621
622
    result = -1;
  }

623
624
625
  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
626
  }
627

628
  if(options->DirPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
629
    log(LOG_WARN,"DirPort option can't be negative.");
630
631
632
    result = -1;
  }

633
634
635
636
637
  if(options->DirPort && options->RecommendedVersions == NULL) {
    log(LOG_WARN,"Directory servers must configure RecommendedVersions.");
    result = -1;
  }

638
  if(options->SocksPort > 1 &&
639
640
     (options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
    log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
641
642
643
    result = -1;
  }

644
  if(options->MaxConn < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
645
    log(LOG_WARN,"MaxConn option must be a non-zero positive integer.");
646
647
648
649
    result = -1;
  }

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

654
  if(options->DirFetchPostPeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
655
    log(LOG_WARN,"DirFetchPostPeriod option must be positive.");
656
657
    result = -1;
  }
658
659
660
661
  if(options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME/2) {
    log(LOG_WARN,"DirFetchPostPeriod is too large; clipping.");
    options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME/2;
  }
662
663

  if(options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
664
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
665
666
667
    result = -1;
  }

668
669
670
671
  /* XXX look at the various nicknamelists and make sure they're
   * valid and don't have hostnames that are too long.
   */

672
673
674
675
  if (rend_config_services(options) < 0) {
    result = -1;
  }

676
  return result;
677
678
}

679
680
681
682
683
684
685
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/