config.c 11.3 KB
Newer Older
1
2
3
4
/* Copyright 2001,2002 Roger Dingledine, Matej Pfajfar. */
/* 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
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* 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

#define CONFIG_LINE_MAXLEN 1024

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
64
65
66
67
68
69
70
71
72
73
74
75
76
    s = argv[i];
    while(*s == '-')
      s++;
    new->key = strdup(s);
    new->value = strdup(argv[i+1]);

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

/* parse the config file and strdup into key/value strings. Return list.
77
 * Warn and ignore mangled lines. */
78
static struct config_line *config_get_lines(FILE *f) {
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
  struct config_line *new;
  struct config_line *front = NULL;
  char line[CONFIG_LINE_MAXLEN];
  int lineno=0; /* current line number */
  char *s;
  char *start, *end;

  assert(f);

  fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */

  while(fgets(line, CONFIG_LINE_MAXLEN, f)) {
    lineno++;

    /* first strip comments */
    s = strchr(line,'#');
    if(s) {
      *s = 0; /* stop the line there */
    }

    /* walk to the end, remove end whitespace */
100
    s = strchr(line, 0); /* now we're at the null */
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    do {
      *s = 0;
      s--;
    } while (isspace(*s));

    start = line;
    while(isspace(*start))
      start++;
    if(*start == 0)
      continue; /* this line has nothing on it */

    end = start;
    while(*end && !isspace(*end))
      end++;
    s = end;
    while(*s && isspace(*s))
      s++;
    if(!*end || !*s) { /* only a keyword on this line. no value. */
      log(LOG_WARNING,"Config line %d has keyword '%s' but no value. Skipping.",lineno,s);
    }
    *end = 0; /* null it out */

    /* prepare to parse the string into key / value */
124
    new = tor_malloc(sizeof(struct config_line));
125
126
127
128
129
130
131
    new->key = strdup(start);
    new->value = strdup(s);

    log(LOG_DEBUG,"Config line %d: parsed keyword '%s', value '%s'",
      lineno, new->key, new->value);
    new->next = front;
    front = new;
Roger Dingledine's avatar
Roger Dingledine committed
132
133
  }

134
  return front;
Roger Dingledine's avatar
Roger Dingledine committed
135
136
}

137
static void config_free_lines(struct config_line *front) {
138
139
140
141
142
143
144
145
146
147
148
149
  struct config_line *tmp;

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

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

150
static int config_compare(struct config_line *c, char *key, int type, void *arg) {
151
  int i;
152
153
154
155
156

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

  /* it's a match. cast and assign. */
157
  log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value);
158
159
160
161

  switch(type) {
    case CONFIG_TYPE_INT:   
      *(int *)arg = atoi(c->value);
162
      break;
163
164
165
    case CONFIG_TYPE_BOOL:
      i = atoi(c->value);
      if (i != 0 && i != 1) {
166
167
        log(LOG_ERR, "Boolean keyword '%s' expects 0 or 1", c->key);
        return 0;
168
169
170
      }
      *(int *)arg = i;
      break;
171
172
    case CONFIG_TYPE_STRING:
      *(char **)arg = strdup(c->value);
173
      break;
174
175
    case CONFIG_TYPE_DOUBLE:
      *(double *)arg = atof(c->value);
176
      break;
177
178
179
180
  }
  return 1;
}

