config.c 86.9 KB
Newer Older
Roger Dingledine's avatar
Roger Dingledine committed
1
2
/* Copyright 2001 Matej Pfajfar.
 * Copyright 2001-2004 Roger Dingledine.
Nick Mathewson's avatar
Nick Mathewson committed
3
 * Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
4
5
/* See LICENSE for licensing information */
/* $Id$ */
6
const char config_c_id[] = "$Id$";
7

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

Roger Dingledine's avatar
Roger Dingledine committed
13
#include "or.h"
14
15
16
#ifdef MS_WINDOWS
#include <shlobj.h>
#endif
17
#include "../common/aes.h"
Roger Dingledine's avatar
Roger Dingledine committed
18

Nick Mathewson's avatar
Nick Mathewson committed
19
20
/** Enumeration of types which option values can take */
typedef enum config_type_t {
21
22
  CONFIG_TYPE_STRING = 0,   /**< An arbitrary string. */
  CONFIG_TYPE_UINT,         /**< A non-negative integer less than MAX_INT */
23
24
  CONFIG_TYPE_INTERVAL,     /**< A number of seconds, with optional units*/
  CONFIG_TYPE_MEMUNIT,      /**< A number of bytes, with optional units*/
25
26
27
28
29
  CONFIG_TYPE_DOUBLE,       /**< A floating-point value */
  CONFIG_TYPE_BOOL,         /**< A boolean value, expressed as 0 or 1. */
  CONFIG_TYPE_CSV,          /**< A list of strings, separated by commas and optional
                              * whitespace. */
  CONFIG_TYPE_LINELIST,     /**< Uninterpreted config lines */
30
31
32
33
34
  CONFIG_TYPE_LINELIST_S,   /**< Uninterpreted, context-sensitive config lines,
                             * mixed with other keywords. */
  CONFIG_TYPE_LINELIST_V,   /**< Catch-all "virtual" option to summarize
                             * context-sensitive config lines when fetching.
                             */
35
  CONFIG_TYPE_OBSOLETE,     /**< Obsolete (ignored) option. */
Nick Mathewson's avatar
Nick Mathewson committed
36
} config_type_t;
37

Nick Mathewson's avatar
Nick Mathewson committed
38
/* An abbreviation for a configuration option allowed on the command line */
39
typedef struct config_abbrev_t {
40
41
  const char *abbreviated;
  const char *full;
42
  int commandline_only;
43
44
} config_abbrev_t;

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

Nick Mathewson's avatar
Nick Mathewson committed
49
/* A list of command-line abbreviations. */
50
static config_abbrev_t _config_abbrevs[] = {
51
  PLURAL(ExitNode),
52
  PLURAL(EntryNode),
53
54
  PLURAL(ExcludeNode),
  PLURAL(FirewallPort),
55
  PLURAL(LongLivedPort),
56
57
  PLURAL(HiddenServiceNode),
  PLURAL(HiddenServiceExcludeNode),
58
  PLURAL(NumCpu),
59
60
  PLURAL(RendNode),
  PLURAL(RendExcludeNode),
61
62
  PLURAL(StrictEntryNode),
  PLURAL(StrictExitNode),
63
  { "l", "Log", 1},
64
65
  { "BandwidthRateBytes", "BandwidthRate", 0},
  { "BandwidthBurstBytes", "BandwidthBurst", 0},
66
  { "DirFetchPostPeriod", "StatusFetchPeriod", 0},
67
  { "MaxConn", "ConnLimit", 0},
68
  { NULL, NULL , 0},
69
};
70
#undef PLURAL
71

72
/** A variable allowed in the configuration file or on the command line. */
73
typedef struct config_var_t {
74
75
76
77
  const char *name; /**< The full keyword (case insensitive). */
  config_type_t type; /**< How to interpret the type and turn it into a value. */
  off_t var_offset; /**< Offset of the corresponding member of or_options_t. */
  const char *initvalue; /**< String (or null) describing initial value. */
78
79
} config_var_t;

Nick Mathewson's avatar
Nick Mathewson committed
80
/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
Nick Mathewson's avatar
Nick Mathewson committed
81
#define STRUCT_OFFSET(tp, member) ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
Nick Mathewson's avatar
Nick Mathewson committed
82
83
84
85
/** An entry for config_vars: "The option <b>name</b> has type
 * CONFIG_TYPE_<b>conftype</b>, and corresponds to
 * or_options_t.<b>member</b>"
 */
86
87
#define VAR(name,conftype,member,initvalue) \
  { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), initvalue }
Nick Mathewson's avatar
Nick Mathewson committed
88
/** An entry for config_vars: "The option <b>name</b> is obsolete." */
89
#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL }
90

Nick Mathewson's avatar
Nick Mathewson committed
91
92
93
94
/** Array of configuration options.  Until we disallow nonstandard
 * abbreviations, order is significant, since the first matching option will
 * be chosen first.
 */
