tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_confmgr.c (13238B)


      1 /* Copyright (c) 2001-2004, Roger Dingledine.
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 /*
      7 * Tests for confmgt.c's features that support multiple configuration
      8 * formats and configuration objects.
      9 */
     10 
     11 #define CONFMGT_PRIVATE
     12 #include "orconfig.h"
     13 
     14 #include "core/or/or.h"
     15 #include "lib/encoding/confline.h"
     16 #include "lib/confmgt/confmgt.h"
     17 #include "test/test.h"
     18 #include "test/log_test_helpers.h"
     19 
     20 /*
     21 * Set up a few objects: a pasture_cfg is toplevel; it has a llama_cfg and an
     22 * alpaca_cfg.
     23 */
     24 
     25 typedef struct {
     26  uint32_t magic;
     27  char *address;
     28  int opentopublic;
     29  config_suite_t *subobjs;
     30 } pasture_cfg_t;
     31 
     32 typedef struct {
     33  char *llamaname;
     34  int cuteness;
     35  uint32_t magic;
     36  int eats_meat; /* deprecated; llamas are never carnivorous. */
     37 
     38  char *description; // derived from other fields.
     39 } llama_cfg_t;
     40 
     41 typedef struct {
     42  uint32_t magic;
     43  int fuzziness;
     44  char *alpacaname;
     45  int n_wings; /* deprecated; alpacas don't have wings. */
     46 
     47  int square_fuzziness; /* Derived from fuzziness. */
     48 } alpaca_cfg_t;
     49 
     50 /*
     51 * Make the above into configuration objects.
     52 */
     53 
     54 static pasture_cfg_t pasture_cfg_t_dummy;
     55 static llama_cfg_t llama_cfg_t_dummy;
     56 static alpaca_cfg_t alpaca_cfg_t_dummy;
     57 
     58 #define PV(name, type, dflt) \
     59  CONFIG_VAR_ETYPE(pasture_cfg_t, #name, type, name, 0, dflt)
     60 #define LV(name, type, dflt) \
     61  CONFIG_VAR_ETYPE(llama_cfg_t, #name, type, name, 0, dflt)
     62 #define AV(name, type, dflt) \
     63  CONFIG_VAR_ETYPE(alpaca_cfg_t, #name, type, name, 0, dflt)
     64 static const config_var_t pasture_vars[] = {
     65  PV(address, STRING, NULL),
     66  PV(opentopublic, BOOL, "1"),
     67  END_OF_CONFIG_VARS
     68 };
     69 static const config_var_t llama_vars[] =
     70 {
     71  LV(llamaname, STRING, NULL),
     72  LV(eats_meat, BOOL, NULL),
     73  LV(cuteness, POSINT, "100"),
     74  END_OF_CONFIG_VARS
     75 };
     76 static const config_var_t alpaca_vars[] =
     77 {
     78  AV(alpacaname, STRING, NULL),
     79  AV(fuzziness, POSINT, "50"),
     80  AV(n_wings, POSINT, "0"),
     81  END_OF_CONFIG_VARS
     82 };
     83 
     84 static config_deprecation_t llama_deprecations[] = {
     85  { "eats_meat", "Llamas are herbivores." },
     86  {NULL,NULL}
     87 };
     88 
     89 static config_deprecation_t alpaca_deprecations[] = {
     90  { "n_wings", "Alpacas are quadrupeds." },
     91  {NULL,NULL}
     92 };
     93 
     94 static int clear_llama_cfg_called = 0;
     95 static void
     96 clear_llama_cfg(const config_mgr_t *mgr, void *llamacfg)
     97 {
     98  (void)mgr;
     99  llama_cfg_t *lc = llamacfg;
    100  tor_free(lc->description);
    101  ++clear_llama_cfg_called;
    102 }
    103 
    104 static config_abbrev_t llama_abbrevs[] = {
    105  { "gracia", "cuteness", 0, 0 },
    106  { "gentillesse", "cuteness", 0, 0 },
    107  { NULL, NULL, 0, 0 },
    108 };
    109 
    110 static int
    111 legacy_validate_pasture(const void *old_, void *obj, char **msg_out)
    112 {
    113  const pasture_cfg_t *old = old_;
    114  pasture_cfg_t *p = obj;
    115 
    116  // llamas can't find their way home if the letters are lowercase.
    117  if (p->address)
    118    tor_strupper(p->address);
    119 
    120  if (old && old->address &&
    121      (!p->address || strcmp(old->address, p->address))) {
    122    *msg_out = tor_strdup("You can't move a pasture.");
    123    return -1;
    124  }
    125 
    126  return 0;
    127 }
    128 
    129 static int
    130 validate_llama(const void *obj, char **msg_out)
    131 {
    132  const llama_cfg_t *llama = obj;
    133  tor_assert(llama->magic == 0x11aa11);
    134 
    135  if (! llama->llamaname || strlen(llama->llamaname) == 0) {
    136    *msg_out = tor_strdup("A llama has no name!?");
    137    return -1;
    138  }
    139 
    140  if (strspn(llama->llamaname, "0123456789") == strlen(llama->llamaname)) {
    141    *msg_out = tor_strdup("It is not a number; it is a free llama!");
    142    return -1;
    143  }
    144 
    145  return 0;
    146 }
    147 
    148 static int
    149 check_transition_alpaca(const void *old_, const void *new_, char **msg_out)
    150 {
    151  const alpaca_cfg_t *old_alpaca = old_;
    152  const alpaca_cfg_t *new_alpaca = new_;
    153 
    154  tor_assert(old_alpaca && new_alpaca);
    155  tor_assert(old_alpaca->magic == 0xa15aca);
    156  tor_assert(new_alpaca->magic == 0xa15aca);
    157 
    158  if (old_alpaca->fuzziness > new_alpaca->fuzziness) {
    159    *msg_out = tor_strdup("An alpaca only becomes more fuzzy over time.");
    160    return -1;
    161  }
    162 
    163  return 0;
    164 }
    165 
    166 static int
    167 post_normalize_llama(void *obj, char **msg_out)
    168 {
    169  (void)msg_out;
    170  llama_cfg_t *llama = obj;
    171  tor_assert(llama->magic == 0x11aa11);
    172  tor_assert(llama->llamaname); // we have already checked for a NULL name.
    173  tor_free(llama->description);
    174  tor_asprintf(&llama->description, "A llama called %s.", llama->llamaname);
    175  return 0;
    176 }
    177 
    178 static int
    179 pre_normalize_alpaca(void *obj, char **msg_out)
    180 {
    181  (void)msg_out;
    182  alpaca_cfg_t *alpaca = obj;
    183  tor_assert(alpaca->magic == 0xa15aca);
    184  alpaca->square_fuzziness = alpaca->fuzziness * alpaca->fuzziness;
    185  return 0;
    186 }
    187 
    188 static const config_format_t pasture_fmt = {
    189  sizeof(pasture_cfg_t),
    190  {
    191    "pasture_cfg_t",
    192    8989,
    193    offsetof(pasture_cfg_t, magic)
    194  },
    195  .vars = pasture_vars,
    196  .has_config_suite = true,
    197  .config_suite_offset = offsetof(pasture_cfg_t, subobjs),
    198  .legacy_validate_fn = legacy_validate_pasture,
    199 };
    200 
    201 static const config_format_t llama_fmt = {
    202  sizeof(llama_cfg_t),
    203  {
    204    "llama_cfg_t",
    205    0x11aa11,
    206    offsetof(llama_cfg_t, magic)
    207  },
    208  .vars = llama_vars,
    209  .deprecations = llama_deprecations,
    210  .abbrevs = llama_abbrevs,
    211  .clear_fn = clear_llama_cfg,
    212  .validate_fn = validate_llama,
    213  .post_normalize_fn = post_normalize_llama,
    214 };
    215 
    216 static const config_format_t alpaca_fmt = {
    217  sizeof(alpaca_cfg_t),
    218  {
    219    "alpaca_cfg_t",
    220    0xa15aca,
    221    offsetof(alpaca_cfg_t, magic)
    222  },
    223  .vars = alpaca_vars,
    224  .deprecations = alpaca_deprecations,
    225  .pre_normalize_fn = pre_normalize_alpaca,
    226  .check_transition_fn = check_transition_alpaca,
    227 };
    228 
    229 #define LLAMA_IDX 0
    230 #define ALPACA_IDX 1
    231 
    232 static config_mgr_t *
    233 get_mgr(bool freeze)
    234 {
    235  config_mgr_t *mgr = config_mgr_new(&pasture_fmt);
    236  tt_int_op(LLAMA_IDX, OP_EQ, config_mgr_add_format(mgr, &llama_fmt));
    237  tt_int_op(ALPACA_IDX, OP_EQ, config_mgr_add_format(mgr, &alpaca_fmt));
    238  if (freeze)
    239    config_mgr_freeze(mgr);
    240  return mgr;
    241 
    242 done:
    243  config_mgr_free(mgr);
    244  return NULL;
    245 }
    246 
    247 static void
    248 test_confmgr_init(void *arg)
    249 {
    250  (void)arg;
    251  config_mgr_t *mgr = get_mgr(true);
    252  smartlist_t *vars = NULL;
    253  tt_ptr_op(mgr, OP_NE, NULL);
    254 
    255  vars = config_mgr_list_vars(mgr);
    256  tt_int_op(smartlist_len(vars), OP_EQ, 8); // 8 vars total.
    257 
    258  tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "CUTENESS"));
    259  tt_str_op("cuteness", OP_EQ, config_find_option_name(mgr, "GRACIA"));
    260  smartlist_free(vars);
    261 
    262  vars = config_mgr_list_deprecated_vars(mgr); // 2 deprecated vars.
    263  tt_int_op(smartlist_len(vars), OP_EQ, 2);
    264  tt_assert(smartlist_contains_string(vars, "eats_meat"));
    265  tt_assert(smartlist_contains_string(vars, "n_wings"));
    266 
    267  tt_str_op("Llamas are herbivores.", OP_EQ,
    268            config_find_deprecation(mgr, "EATS_MEAT"));
    269  tt_str_op("Alpacas are quadrupeds.", OP_EQ,
    270            config_find_deprecation(mgr, "N_WINGS"));
    271 
    272 done:
    273  smartlist_free(vars);
    274  config_mgr_free(mgr);
    275 }
    276 
    277 static void
    278 test_confmgr_magic(void *args)
    279 {
    280  (void)args;
    281  // Every time we build a manager, it is supposed to get a different magic
    282  // number.  Let's test that.
    283  config_mgr_t *mgr1 = get_mgr(true);
    284  config_mgr_t *mgr2 = get_mgr(true);
    285  config_mgr_t *mgr3 = get_mgr(true);
    286 
    287  pasture_cfg_t *p1 = NULL, *p2 = NULL, *p3 = NULL;
    288 
    289  tt_assert(mgr1);
    290  tt_assert(mgr2);
    291  tt_assert(mgr3);
    292 
    293  p1 = config_new(mgr1);
    294  p2 = config_new(mgr2);
    295  p3 = config_new(mgr3);
    296 
    297  tt_assert(p1);
    298  tt_assert(p2);
    299  tt_assert(p3);
    300 
    301  // By chance, two managers get the same magic with P=2^-32.  Let's
    302  // make sure that at least two of them are different, so that our
    303  // odds of a false positive are 1/2^-64.
    304  tt_assert((p1->magic != p2->magic) || (p2->magic != p3->magic));
    305 
    306 done:
    307  config_free(mgr1, p1);
    308  config_free(mgr2, p2);
    309  config_free(mgr3, p3);
    310 
    311  config_mgr_free(mgr1);
    312  config_mgr_free(mgr2);
    313  config_mgr_free(mgr3);
    314 }
    315 
    316 static const char *simple_pasture =
    317  "LLamaname hugo\n"
    318  "Alpacaname daphne\n"
    319  "gentillesse 42\n"
    320  "address 123 Camelid ave\n";
    321 
    322 static void
    323 test_confmgr_parse(void *arg)
    324 {
    325  (void)arg;
    326  config_mgr_t *mgr = get_mgr(true);
    327  pasture_cfg_t *p = config_new(mgr);
    328  config_line_t *lines = NULL;
    329  char *msg = NULL;
    330 
    331  config_init(mgr, p); // set defaults.
    332 
    333  int r = config_get_lines(simple_pasture, &lines, 0);
    334  tt_int_op(r, OP_EQ, 0);
    335  r = config_assign(mgr, p, lines, 0, &msg);
    336  tt_int_op(r, OP_EQ, 0);
    337 
    338  tt_int_op(p->opentopublic, OP_EQ, 1);
    339  tt_str_op(p->address, OP_EQ, "123 Camelid ave");
    340 
    341  // We are using this API directly; modules outside confparse will, in the
    342  // future, not.
    343  const alpaca_cfg_t *ac = config_mgr_get_obj(mgr, p, ALPACA_IDX);
    344  const llama_cfg_t *lc = config_mgr_get_obj(mgr, p, LLAMA_IDX);
    345  tt_str_op(lc->llamaname, OP_EQ, "hugo");
    346  tt_str_op(ac->alpacaname, OP_EQ, "daphne");
    347  tt_int_op(lc->cuteness, OP_EQ, 42);
    348  tt_int_op(ac->fuzziness, OP_EQ, 50);
    349 
    350  // We set the description for the llama here, so that the clear function
    351  // can clear it.  (Later we can do this in a verification function.)
    352  clear_llama_cfg_called = 0;
    353  llama_cfg_t *mut_lc = config_mgr_get_obj_mutable(mgr, p, LLAMA_IDX);
    354  mut_lc->description = tor_strdup("A llama named Hugo.");
    355  config_free(mgr, p);
    356  tt_int_op(clear_llama_cfg_called, OP_EQ, 1);
    357 
    358 done:
    359  config_free_lines(lines);
    360  config_free(mgr, p);
    361  config_mgr_free(mgr);
    362  tor_free(msg);
    363 }
    364 
    365 static void
    366 test_confmgr_dump(void *arg)
    367 {
    368  (void)arg;
    369  config_mgr_t *mgr = get_mgr(true);
    370  pasture_cfg_t *p = config_new(mgr);
    371  pasture_cfg_t *defaults = config_new(mgr);
    372  config_line_t *lines = NULL;
    373  char *msg = NULL;
    374  char *s = NULL;
    375 
    376  config_init(mgr, p); // set defaults.
    377  config_init(mgr, defaults); // set defaults.
    378 
    379  int r = config_get_lines(simple_pasture, &lines, 0);
    380  tt_int_op(r, OP_EQ, 0);
    381  r = config_assign(mgr, p, lines, 0, &msg);
    382  tt_int_op(r, OP_EQ, 0);
    383 
    384  s = config_dump(mgr, defaults, p, 1, 0);
    385  tt_str_op("address 123 Camelid ave\n"
    386            "alpacaname daphne\n"
    387            "cuteness 42\n"
    388            "llamaname hugo\n", OP_EQ, s);
    389 
    390 done:
    391  config_free_lines(lines);
    392  config_free(mgr, p);
    393  config_free(mgr, defaults);
    394  config_mgr_free(mgr);
    395 
    396  tor_free(msg);
    397  tor_free(s);
    398 }
    399 
    400 static pasture_cfg_t *
    401 parse_and_validate(config_mgr_t *mgr,
    402                   const char *inp, const pasture_cfg_t *old, char **msg_out)
    403 {
    404  pasture_cfg_t *p = config_new(mgr);
    405  pasture_cfg_t *result = NULL;
    406  config_line_t *lines = NULL;
    407 
    408  config_init(mgr, p); // set defaults.
    409  int r = config_get_lines(inp, &lines, 0);
    410  tt_int_op(r, OP_EQ, 0);
    411  r = config_assign(mgr, p, lines, 0, msg_out);
    412  tt_int_op(r, OP_EQ, 0);
    413  tor_free(*msg_out); // sets it to NULL
    414  r = config_validate(mgr, old, p, msg_out);
    415  if (r < 0)
    416    goto done;
    417 
    418  tt_ptr_op(*msg_out, OP_EQ, NULL);
    419  result = p;
    420  p = NULL; // prevent free
    421 done:
    422  config_free(mgr, p);
    423  config_free_lines(lines);
    424  return result;
    425 }
    426 
    427 static void
    428 test_confmgr_validate(void *arg)
    429 {
    430  (void)arg;
    431  char *msg = NULL;
    432  config_mgr_t *mgr = get_mgr(true);
    433  pasture_cfg_t *p_orig, *p=NULL;
    434 
    435  p_orig = parse_and_validate(mgr, "Llamaname Quest\n"
    436                                   "Address 99 camelid way\n"
    437                                   "Fuzziness 8\n", NULL, &msg);
    438  tt_assert(p_orig);
    439 
    440  // Make sure normalization code was run.
    441  const alpaca_cfg_t *ac0 = config_mgr_get_obj(mgr, p_orig, ALPACA_IDX);
    442  const llama_cfg_t *lc0 = config_mgr_get_obj(mgr, p_orig, LLAMA_IDX);
    443  tt_int_op(ac0->fuzziness, OP_EQ, 8);
    444  tt_int_op(ac0->square_fuzziness, OP_EQ, 64);
    445  tt_str_op(lc0->description, OP_EQ, "A llama called Quest.");
    446  tt_str_op(p_orig->address, OP_EQ, "99 CAMELID WAY");
    447 
    448  // try a bad llamaname.
    449  p = parse_and_validate(mgr, "llamaname 123", p_orig, &msg);
    450  tt_assert(!p);
    451  tt_str_op(msg, OP_EQ, "It is not a number; it is a free llama!");
    452  tor_free(msg);
    453 
    454  // try a llamaname that would crash the post_normalize step, if it ran.
    455  p = parse_and_validate(mgr, "", p_orig, &msg);
    456  tt_assert(!p);
    457  tt_str_op(msg, OP_EQ, "A llama has no name!?");
    458  tor_free(msg);
    459 
    460  // Verify that a transition to a less fuzzy alpaca fails.
    461  p = parse_and_validate(mgr, "Llamaname Quest\n"
    462                              "Address 99 camelid way\n"
    463                              "Fuzziness 4\n", p_orig, &msg);
    464  tt_assert(!p);
    465  tt_str_op(msg, OP_EQ, "An alpaca only becomes more fuzzy over time.");
    466  tor_free(msg);
    467 
    468  // Try a transition to a more fuzzy alpaca; it should work fine.
    469  p = parse_and_validate(mgr, "Llamaname Mercutio\n"
    470                              // the default fuzziness is 50
    471                              "Address 99 camelid way\n", p_orig, &msg);
    472  tt_assert(p);
    473  config_free(mgr, p);
    474 
    475  // Verify that we can't move the pasture.
    476  p = parse_and_validate(mgr, "Llamaname Montague\n"
    477                              // the default fuzziness is 50
    478                              "Address 99 ungulate st\n", p_orig, &msg);
    479  tt_assert(!p);
    480  tt_str_op(msg, OP_EQ, "You can't move a pasture.");
    481 
    482 done:
    483  config_free(mgr, p);
    484  config_free(mgr, p_orig);
    485  config_mgr_free(mgr);
    486  tor_free(msg);
    487 }
    488 
    489 #define CONFMGR_TEST(name, flags)                       \
    490  { #name, test_confmgr_ ## name, flags, NULL, NULL }
    491 
    492 struct testcase_t confmgr_tests[] = {
    493  CONFMGR_TEST(init, 0),
    494  CONFMGR_TEST(magic, 0),
    495  CONFMGR_TEST(parse, 0),
    496  CONFMGR_TEST(dump, 0),
    497  CONFMGR_TEST(validate, 0),
    498  END_OF_TESTCASES
    499 };