181
static void config_assign(or_options_t *options, struct config_line *list) {
182
183
184
185
186
187
188
189
190
191
192

  /* 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 */
    config_compare(list, "LogLevel",       CONFIG_TYPE_STRING, &options->LogLevel) ||
    config_compare(list, "PrivateKeyFile", CONFIG_TYPE_STRING, &options->PrivateKeyFile) ||
193
    config_compare(list, "SigningPrivateKeyFile", CONFIG_TYPE_STRING, &options->SigningPrivateKeyFile) ||
194
    config_compare(list, "RouterFile",     CONFIG_TYPE_STRING, &options->RouterFile) ||
Roger Dingledine's avatar
Roger Dingledine committed
195
196
    config_compare(list, "CertFile",       CONFIG_TYPE_STRING, &options->CertFile) ||
    config_compare(list, "Nickname",       CONFIG_TYPE_STRING, &options->Nickname) ||
197
198
199
200
201
202
203
204

    /* int options */
    config_compare(list, "MaxConn",         CONFIG_TYPE_INT, &options->MaxConn) ||
    config_compare(list, "APPort",          CONFIG_TYPE_INT, &options->APPort) ||
    config_compare(list, "ORPort",          CONFIG_TYPE_INT, &options->ORPort) ||
    config_compare(list, "DirPort",         CONFIG_TYPE_INT, &options->DirPort) ||
    config_compare(list, "DirFetchPeriod",  CONFIG_TYPE_INT, &options->DirFetchPeriod) ||
    config_compare(list, "KeepalivePeriod", CONFIG_TYPE_INT, &options->KeepalivePeriod) ||
205
    config_compare(list, "MaxOnionsPending",CONFIG_TYPE_INT, &options->MaxOnionsPending) ||
206
    config_compare(list, "NewCircuitPeriod",CONFIG_TYPE_INT, &options->NewCircuitPeriod) ||
207
    config_compare(list, "TotalBandwidth",  CONFIG_TYPE_INT, &options->TotalBandwidth) ||
208
    config_compare(list, "NumCpus",         CONFIG_TYPE_INT, &options->NumCpus) ||
209

210
    config_compare(list, "OnionRouter",     CONFIG_TYPE_BOOL, &options->OnionRouter) ||
211
212
213
    config_compare(list, "Daemon",          CONFIG_TYPE_BOOL, &options->Daemon) ||
    config_compare(list, "TrafficShaping",  CONFIG_TYPE_BOOL, &options->TrafficShaping) ||
    config_compare(list, "LinkPadding",     CONFIG_TYPE_BOOL, &options->LinkPadding) ||
214
    config_compare(list, "IgnoreVersion",   CONFIG_TYPE_BOOL, &options->IgnoreVersion) ||
215

216
217
218
219
220
221
    /* float options */
    config_compare(list, "CoinWeight",     CONFIG_TYPE_DOUBLE, &options->CoinWeight)

    ) {
      /* then we're ok. it matched something. */
    } else {
222
      log_fn(LOG_WARNING,"Ignoring unknown keyword '%s'.",list->key);
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    }

    list = list->next;
  }  
}

/* 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[256];
  int i;
  int result = 0;

237
/* give reasonable values for each option. Defaults to zero. */
238
239
240
241
  memset(options,0,sizeof(or_options_t));
  options->LogLevel = "debug";
  options->loglevel = LOG_DEBUG;
  options->CoinWeight = 0.8;
242
  options->MaxConn = 900;
243
  options->DirFetchPeriod = 600;
244
  options->KeepalivePeriod = 300;
245
  options->MaxOnionsPending = 10;
246
  options->NewCircuitPeriod = 60; /* once a minute */
247
  options->TotalBandwidth = 800000; /* at most 800kB/s total sustained incoming */
248
  options->NumCpus = 1;
Roger Dingledine's avatar
Roger Dingledine committed
249
  options->CertFile = "default.cert";
250
251
252
//  options->ReconnectPeriod = 6001;

/* get config lines from /etc/torrc and assign them */
253
254
#define rcfile "torrc"
  snprintf(fname,256,"/etc/%s",rcfile);
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

  cf = config_open(fname);
  if(cf) {
    /* we got it open. pull out the config lines. */
    cl = config_get_lines(cf);
    config_assign(options,cl);
    config_free_lines(cl);
    config_close(cf);
  }
  /* if we failed to open it, ignore */

/* learn config file name, get config lines, assign them */
  i = 1;
  while(i < argc-1 && strcmp(argv[i],"-f")) {
//    log(LOG_DEBUG,"examining arg %d (%s), it's not -f.",i,argv[i]);
    i++;
  }
  if(i < argc-1) { /* we found one */
    log(LOG_DEBUG,"Opening specified config file '%s'",argv[i+1]);
    cf = config_open(argv[i+1]);
    if(!cf) { /* it's defined but not there. that's no good. */
      log(LOG_ERR, "Unable to open configuration file '%s'.",argv[i+1]);
      return -1;
    }
    cl = config_get_lines(cf);
    config_assign(options,cl);
    config_free_lines(cl);
    config_close(cf);
  }
 