95
static config_var_t _config_vars[] = {
96
  VAR("Address",             STRING,   Address,              NULL),
97
  VAR("AccountingStart",     STRING,   AccountingStart,      NULL),
98
  VAR("AllowUnverifiedNodes",CSV,      AllowUnverifiedNodes, "middle,rendezvous"),
99
  VAR("AuthoritativeDirectory",BOOL,   AuthoritativeDir,     "0"),
100
  VAR("BandwidthRate",       MEMUNIT,  BandwidthRate,        "2 MB"),
101
  VAR("BandwidthBurst",      MEMUNIT,  BandwidthBurst,       "5 MB"),
102
  VAR("MaxAdvertisedBandwidth",MEMUNIT,MaxAdvertisedBandwidth,"128 TB"),
103
104
  VAR("ClientOnly",          BOOL,     ClientOnly,           "0"),
  VAR("ContactInfo",         STRING,   ContactInfo,          NULL),
105
  VAR("ControlPort",         UINT,     ControlPort,          "0"),
106
  VAR("CookieAuthentication",BOOL,     CookieAuthentication, "0"),
107
108
  VAR("DebugLogFile",        STRING,   DebugLogFile,         NULL),
  VAR("DataDirectory",       STRING,   DataDirectory,        NULL),
109
  VAR("DirAllowPrivateAddresses",BOOL, DirAllowPrivateAddresses, NULL),
110
111
  VAR("DirPort",             UINT,     DirPort,              "0"),
  VAR("DirBindAddress",      LINELIST, DirBindAddress,       NULL),
112
113
  /** DOCDOC **/
  VAR("DirFetchPeriod",      INTERVAL, DirFetchPeriod,       "0 seconds"),
114
115
  VAR("DirPostPeriod",       INTERVAL, DirPostPeriod,        "20 minutes"),
  VAR("RendPostPeriod",      INTERVAL, RendPostPeriod,       "20 minutes"),
116
117
118
119
120
121
122
123
  VAR("DirPolicy",           LINELIST, DirPolicy,            NULL),
  VAR("DirServer",           LINELIST, DirServers,           NULL),
  VAR("ExitNodes",           STRING,   ExitNodes,            NULL),
  VAR("EntryNodes",          STRING,   EntryNodes,           NULL),
  VAR("StrictExitNodes",     BOOL,     StrictExitNodes,      "0"),
  VAR("StrictEntryNodes",    BOOL,     StrictEntryNodes,     "0"),
  VAR("ExitPolicy",          LINELIST, ExitPolicy,           NULL),
  VAR("ExcludeNodes",        STRING,   ExcludeNodes,         NULL),
124
125
  VAR("TrackHostExits",      CSV,      TrackHostExits,       NULL),
  VAR("TrackHostExitsExpire",INTERVAL, TrackHostExitsExpire, "30 minutes"),
126
  VAR("MapAddress",          LINELIST, AddressMap,           NULL),
127
  VAR("FascistFirewall",     BOOL,     FascistFirewall,      "0"),
128
  VAR("FirewallPorts",       CSV,      FirewallPorts,        "80,443"),
129
130
  VAR("MyFamily",            STRING,   MyFamily,             NULL),
  VAR("NodeFamily",          LINELIST, NodeFamilies,         NULL),
Roger Dingledine's avatar
Roger Dingledine committed
131
  VAR("NoPublish",           BOOL,     NoPublish,            "0"),
132
  VAR("Group",               STRING,   Group,                NULL),
133
  VAR("HashedControlPassword",STRING,  HashedControlPassword, NULL),
134
  VAR("HttpProxy",           STRING,   HttpProxy,            NULL),
135
  VAR("HttpProxyAuthenticator",STRING, HttpProxyAuthenticator,NULL),
136
  VAR("HttpsProxy",          STRING,   HttpsProxy,           NULL),
137
  VAR("HttpsProxyAuthenticator",STRING,HttpsProxyAuthenticator,NULL),
138
  VAR("HardwareAccel",       BOOL,     HardwareAccel,        "1"),
139
140
141
142
143
  VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines,    NULL),
  VAR("HiddenServiceDir",    LINELIST_S, RendConfigLines,    NULL),
  VAR("HiddenServicePort",   LINELIST_S, RendConfigLines,    NULL),
  VAR("HiddenServiceNodes",  LINELIST_S, RendConfigLines,    NULL),
  VAR("HiddenServiceExcludeNodes", LINELIST_S, RendConfigLines, NULL),
144
  VAR("IgnoreVersion",       BOOL,     IgnoreVersion,        "0"),
145
  VAR("KeepalivePeriod",     INTERVAL, KeepalivePeriod,      "5 minutes"),
