config.c 13.8 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
14
/* 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

15
#define CONFIG_LINE_MAXLEN 4096
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

struct config_line {
  char *key;
  char *value;
  struct config_line *next;
};

static FILE *config_open(const unsigned char *filename);
static int config_close(FILE *f);
static struct config_line *config_get_commandlines(int argc, char **argv);
static struct config_line *config_get_lines(FILE *f);
static void config_free_lines(struct config_line *front);
static int config_compare(struct config_line *c, char *key, int type, void *arg);
static void config_assign(or_options_t *options, struct config_line *list);

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

/* close configuration file */
42
static int config_close(FILE *f) {
43
44
45
46
  assert(f);
  return fclose(f);
}

47
static struct config_line *config_get_commandlines(int argc, char **argv) {
48
49
50
51
52
53
54
55
56
57
58
59
  struct config_line *new;
  struct config_line *front = NULL;
  char *s;
  int i = 1;

  while(i < argc-1) { 
    if(!strcmp(argv[i],"-f")) {
//      log(LOG_DEBUG,"Commandline: skipping over -f.");
      i+=2; /* this is the config file option. ignore it. */
      continue;
    }

60
    new = tor_malloc(sizeof(struct config_line));
61
62
63
    s = argv[i];
    while(*s == '-')
      s++;
64
65
    new->key = tor_strdup(s);
    new->value = tor_strdup(argv[i+1]);
66
67
68
69
70
71
72
73
74
75

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

76
77
/* parse the config file and strdup into key/value strings. Return list,
 * or NULL if parsing the file failed.
78
 * Warn and ignore mangled lines. */
79
static struct config_line *config_get_lines(FILE *f) {
80
81
82
  struct config_line *new;
  struct config_line *front = NULL;
  char line[CONFIG_LINE_MAXLEN];
83
84
  int result;
  char *key, *value;
85

86
  while( (result=parse_line_from_file(line,sizeof(line),f,&key,&value)) > 0) {
87
    new = tor_malloc(sizeof(struct config_line));
88
89
    new->key = tor_strdup(key);
    new->value = tor_strdup(value);
90
91
92

    new->next = front;
    front = new;
Roger Dingledine's avatar
Roger Dingledine committed
93
  }
94
95
  if(result < 0)
    return NULL;
96
  return front;
Roger Dingledine's avatar
Roger Dingledine committed
97
98
}

99
static void config_free_lines(struct config_line *front) {
100
101
102
103
104
105
106
107
108
109
110
111
  struct config_line *tmp;

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

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

112
static int config_compare(struct config_line *c, char *key, int type, void *arg) {
113
  int i;
114
115
116
117
118

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

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

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

144
static void config_assign(or_options_t *options, struct config_line *list) {
145
146
147
148
149
150
151
152
153

  /* iterate through list. for each item convert as appropriate and assign to 'options'. */

  while(list) {
    if(

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

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

156
    config_compare(list, "DebugLogFile",   CONFIG_TYPE_STRING, &options->DebugLogFile) ||
157
    config_compare(list, "DataDirectory",  CONFIG_TYPE_STRING, &options->DataDirectory) ||
158
159
160
161
    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) ||

162
163
    config_compare(list, "ExitNodes",      CONFIG_TYPE_STRING, &options->ExitNodes) ||
    config_compare(list, "EntryNodes",     CONFIG_TYPE_STRING, &options->EntryNodes) ||
164
    config_compare(list, "ExitPolicy",     CONFIG_TYPE_STRING, &options->ExitPolicy) ||
165

166
    config_compare(list, "Group",          CONFIG_TYPE_STRING, &options->Group) ||
167

168
169
170
171
172
173
174
175
176
    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) ||
177
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
178
179

    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
180
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
181
    config_compare(list, "NumCpus",        CONFIG_TYPE_INT, &options->NumCpus) ||
182

183
184
185
    config_compare(list, "ORPort",         CONFIG_TYPE_INT, &options->ORPort) ||
    config_compare(list, "ORBindAddress",  CONFIG_TYPE_STRING, &options->ORBindAddress) ||
    config_compare(list, "OnionRouter",    CONFIG_TYPE_BOOL, &options->OnionRouter) ||
186

187
    config_compare(list, "PidFile",        CONFIG_TYPE_STRING, &options->PidFile) ||
188
    config_compare(list, "PathlenCoinWeight",CONFIG_TYPE_DOUBLE, &options->PathlenCoinWeight) ||
189
190
191

    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
    config_compare(list, "RunAsDaemon",    CONFIG_TYPE_BOOL, &options->RunAsDaemon) ||
192
    config_compare(list, "RecommendedVersions",CONFIG_TYPE_STRING, &options->RecommendedVersions) ||
193

194
195
196
197
198
199
200
    config_compare(list, "SocksPort",      CONFIG_TYPE_INT, &options->SocksPort) ||
    config_compare(list, "SocksBindAddress",CONFIG_TYPE_STRING,&options->SocksBindAddress) ||

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

    config_compare(list, "User",           CONFIG_TYPE_STRING, &options->User)
201
202
203
    ) {
      /* then we're ok. it matched something. */
    } else {
Roger Dingledine's avatar
Roger Dingledine committed
204
      log_fn(LOG_WARN,"Ignoring unknown keyword '%s'.",list->key);
205
206
207
208
209
210
    }

    list = list->next;
  }  
}

211
/* prints the usage of tor. */
212
void print_usage(void) {
213
214
215
  printf("tor -f <torrc> [args]\n"
         "-d <file>\t\tDebug file\n"
         "-m <max>\t\tMax number of connections\n"
216
217
218
219
220
         "-l <level>\t\tLog level\n"
         "-t <bandwidth>\t\tTotal bandwidth\n"
         "-r <file>\t\tList of known routers\n");
  printf("\nClient options:\n"
         "-e \"nick1 nick2 ...\"\t\tExit nodes\n"
221
         "-s <IP>\t\t\tPort to bind to for Socks\n"
222
         );
223
224
  printf("\nServer options:\n"
         "-n <nick>\t\tNickname of router\n"
225
226
227
         "-o <port>\t\tOR port to bind to\n"
         "-p <file>\t\tPID file\n"
         );
228
229
}

230
231
232
233
234
235
236
237
238
void free_options(or_options_t *options) {
  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);
239
240
  tor_free(options->ExitNodes);
  tor_free(options->EntryNodes);
241
  tor_free(options->ExitPolicy);
242
243
  tor_free(options->SocksBindAddress);
  tor_free(options->ORBindAddress);
244
  tor_free(options->DirBindAddress);
245
  tor_free(options->RecommendedVersions);
246
247
  tor_free(options->User);
  tor_free(options->Group);
248
}
249

250
void init_options(or_options_t *options) {
251
/* give reasonable values for each option. Defaults to zero. */
252
  memset(options,0,sizeof(or_options_t));
253
  options->LogLevel = tor_strdup("info");
254
255
  options->ExitNodes = tor_strdup("");
  options->EntryNodes = tor_strdup("");
256
  options->ExitPolicy = tor_strdup("reject 127.0.0.1:*");
257
258
  options->SocksBindAddress = tor_strdup("127.0.0.1");
  options->ORBindAddress = tor_strdup("0.0.0.0");
259
  options->DirBindAddress = tor_strdup("0.0.0.0");
260
  options->RecommendedVersions = tor_strdup("none");
261
  options->loglevel = LOG_INFO;
262
  options->PidFile = tor_strdup("tor.pid");
263
  options->DataDirectory = NULL;
264
  options->PathlenCoinWeight = 0.3;
265
  options->MaxConn = 900;
266
  options->DirFetchPostPeriod = 600;
267
  options->KeepalivePeriod = 300;
268
  options->MaxOnionsPending = 100;
269
  options->NewCircuitPeriod = 60; /* once a minute */
270
  options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */
271
  options->NumCpus = 1;
272
273
274
275
276
277
278
279
280
}

/* return 0 if success, <0 if failure. */
int getconfig(int argc, char **argv, or_options_t *options) {
  struct config_line *cl;
  FILE *cf;
  char *fname;
  int i;
  int result = 0;
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  static int first_load = 1;
  static char **backup_argv;
  static int backup_argc;
  char *previous_pidfile = NULL;
  int previous_runasdaemon = 0;
  int previous_onionrouter = -1;

  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 */
    previous_pidfile = tor_strdup(options->PidFile);
    previous_runasdaemon = options->RunAsDaemon;
    previous_onionrouter = options->OnionRouter;
    free_options(options);
  }
302
  init_options(options); 
303

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

309
310
311
312
313
314
/* 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 */
315
    fname = argv[i+1];
316
317
  } else { /* didn't find one, try CONFDIR */
    fname = CONFDIR "/torrc";
318
319
320
321
  }
  log(LOG_DEBUG,"Opening config file '%s'",fname);

  cf = config_open(fname);