/* go through command-line variables too */
  cl = config_get_commandlines(argc,argv);
  config_assign(options,cl);
  config_free_lines(cl);

/* Validate options */

  if(options->LogLevel) {
    if(!strcmp(options->LogLevel,"emerg"))
      options->loglevel = LOG_EMERG;
    else if(!strcmp(options->LogLevel,"alert"))
      options->loglevel = LOG_ALERT;
    else if(!strcmp(options->LogLevel,"crit"))
      options->loglevel = LOG_CRIT;
    else if(!strcmp(options->LogLevel,"err"))
      options->loglevel = LOG_ERR;
    else if(!strcmp(options->LogLevel,"warning"))
      options->loglevel = LOG_WARNING;
    else if(!strcmp(options->LogLevel,"notice"))
      options->loglevel = LOG_NOTICE;
    else if(!strcmp(options->LogLevel,"info"))
      options->loglevel = LOG_INFO;
    else if(!strcmp(options->LogLevel,"debug"))
      options->loglevel = LOG_DEBUG;
    else {
      log(LOG_ERR,"LogLevel must be one of emerg|alert|crit|err|warning|notice|info|debug.");
      result = -1;
    }
  }

  if(options->RouterFile == NULL) {
    log(LOG_ERR,"RouterFile option required, but not found.");
    result = -1;
  }

320
  if(options->ORPort < 0) {
321
    log(LOG_ERR,"ORPort option can't be negative.");
322
323
324
    result = -1;
  }

325
326
  if(options->OnionRouter && options->ORPort == 0) {
    log(LOG_ERR,"If OnionRouter is set, then ORPort must be positive.");
327
328
329
    result = -1;
  }

330
331
  if(options->OnionRouter && options->PrivateKeyFile == NULL) {
    log(LOG_ERR,"PrivateKeyFile option required for OnionRouter, but not found.");
332
333
334
    result = -1;
  }

Roger Dingledine's avatar
Roger Dingledine committed
335
336
337
338
339
  if(options->OnionRouter && options->Nickname == NULL) {
    log_fn(LOG_ERR,"Nickname required for OnionRouter, but not found.");
    return -1;
  }

340
341
  if(options->DirPort > 0 && options->SigningPrivateKeyFile == NULL) {
    log(LOG_ERR,"SigningPrivateKeyFile option required for DirServer, but not found.");
342
343
344
    result = -1;
  }

345
346
  if(options->APPort < 0) {
    log(LOG_ERR,"APPort option can't be negative.");
347
348
349
    result = -1;
  }

350
351
  if(options->DirPort < 0) {
    log(LOG_ERR,"DirPort option can't be negative.");
352
353
354
    result = -1;
  }

355
  if(options->APPort > 1 &&
356
     (options->CoinWeight < 0.0 || options->CoinWeight >= 1.0)) {
357
    log(LOG_ERR,"CoinWeight option must be >=0.0 and <1.0.");
358
359
360
    result = -1;
  }

361
  if(options->MaxConn < 1) {
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    log(LOG_ERR,"MaxConn option must be a non-zero positive integer.");
    result = -1;
  }

  if(options->MaxConn >= MAXCONNECTIONS) {
    log(LOG_ERR,"MaxConn option must be less than %d.", MAXCONNECTIONS);
    result = -1;
  }

  if(options->DirFetchPeriod < 1) {
    log(LOG_ERR,"DirFetchPeriod option must be positive.");
    result = -1;
  }

  if(options->KeepalivePeriod < 1) {
    log(LOG_ERR,"KeepalivePeriod option must be positive.");
    result = -1;
  }

  return result;
382
383
}

384
385
386
387
388
389
390
/*
  Local Variables:
  mode:c
  indent-tabs-mode:nil
  c-basic-offset:2
  End:
*/