146
147
148
  VAR("Log",                 LINELIST, Logs,                 NULL),
  VAR("LogLevel",            LINELIST_S, OldLogOptions,      NULL),
  VAR("LogFile",             LINELIST_S, OldLogOptions,      NULL),
149
  OBSOLETE("LinkPadding"),
150
  VAR("ConnLimit",           UINT,     ConnLimit,            "1024"),
151
  VAR("MaxOnionsPending",    UINT,     MaxOnionsPending,     "100"),
152
  VAR("MonthlyAccountingStart",UINT,   _MonthlyAccountingStart,"0"),
153
154
  VAR("AccountingMaxKB",     UINT,     _AccountingMaxKB,     "0"),
  VAR("AccountingMax",       MEMUNIT,   AccountingMax,        "0 bytes"),
155
  VAR("Nickname",            STRING,   Nickname,             NULL),
156
  VAR("NewCircuitPeriod",    INTERVAL, NewCircuitPeriod,     "30 seconds"),
157
  VAR("MaxCircuitDirtiness", INTERVAL, MaxCircuitDirtiness,  "10 minutes"),
158
159
160
161
162
  VAR("NumCpus",             UINT,     NumCpus,              "1"),
  VAR("ORPort",              UINT,     ORPort,               "0"),
  VAR("ORBindAddress",       LINELIST, ORBindAddress,        NULL),
  VAR("OutboundBindAddress", STRING,   OutboundBindAddress,  NULL),
  VAR("PidFile",             STRING,   PidFile,              NULL),
163
  VAR("LongLivedPorts",      CSV,      LongLivedPorts,       "21,22,706,1863,5050,5190,5222,5223,6667,8300,8888"),
164
165
  VAR("PathlenCoinWeight",   DOUBLE,   PathlenCoinWeight,    "0.3"),
  VAR("RedirectExit",        LINELIST, RedirectExit,         NULL),
166
  VAR("RephistTrackTime",    INTERVAL, RephistTrackTime,     "24 hours"),
167
  OBSOLETE("RouterFile"),
168
169
170
171
172
  VAR("RunAsDaemon",         BOOL,     RunAsDaemon,          "0"),
  VAR("RunTesting",          BOOL,     RunTesting,           "0"),
  VAR("RecommendedVersions", LINELIST, RecommendedVersions,  NULL),
  VAR("RendNodes",           STRING,   RendNodes,            NULL),
  VAR("RendExcludeNodes",    STRING,   RendExcludeNodes,     NULL),
173
  VAR("SafeLogging",         BOOL,     SafeLogging,          "1"),
174
  VAR("ShutdownWaitLength",  INTERVAL, ShutdownWaitLength,   "30 seconds"),
175
  VAR("SocksPort",           UINT,     SocksPort,            "9050"),
176
177
  VAR("SocksBindAddress",    LINELIST, SocksBindAddress,     NULL),
  VAR("SocksPolicy",         LINELIST, SocksPolicy,          NULL),
178
179
  /** DOCDOC */
  VAR("StatusFetchPeriod",   INTERVAL, StatusFetchPeriod,    "0 seconds"),
180
  VAR("SysLog",              LINELIST_S, OldLogOptions,      NULL),
181
  OBSOLETE("TrafficShaping"),
182
  VAR("User",                STRING,   User,                 NULL),
183
184
  VAR("UseHelperNodes",      BOOL,     UseHelperNodes,       "0"),
  VAR("NumHelperNodes",      UINT,     NumHelperNodes,       "3"),
185
  VAR("__LeaveStreamsUnattached", BOOL,LeaveStreamsUnattached, "0"),
186
  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
187
188
189
190
};
#undef VAR
#undef OBSOLETE

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
typedef int (*validate_fn_t)(void*);

typedef struct {
  size_t size;
  uint32_t magic;
  off_t magic_offset;
  config_abbrev_t *abbrevs;
  config_var_t *vars;
  validate_fn_t validate_fn;
} config_format_t;

#define CHECK(fmt, cfg) do {                                            \
    tor_assert(fmt && cfg);                                             \
    tor_assert((fmt)->magic == *(uint32_t*)(((char*)(cfg))+fmt->magic_offset)); \
  } while (0)

Nick Mathewson's avatar
Nick Mathewson committed
207
/** Largest allowed config line */
208
#define CONFIG_LINE_T_MAXLEN 4096
209

210
static void config_line_append(config_line_t **lst,
211
                               const char *key, const char *val);
212
213
214
215
216
217
static void option_reset(config_format_t *fmt, or_options_t *options,
                         config_var_t *var);
static void options_free(config_format_t *fmt, or_options_t *options);
static int option_is_same(config_format_t *fmt,
                          or_options_t *o1, or_options_t *o2,const char *name);
static or_options_t *options_dup(config_format_t *fmt, or_options_t *old);
218
219
220
static int options_validate(or_options_t *options);
static int options_transition_allowed(or_options_t *old, or_options_t *new);
static int check_nickname_list(const char *lst, const char *name);
221
static void config_register_addressmaps(or_options_t *options);
222