322
  if(!cf) {
Roger Dingledine's avatar
Roger Dingledine committed
323
    log(LOG_WARN, "Unable to open configuration file '%s'.",fname);
324
    return -1;
325
  }
326
327

  cl = config_get_lines(cf);
328
  if(!cl) return -1;
329
330
331
  config_assign(options,cl);
  config_free_lines(cl);
  config_close(cf);
332
333
334
335
336
337
338
339
 
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
  config_assign(options,cl);
  config_free_lines(cl);

/* Validate options */

340
  /* first check if any of the previous options have changed but aren't allowed to */
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  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;
  }
  if(previous_onionrouter >= 0 && previous_onionrouter != options->OnionRouter) {
    log_fn(LOG_WARN,"During reload, OnionRouter changed from %d to %d. Failing.",
           previous_onionrouter, options->OnionRouter);
    return -1;
  }

358
  if(options->LogLevel) {
359
    if(!strcmp(options->LogLevel,"err"))
360
      options->loglevel = LOG_ERR;
Roger Dingledine's avatar
Roger Dingledine committed
361
362
    else if(!strcmp(options->LogLevel,"warn"))
      options->loglevel = LOG_WARN;
363
364
365
366
367
    else if(!strcmp(options->LogLevel,"info"))
      options->loglevel = LOG_INFO;
    else if(!strcmp(options->LogLevel,"debug"))
      options->loglevel = LOG_DEBUG;
    else {
Roger Dingledine's avatar
Roger Dingledine committed
368
      log(LOG_WARN,"LogLevel must be one of err|warn|info|debug.");
369
370
371
372
373
      result = -1;
    }
  }

  if(options->RouterFile == NULL) {
Roger Dingledine's avatar
Roger Dingledine committed
374
    log(LOG_WARN,"RouterFile option required, but not found.");
375
376
377
    result = -1;
  }