223
static int parse_dir_server_line(const char *line, int validate_only);
224
static int parse_redirect_line(smartlist_t *result,
225
                               config_line_t *line);
226
227
228
static int parse_log_severity_range(const char *range, int *min_out,
                                    int *max_out);
static int convert_log_option(or_options_t *options,
229
230
                              config_line_t *level_opt,
                              config_line_t *file_opt, int isDaemon);
231
232
233
234
static int add_single_log_option(or_options_t *options, int minSeverity,
                                 int maxSeverity,
                                 const char *type, const char *fname);
static int normalize_log_options(or_options_t *options);
235
static int validate_data_directory(or_options_t *options);
236
static int write_configuration_file(const char *fname, or_options_t *options);
237
static config_line_t *get_assigned_option(config_format_t *fmt,
238
239
                                     or_options_t *options, const char *key);
static void config_init(config_format_t *fmt, or_options_t *options);
240

241
242
static uint64_t config_parse_memunit(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
243
static void print_cvs_version(void);
244
static int init_libevent(void);
245
#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
246
static void check_libevent_version(const char *m, const char *v, int server);
247
#endif
248

249
250
251
252
253
254
255
256
257
258
259
#define OR_OPTIONS_MAGIC 9090909

static config_format_t config_format = {
  sizeof(or_options_t),
  OR_OPTIONS_MAGIC,
  STRUCT_OFFSET(or_options_t, _magic),
  _config_abbrevs,
  _config_vars,
  (validate_fn_t)options_validate
};

260
261
262
263
264
265
/*
 * Functions to read and write the global options pointer.
 */

/** Command-line and config-file options. */
static or_options_t *global_options=NULL;
Roger Dingledine's avatar
Roger Dingledine committed
266
/** Name of most recently read torrc file. */
267
static char *config_fname = NULL;
268

269
270
271
272
273
274
275
276
277
static void *
config_alloc(config_format_t *fmt)
{
  void *opts = opts = tor_malloc_zero(fmt->size);
  *(uint32_t*)(((char*)opts)+fmt->magic_offset) = fmt->magic;
  CHECK(fmt, opts);
  return opts;
}

278
279
/** Return the currently configured options. */
or_options_t *
280
281
get_options(void)
{
282
283
284
  tor_assert(global_options);
  return global_options;
}
285

Roger Dingledine's avatar
Roger Dingledine committed
286
/** Change the current global options to contain <b>new_val</b> instead
287
 * of their current value; free the old value as necessary.
288
289
 */
void
290
291
set_options(or_options_t *new_val)
{
292
  if (global_options)
293
    options_free(&config_format, global_options);
294
  global_options = new_val;
295
296
}

297
298
299
void
config_free_all(void)
{
300
  options_free(&config_format, global_options);
301
302
303
  tor_free(config_fname);
}

304
305
306
307
/** If options->SafeLogging is on, return a not very useful string,
 * else return address.
 */
const char *
308
309
safe_str(const char *address)
{
310
311
312
313
314
315
  if (get_options()->SafeLogging)
    return "[scrubbed]";
  else
    return address;
}

316
317
318
/** Fetch the active option list, and take actions based on it. All
 * of the things we do should survive being done repeatedly.
 * Return 0 if all goes well, return -1 if it's time to die.
319
320
321
322
323
324
 *
 * Note 1: <b>new_val</b> must have previously been validated with
 * options_validate(), or Tor may freak out and exit.
 *
 * Note 2: We haven't moved all the "act on new configuration" logic
 * here yet.  Some is still in do_hup() and other places.
325
326
 */
int
327
328
options_act(void)
{
329
  config_line_t *cl;
330
  or_options_t *options = get_options();
331
332
333
  static int libevent_initialized = 0;

  if (options->RunAsDaemon) {
334
    start_daemon();
335
  }
336
337

  clear_trusted_dir_servers();
338
  for (cl = options->DirServers; cl; cl = cl->next) {
339
340
    if (parse_dir_server_line(cl->value, 0)<0) {
      log_fn(LOG_ERR,
341
             "Bug: Previously validated DirServer line could not be added!");
342
      return -1;
343
344
345
    }
  }

346
  if (rend_config_services(options, 0)<0) {
347
    log_fn(LOG_ERR,
348
           "Bug: Previously validated hidden services line could not be added!");
349
    return -1;
350
  }
351

352
353
354
  if (options->EntryNodes && strlen(options->EntryNodes))
    options->UseHelperNodes = 0;

355
  /* Setuid/setgid as appropriate */
356
357
  if (options->User || options->Group) {
    if (switch_id(options->User, options->Group) != 0) {
358
359
360
361
362
      return -1;
    }
  }

  /* Ensure data directory is private; create if possible. */
363
  if (check_private_dir(options->DataDirectory, CPD_CREATE) != 0) {
364
    log_fn(LOG_ERR, "Couldn't access/create private data directory %s",
365
           options->DataDirectory);
366
367
368
369
370
371
372
373
    return -1;
  }

  /* Bail out at this point if we're not going to be a server: we want
   * to not fork, and to log stuff to stderr. */
  if (options->command != CMD_RUN_TOR)
    return 0;

374
  mark_logs_temp(); /* Close current logs once new logs are open. */
375
  if (options_init_logs(options, 0)<0) /* Configure the log(s) */
376
    return -1;
377

378
379
380
  /* Close the temporary log we used while starting up, if it isn't already
   * gone. */
  close_temp_logs();
381
  add_callback_log(LOG_ERR, LOG_ERR, control_event_logmsg);
382
  control_adjust_event_log_severity();
383

384
385
386
387
388
389
390
  /* Set up libevent. */
  if (!libevent_initialized) {
    if (init_libevent())
      return -1;
    libevent_initialized = 1;
  }

391
  options->_ConnLimit =
392
    set_max_file_descriptors((unsigned)options->ConnLimit, MAXCONNECTIONS);
393
  if (options->_ConnLimit < 0)
394
395
    return -1;

396
397
398
399
400
401
402
403
404
  {
    smartlist_t *sl = smartlist_create();
    for (cl = options->RedirectExit; cl; cl = cl->next) {
      if (parse_redirect_line(sl, cl)<0)
        return -1;
    }
    set_exit_redirects(sl);
  }

405
  /* Finish backgrounding the process */
406
  if (options->RunAsDaemon) {
407
    /* We may be calling this for the n'th time (on SIGHUP), but it's safe. */
408
    finish_daemon(options->DataDirectory);
409
410
411
412
  }

  /* Write our pid to the pid file. If we do not have write permissions we
   * will log a warning */
413
  if (options->PidFile)
414
415
    write_pidfile(options->PidFile);

416
417
418
  /* Register addressmap directives */
  config_register_addressmaps(options);

419
420
421
422
  /* Update address policies. */
  parse_socks_policy();
  parse_dir_policy();

423
424
  init_cookie_authentication(options->CookieAuthentication);

425
426
  /* reload keys as needed for rendezvous services. */
  if (rend_service_load_keys()<0) {
427
    log_fn(LOG_ERR,"Error loading rendezvous service keys");
428
429
430
    return -1;
  }

431
  /* Set up accounting */
432
  if (accounting_parse_options(options, 0)<0) {
433
    log_fn(LOG_ERR,"Error in accounting options");
434
435
    return -1;
  }
436
  if (accounting_is_enabled(options))
437
438
    configure_accounting(time(NULL));

439
  if (!we_are_hibernating() && retry_all_listeners(0) < 0) {
440
441
442
443
    log_fn(LOG_ERR,"Failed to bind one of the listener ports.");
    return -1;
  }

444
445
446
447
448
449
  /* Since our options changed, we might need to regenerate and upload our
   * server descriptor.  (We could probably be more clever about only calling
   * this when something significant changed.)
   */
  mark_my_descriptor_dirty();

450
  return 0;
451
452
453
454
455
456
}

/*
 * Functions to parse config options
 */

Nick Mathewson's avatar
Nick Mathewson committed
457
/** If <b>option</b> is an official abbreviation for a longer option,
458
459
460
 * return the longer option.  Otherwise return <b>option</b>.
 * If <b>command_line</b> is set, apply all abbreviations.  Otherwise, only
 * apply abbreviations that work for the config file and the command line. */
461
static const char *
462
expand_abbrev(config_format_t *fmt, const char *option, int command_line)
463
464
{
  int i;
465
466
467
  if (! fmt->abbrevs)
    return option;
  for (i=0; fmt->abbrevs[i].abbreviated; ++i) {
468
    /* Abbreviations aren't casei. */
469
470
471
    if (!strcasecmp(option,fmt->abbrevs[i].abbreviated) &&
        (command_line || !fmt->abbrevs[i].commandline_only)) {
      return fmt->abbrevs[i].full;
472
    }
473
474
475
  }
  return option;
}
Nick Mathewson's avatar
Nick Mathewson committed
476

Nick Mathewson's avatar
Nick Mathewson committed
477
/** Helper: Read a list of configuration options from the command line. */
478
static config_line_t *
479
480
config_get_commandlines(int argc, char **argv)
{
481
482
  config_line_t *front = NULL;
  config_line_t **new = &front;
483
484
485
  char *s;
  int i = 1;

486
  while (i < argc-1) {
487
488
489
    if (!strcmp(argv[i],"-f") ||
        !strcmp(argv[i],"--hash-password")) {
      i += 2; /* command-line option with argument. ignore them. */
490
      continue;
491
492
    } else if (!strcmp(argv[i],"--list-fingerprint")) {
      i += 1; /* command-line option. ignore it. */
493
      continue;
Nick Mathewson's avatar
Nick Mathewson committed
494
495
496
    } else if (!strcmp(argv[i],"--nt-service")) {
      i += 1;
      continue;
497
498
    }

499
    *new = tor_malloc_zero(sizeof(config_line_t));
500
    s = argv[i];
501

502
    while (*s == '-')
503
      s++;
504

505
    (*new)->key = tor_strdup(expand_abbrev(&config_format, s, 1));
506
507
    (*new)->value = tor_strdup(argv[i+1]);
    (*new)->next = NULL;
508
    log(LOG_DEBUG,"Commandline: parsed keyword '%s', value '%s'",
509
510
511
        (*new)->key, (*new)->value);

    new = &((*new)->next);
512
513
514
515
516
    i += 2;
  }
  return front;
}

Nick Mathewson's avatar
Nick Mathewson committed
517
/** Helper: allocate a new configuration option mapping 'key' to 'val',
518
519
 * append it to *<b>lst</b>. */
static void
520
config_line_append(config_line_t **lst,
521
522
                   const char *key,
                   const char *val)
523
{
524
  config_line_t *newline;
525

526
  newline = tor_malloc(sizeof(config_line_t));
527
528
  newline->key = tor_strdup(key);
  newline->value = tor_strdup(val);
529
530
531
532
533
  newline->next = NULL;
  while (*lst)
    lst = &((*lst)->next);

  (*lst) = newline;
534
535
}

536
537
/** Helper: parse the config string and strdup into key/value
 * strings. Set *result to the list, or NULL if parsing the string
538
539
 * failed.  Return 0 on success, -1 on failure. Warn and ignore any
 * misformatted lines. */
540
int
541
config_get_lines(char *string, config_line_t **result)
542
{
543
  config_line_t *list = NULL, **next;
544
  char *k, *v;
545

546
  next = &list;
547
548
549
550
551
552
  do {
    string = parse_line_from_str(string, &k, &v);
    if (!string) {
      config_free_lines(list);
      return -1;
    }
553
554
555
556
    if (k && v) {
      /* This list can get long, so we keep a pointer to the end of it
       * rather than using config_line_append over and over and getting n^2
       * performance.  This is the only really long list. */
557
      *next = tor_malloc(sizeof(config_line_t));
558
559
560
561
562
      (*next)->key = tor_strdup(k);
      (*next)->value = tor_strdup(v);
      (*next)->next = NULL;
      next = &((*next)->next);
    }
563
  } while (*string);
564

565
  *result = list;
566
  return 0;
Roger Dingledine's avatar
Roger Dingledine committed
567
568
}

Nick Mathewson's avatar
Nick Mathewson committed
569
570
571
/**
 * Free all the configuration lines on the linked list <b>front</b>.
 */
572
void
573
config_free_lines(config_line_t *front)
574
{
575
  config_line_t *tmp;
576

577
  while (front) {
578
579
580
    tmp = front;
    front = tmp->next;

Roger Dingledine's avatar
Roger Dingledine committed
581
582
583
    tor_free(tmp->key);
    tor_free(tmp->value);
    tor_free(tmp);
584
585
586
  }
}

Nick Mathewson's avatar
Nick Mathewson committed
587
588
589
590
/** If <b>key</b> is a configuration option, return the corresponding
 * config_var_t.  Otherwise, if <b>key</b> is a non-standard abbreviation,
 * warn, and return the corresponding config_var_t.  Otherwise return NULL.
 */
591
static config_var_t *
592
config_find_option(config_format_t *fmt, const char *key)
593
594
{
  int i;
595
  size_t keylen = strlen(key);
596
  if (!keylen)
597
    return NULL; /* if they say "--" on the commandline, it's not an option */
Nick Mathewson's avatar
Nick Mathewson committed
598
  /* First, check for an exact (case-insensitive) match */
599
600
601
  for (i=0; fmt->vars[i].name; ++i) {
    if (!strcasecmp(key, fmt->vars[i].name))
      return &fmt->vars[i];
Nick Mathewson's avatar
Nick Mathewson committed
602
603
  }
  /* If none, check for an abbreviated match */
604
605
  for (i=0; fmt->vars[i].name; ++i) {
    if (!strncasecmp(key, fmt->vars[i].name, keylen)) {
606
607
      log_fn(LOG_WARN, "The abbreviation '%s' is deprecated. "
          "Tell Nick and Roger to make it official, or just use '%s' instead",
608
609
             key, fmt->vars[i].name);
      return &fmt->vars[i];
610
611
    }
  }
612
  /* Okay, unrecognized options */
613
614
615
  return NULL;
}

Nick Mathewson's avatar
Nick Mathewson committed
616
/** If <b>c</b> is a syntactically valid configuration line, update
617
618
 * <b>options</b> with its value and return 0.  Otherwise return -1 for bad key,
 * -2 for bad value.
619
620
621
622
 *
 * If 'reset' is set, and we get a line containing no value, restore the
 * option to its default value.
 */
623
static int
624
config_assign_line(config_format_t *fmt,
625
                   or_options_t *options, config_line_t *c, int reset)
626
{
627
  int i, ok;
628
629
  config_var_t *var;
  void *lvalue;
630

631
632
633
  CHECK(fmt, options);

  var = config_find_option(fmt, c->key);
634
635
636
637
638
639
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", c->key);
    return -1;
  }
  /* Put keyword into canonical case. */
  if (strcmp(var->name, c->key)) {
640
    tor_free(c->key);
641
    c->key = tor_strdup(var->name);
642
643
  }

644
  if (reset && !strlen(c->value)) {
645
    option_reset(fmt, options, var);
646
647
648
    return 0;
  }

Nick Mathewson's avatar
Nick Mathewson committed
649
  lvalue = ((char*)options) + var->var_offset;
650
  switch (var->type) {
651

652
653
654
  case CONFIG_TYPE_UINT:
    i = tor_parse_long(c->value, 10, 0, INT_MAX, &ok, NULL);
    if (!ok) {
655
      log(LOG_WARN, "Int keyword '%s %s' is malformed or out of bounds.",
656
          c->key,c->value);
657
      return -2;
658
    }
659
    *(int *)lvalue = i;
660
661
    break;

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  case CONFIG_TYPE_INTERVAL: {
    i = config_parse_interval(c->value, &ok);
    if (!ok) {
      return -2;
    }
    *(int *)lvalue = i;
    break;
  }

  case CONFIG_TYPE_MEMUNIT: {
    uint64_t u64 = config_parse_memunit(c->value, &ok);
    if (!ok) {
      return -2;
    }
    *(uint64_t *)lvalue = u64;
    break;
  }

680
681
682
  case CONFIG_TYPE_BOOL:
    i = tor_parse_long(c->value, 10, 0, 1, &ok, NULL);
    if (!ok) {
683
      log(LOG_WARN, "Boolean keyword '%s' expects 0 or 1.", c->key);
684
      return -2;
685
    }
686
    *(int *)lvalue = i;
687
688
689
    break;

  case CONFIG_TYPE_STRING:
690
691
    tor_free(*(char **)lvalue);
    *(char **)lvalue = tor_strdup(c->value);
692
693
694
    break;

  case CONFIG_TYPE_DOUBLE:
695
    *(double *)lvalue = atof(c->value);
696
697
698
    break;

  case CONFIG_TYPE_CSV:
699
700
701
702
    if (*(smartlist_t**)lvalue) {
      SMARTLIST_FOREACH(*(smartlist_t**)lvalue, char *, cp, tor_free(cp));
      smartlist_clear(*(smartlist_t**)lvalue);
    } else {
703
      *(smartlist_t**)lvalue = smartlist_create();
704
    }
705

706
    smartlist_split_string(*(smartlist_t**)lvalue, c->value, ",",
707
708
709
                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
    break;

710
711
  case CONFIG_TYPE_LINELIST:
  case CONFIG_TYPE_LINELIST_S:
712
    config_line_append((config_line_t**)lvalue, c->key, c->value);
713
    break;
714
715
716
717

  case CONFIG_TYPE_OBSOLETE:
    log_fn(LOG_WARN, "Skipping obsolete configuration option '%s'", c->key);
    break;
718
719
  case CONFIG_TYPE_LINELIST_V:
    log_fn(LOG_WARN, "Can't provide value for virtual option '%s'", c->key);
720
    return -2;
721
722
723
  default:
    tor_assert(0);
    break;
724
  }
725
  return 0;
726
727
}

728
729
/** restore the option named <b>key</b> in options to its default value. */
static void
730
config_reset_line(config_format_t *fmt, or_options_t *options, const char *key)
731
732
733
{
  config_var_t *var;

734
735
736
  CHECK(fmt, options);

  var = config_find_option(fmt, key);
737
738
739
  if (!var)
    return; /* give error on next pass. */

740
  option_reset(fmt, options, var);
741
742
}

743
744
/** Return true iff key is a valid configuration option. */
int
745
option_is_recognized(const char *key)
746
{
747
  config_var_t *var = config_find_option(&config_format, key);
748
749
750
  return (var != NULL);
}

751
752
/** Return the canonical name of a configuration option. */
const char *
753
option_get_canonical_name(const char *key)
754
{
755
  config_var_t *var = config_find_option(&config_format, key);
756
757
758
  return var->name;
}

759
760
/** Return a canonicalized list of the options assigned for key.
 */
761
config_line_t *
762
option_get_assignment(or_options_t *options, const char *key)
763
764
765
766
{
  return get_assigned_option(&config_format, options, key);
}

767
static config_line_t *
768
get_assigned_option(config_format_t *fmt, or_options_t *options, const char *key)
769
770
771
772
{
  config_var_t *var;
  const void *value;
  char buf[32];
773
  config_line_t *result;
774
  tor_assert(options && key);
775

776
777
778
  CHECK(fmt, options);

  var = config_find_option(fmt, key);
779
780
781
  if (!var) {
    log_fn(LOG_WARN, "Unknown option '%s'.  Failing.", key);
    return NULL;
782
783
784
  } else if (var->type == CONFIG_TYPE_LINELIST_S) {
    log_fn(LOG_WARN, "Can't return context-sensitive '%s' on its own", key);
    return NULL;
785
786
787
  }
  value = ((char*)options) + var->var_offset;

788
789
  if (var->type == CONFIG_TYPE_LINELIST ||
      var->type == CONFIG_TYPE_LINELIST_V) {
790
    /* Linelist requires special handling: we just copy and return it. */
791
792
    const config_line_t *next_in = *(const config_line_t**)value;
    config_line_t **next_out = &result;
793
    while (next_in) {
794
      *next_out = tor_malloc(sizeof(config_line_t));
795
796
797
798
799
800
801
802
803
      (*next_out)->key = tor_strdup(next_in->key);
      (*next_out)->value = tor_strdup(next_in->value);
      next_in = next_in->next;
      next_out = &((*next_out)->next);
    }
    (*next_out) = NULL;
    return result;
  }

804
  result = tor_malloc_zero(sizeof(config_line_t));
805
  result->key = tor_strdup(var->name);
806
  switch (var->type)
807
808
    {
    case CONFIG_TYPE_STRING:
809
810
811
812
813
814
815
      if (*(char**)value) {
        result->value = tor_strdup(*(char**)value);
      } else {
        tor_free(result->key);
        tor_free(result);
        return NULL;
      }
816
      break;
817
    case CONFIG_TYPE_INTERVAL:
818
    case CONFIG_TYPE_UINT:
819
      /* This means every or_options_t uint or bool element
820
       * needs to be an int. Not, say, a uint16_t or char. */
821
822
823
      tor_snprintf(buf, sizeof(buf), "%d", *(int*)value);
      result->value = tor_strdup(buf);
      break;
824
825
826
827
828
    case CONFIG_TYPE_MEMUNIT:
      tor_snprintf(buf, sizeof(buf), U64_FORMAT,
                   U64_PRINTF_ARG(*(uint64_t*)value));
      result->value = tor_strdup(buf);
      break;
829
830
831
832
833
834
835
836
    case CONFIG_TYPE_DOUBLE:
      tor_snprintf(buf, sizeof(buf), "%f", *(double*)value);
      result->value = tor_strdup(buf);
      break;
    case CONFIG_TYPE_BOOL:
      result->value = tor_strdup(*(int*)value ? "1" : "0");
      break;
    case CONFIG_TYPE_CSV:
Roger Dingledine's avatar
Roger Dingledine committed
837
838
      if (*(smartlist_t**)value)
        result->value = smartlist_join_strings(*(smartlist_t**)value,",",0,NULL);
839
840
841
      else
        result->value = tor_strdup("");
      break;
842
843
844
845
846
    case CONFIG_TYPE_OBSOLETE:
      log_fn(LOG_WARN,"You asked me for the value of an obsolete config option %s.", key);
      tor_free(result->key);
      tor_free(result);
      return NULL;
847
848
849
    default:
      tor_free(result->key);
      tor_free(result);
850
      log_fn(LOG_WARN,"Bug: unknown type %d for known key %s", var->type, key);
851
852
853
854
855
856
      return NULL;
    }

  return result;
}

857
/** Iterate through the linked list of requested options <b>list</b>.
Nick Mathewson's avatar
Nick Mathewson committed
858
 * For each item, convert as appropriate and assign to <b>options</b>.
859
 * If an item is unrecognized, return -1 immediately,
860
861
862
863
 * else return 0 for success.
 *
 * If <b>reset</b>, then interpret empty lines as meaning "restore to
 * default value", and interpret LINELIST* options as replacing (not
864
865
 * extending) their previous values.  Return 0 on success, -1 on bad key,
 * -2 on bad value.
866
 */
867
static int
868
config_assign(config_format_t *fmt,
869
              or_options_t *options, config_line_t *list, int reset)
870
{
871
  config_line_t *p;
872
873

  CHECK(fmt, options);
874
875
876

  /* pass 1: normalize keys */
  for (p = list; p; p = p->next) {
877
    const char *full = expand_abbrev(fmt, p->key, 0);
878
879
880
    if (strcmp(full,p->key)) {
      tor_free(p->key);
      p->key = tor_strdup(full);
881
    }
882
883
  }