378
  if(options->ORPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
379
    log(LOG_WARN,"ORPort option can't be negative.");
380
381
382
    result = -1;
  }

383
  if(options->OnionRouter && options->ORPort == 0) {
Roger Dingledine's avatar
Roger Dingledine committed
384
    log(LOG_WARN,"If OnionRouter is set, then ORPort must be positive.");
385
386
387
    result = -1;
  }

388
  if(options->OnionRouter && options->DataDirectory == NULL) {
Roger Dingledine's avatar
Roger Dingledine committed
389
    log(LOG_WARN,"DataDirectory option required for OnionRouter, but not found.");
390
391
392
    result = -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
393
  if(options->OnionRouter && options->Nickname == NULL) {
Roger Dingledine's avatar
Roger Dingledine committed
394
    log_fn(LOG_WARN,"Nickname required for OnionRouter, but not found.");
395
396
397
    result = -1;
  }

398
399
  if(options->SocksPort < 0) {
    log(LOG_WARN,"SocksPort option can't be negative.");
400
401
402
    result = -1;
  }

403
404
405
406
407
  if(options->SocksPort == 0 && options->ORPort == 0) {
    log(LOG_WARN,"SocksPort and ORPort are both undefined? Quitting.");
    result = -1;
  } 

408
  if(options->DirPort < 0) {
Roger Dingledine's avatar
Roger Dingledine committed
409
    log(LOG_WARN,"DirPort option can't be negative.");
410
411
412
    result = -1;
  }

413
  if(options->SocksPort > 1 &&
414
415
     (options->PathlenCoinWeight < 0.0 || options->PathlenCoinWeight >= 1.0)) {
    log(LOG_WARN,"PathlenCoinWeight option must be >=0.0 and <1.0.");
416
417
418
    result = -1;
  }

419
  if(options->MaxConn < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
420
    log(LOG_WARN,"MaxConn option must be a non-zero positive integer.");
421
422
423
424
    result = -1;
  }

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

429
  if(options->DirFetchPostPeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
430
    log(LOG_WARN,"DirFetchPostPeriod option must be positive.");
431
432
433
434
    result = -1;
  }

  if(options->KeepalivePeriod < 1) {
Roger Dingledine's avatar
Roger Dingledine committed
435
    log(LOG_WARN,"KeepalivePeriod option must be positive.");
436
437
438
439
    result = -1;
  }

  return result;
440
441
}

442
443
444
445
446
447
448
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/