/* Helper for disk_state_load_from_disk(). */ STATIC int disk_state_load_from_disk_impl(const char *fname) { int ret; char *content = NULL; sr_state_t *parsed_state = NULL; sr_disk_state_t *disk_state = NULL; /* Read content of file so we can parse it. */ if ((content = read_file_to_str(fname, 0, NULL)) == NULL) { log_warn(LD_FS, "SR: Unable to read SR state file %s", escaped(fname)); ret = -errno; goto error; } { config_line_t *lines = NULL; char *errmsg = NULL; /* Every error in this code path will return EINVAL. */ ret = -EINVAL; if (config_get_lines(content, &lines, 0) < 0) { config_free_lines(lines); goto error; } disk_state = disk_state_new(time(NULL)); config_assign(&state_format, disk_state, lines, 0, &errmsg); config_free_lines(lines); if (errmsg) { log_warn(LD_DIR, "SR: Reading state error: %s", errmsg); tor_free(errmsg); goto error; } } /* So far so good, we've loaded our state file into our disk state. Let's * validate it and then parse it. */ if (disk_state_validate(disk_state) < 0) { ret = -EINVAL; goto error; } parsed_state = disk_state_parse(disk_state); if (parsed_state == NULL) { ret = -EINVAL; goto error; } state_set(parsed_state); disk_state_set(disk_state); tor_free(content); log_info(LD_DIR, "SR: State loaded successfully from file %s", fname); return 0; error: disk_state_free(disk_state); tor_free(content); return ret; }
/* Reset disk state that is free allocated memory and zeroed the object. */ static void disk_state_reset(void) { /* Free allocated memory */ config_free_lines(sr_disk_state->Commit); config_free_lines(sr_disk_state->SharedRandValues); config_free_lines(sr_disk_state->ExtraLines); tor_free(sr_disk_state->TorVersion); /* Clean up the struct */ memset(sr_disk_state, 0, sizeof(*sr_disk_state)); /* Reset it with useful data */ sr_disk_state->magic_ = SR_DISK_STATE_MAGIC; sr_disk_state->TorVersion = tor_strdup(get_version()); }
/** Helper: parse the config string and strdup into key/value * strings. Set *result to the list, or NULL if parsing the string * failed. Return 0 on success, -1 on failure. Warn and ignore any * misformatted lines. * * If <b>extended</b> is set, then treat keys beginning with / and with + as * indicating "clear" and "append" respectively. */ int config_get_lines(const char *string, config_line_t **result, int extended) { config_line_t *list = NULL, **next; char *k, *v; const char *parse_err; next = &list; do { k = v = NULL; string = parse_config_line_from_str_verbose(string, &k, &v, &parse_err); if (!string) { log_warn(LD_CONFIG, "Error while parsing configuration: %s", parse_err?parse_err:"<unknown>"); config_free_lines(list); tor_free(k); tor_free(v); return -1; } if (k && v) { unsigned command = CONFIG_LINE_NORMAL; if (extended) { if (k[0] == '+') { char *k_new = tor_strdup(k+1); tor_free(k); k = k_new; command = CONFIG_LINE_APPEND; } else if (k[0] == '/') { char *k_new = tor_strdup(k+1); tor_free(k); k = k_new; tor_free(v); v = tor_strdup(""); command = CONFIG_LINE_CLEAR; } } /* 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. */ *next = tor_malloc_zero(sizeof(config_line_t)); (*next)->key = k; (*next)->value = v; (*next)->next = NULL; (*next)->command = command; next = &((*next)->next); } else { tor_free(k); tor_free(v); } } while (*string); *result = list; return 0; }
static void test_pt_get_transport_options(void *arg) { char **execve_args; smartlist_t *transport_list = smartlist_new(); managed_proxy_t *mp; or_options_t *options = get_options_mutable(); char *opt_str = NULL; config_line_t *cl = NULL; (void)arg; execve_args = tor_malloc(sizeof(char*)*2); execve_args[0] = tor_strdup("cheeseshop"); execve_args[1] = NULL; mp = managed_proxy_create(transport_list, execve_args, 1); tt_ptr_op(mp, OP_NE, NULL); opt_str = get_transport_options_for_server_proxy(mp); tt_ptr_op(opt_str, OP_EQ, NULL); smartlist_add_strdup(mp->transports_to_launch, "gruyere"); smartlist_add_strdup(mp->transports_to_launch, "roquefort"); smartlist_add_strdup(mp->transports_to_launch, "stnectaire"); tt_assert(options); cl = tor_malloc_zero(sizeof(config_line_t)); cl->value = tor_strdup("gruyere melty=10 hardness=se;ven"); options->ServerTransportOptions = cl; cl = tor_malloc_zero(sizeof(config_line_t)); cl->value = tor_strdup("stnectaire melty=4 hardness=three"); cl->next = options->ServerTransportOptions; options->ServerTransportOptions = cl; cl = tor_malloc_zero(sizeof(config_line_t)); cl->value = tor_strdup("pepperjack melty=12 hardness=five"); cl->next = options->ServerTransportOptions; options->ServerTransportOptions = cl; opt_str = get_transport_options_for_server_proxy(mp); tt_str_op(opt_str, OP_EQ, "gruyere:melty=10;gruyere:hardness=se\\;ven;" "stnectaire:melty=4;stnectaire:hardness=three"); done: tor_free(opt_str); config_free_lines(cl); managed_proxy_destroy(mp, 0); smartlist_free(transport_list); }
/** * Output a histogram of current circuit build times to * the or_state_t state structure. */ void circuit_build_times_update_state(circuit_build_times_t *cbt, or_state_t *state) { uint32_t *histogram; build_time_t i = 0; build_time_t nbins = 0; config_line_t **next, *line; histogram = circuit_build_times_create_histogram(cbt, &nbins); // write to state config_free_lines(state->BuildtimeHistogram); next = &state->BuildtimeHistogram; *next = NULL; state->TotalBuildTimes = cbt->total_build_times; state->CircuitBuildAbandonedCount = 0; for (i = 0; i < CBT_NCIRCUITS_TO_OBSERVE; i++) { if (cbt->circuit_build_times[i] == CBT_BUILD_ABANDONED) state->CircuitBuildAbandonedCount++; } for (i = 0; i < nbins; i++) { // compress the histogram by skipping the blanks if (histogram[i] == 0) continue; *next = line = tor_malloc_zero(sizeof(config_line_t)); line->key = tor_strdup("CircuitBuildTimeBin"); tor_asprintf(&line->value, "%d %d", CBT_BIN_TO_MS(i), histogram[i]); next = &(line->next); } if (!unit_tests) { if (!get_options()->AvoidDiskWrites) or_state_mark_dirty(get_or_state(), 0); } tor_free(histogram); }
static void test_storagedir_save_labeled(void *arg) { (void)arg; char *dirname = tor_strdup(get_fname_rnd("store_dir")); storage_dir_t *d = NULL; uint8_t *inp = tor_malloc_zero(8192); config_line_t *labels = NULL; char *fname = NULL; uint8_t *saved = NULL; d = storage_dir_new(dirname, 10); tt_assert(d); crypto_rand((char *)inp, 8192); config_line_append(&labels, "Foo", "bar baz"); config_line_append(&labels, "quux", "quuzXxz"); const char expected[] = "Foo bar baz\n" "quux quuzXxz\n"; int r = storage_dir_save_labeled_to_file(d, labels, inp, 8192, &fname); tt_int_op(r, OP_EQ, 0); size_t n = 0; saved = storage_dir_read(d, fname, 1, &n); tt_assert(memchr(saved, '\0', n)); tt_str_op((char*)saved, OP_EQ, expected); /* NUL guarantees strcmp works */ tt_mem_op(saved+strlen(expected)+1, OP_EQ, inp, 8192); done: storage_dir_free(d); tor_free(dirname); tor_free(inp); tor_free(fname); config_free_lines(labels); tor_free(saved); }
/** Reload the persistent state from disk, generating a new state as needed. * Return 0 on success, less than 0 on failure. */ int or_state_load(void) { or_state_t *new_state = NULL; char *contents = NULL, *fname; char *errmsg = NULL; int r = -1, badstate = 0; fname = get_datadir_fname("state"); switch (file_status(fname)) { case FN_FILE: if (!(contents = read_file_to_str(fname, 0, NULL))) { log_warn(LD_FS, "Unable to read state file \"%s\"", fname); goto done; } break; case FN_NOENT: break; case FN_ERROR: case FN_DIR: default: log_warn(LD_GENERAL,"State file \"%s\" is not a file? Failing.", fname); goto done; } new_state = tor_malloc_zero(sizeof(or_state_t)); new_state->_magic = OR_STATE_MAGIC; config_init(&state_format, new_state); if (contents) { config_line_t *lines=NULL; int assign_retval; if (config_get_lines(contents, &lines, 0)<0) goto done; assign_retval = config_assign(&state_format, new_state, lines, 0, 0, &errmsg); config_free_lines(lines); if (assign_retval<0) badstate = 1; if (errmsg) { log_warn(LD_GENERAL, "%s", errmsg); tor_free(errmsg); } } if (!badstate && or_state_validate(NULL, new_state, 1, &errmsg) < 0) badstate = 1; if (errmsg) { log_warn(LD_GENERAL, "%s", errmsg); tor_free(errmsg); } if (badstate && !contents) { log_warn(LD_BUG, "Uh oh. We couldn't even validate our own default state." " This is a bug in Tor."); goto done; } else if (badstate && contents) { or_state_save_broken(fname); tor_free(contents); config_free(&state_format, new_state); new_state = tor_malloc_zero(sizeof(or_state_t)); new_state->_magic = OR_STATE_MAGIC; config_init(&state_format, new_state); } else if (contents) { log_info(LD_GENERAL, "Loaded state from \"%s\"", fname); } else { log_info(LD_GENERAL, "Initialized state"); } if (or_state_set(new_state) == -1) { or_state_save_broken(fname); } new_state = NULL; if (!contents) { global_state->next_write = 0; or_state_save(time(NULL)); } r = 0; done: tor_free(fname); tor_free(contents); if (new_state) config_free(&state_format, new_state); return r; }
static void test_router_get_my_family(void *arg) { (void)arg; or_options_t *options = options_new(); smartlist_t *sl = NULL; char *join = NULL; // Overwrite the result of router_get_my_identity_digest(). This // happens to be okay, but only for testing. set_server_identity_key_digest_testing( (const uint8_t*)"holeinthebottomofthe"); setup_capture_of_logs(LOG_WARN); // No family listed -- so there's no list. sl = get_my_declared_family(options); tt_ptr_op(sl, OP_EQ, NULL); expect_no_log_entry(); #define CLEAR() do { \ if (sl) { \ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); \ smartlist_free(sl); \ } \ tor_free(join); \ mock_clean_saved_logs(); \ } while (0) // Add a single nice friendly hex member. This should be enough // to have our own ID added. tt_ptr_op(options->MyFamily, OP_EQ, NULL); config_line_append(&options->MyFamily, "MyFamily", "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_NE, NULL); tt_int_op(smartlist_len(sl), OP_EQ, 2); join = smartlist_join_strings(sl, " ", 0, NULL); tt_str_op(join, OP_EQ, "$686F6C65696E746865626F74746F6D6F66746865 " "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); expect_no_log_entry(); CLEAR(); // Add a hex member with a ~. The ~ part should get removed. config_line_append(&options->MyFamily, "MyFamily", "$0123456789abcdef0123456789abcdef01234567~Muffin"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_NE, NULL); tt_int_op(smartlist_len(sl), OP_EQ, 3); join = smartlist_join_strings(sl, " ", 0, NULL); tt_str_op(join, OP_EQ, "$0123456789ABCDEF0123456789ABCDEF01234567 " "$686F6C65696E746865626F74746F6D6F66746865 " "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); expect_no_log_entry(); CLEAR(); // Nickname lookup will fail, so a nickname will appear verbatim. config_line_append(&options->MyFamily, "MyFamily", "BAGEL"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_NE, NULL); tt_int_op(smartlist_len(sl), OP_EQ, 4); join = smartlist_join_strings(sl, " ", 0, NULL); tt_str_op(join, OP_EQ, "$0123456789ABCDEF0123456789ABCDEF01234567 " "$686F6C65696E746865626F74746F6D6F66746865 " "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " "bagel"); expect_single_log_msg_containing( "There is a router named \"BAGEL\" in my declared family, but " "I have no descriptor for it."); CLEAR(); // A bogus digest should fail entirely. config_line_append(&options->MyFamily, "MyFamily", "$painauchocolat"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_NE, NULL); tt_int_op(smartlist_len(sl), OP_EQ, 4); join = smartlist_join_strings(sl, " ", 0, NULL); tt_str_op(join, OP_EQ, "$0123456789ABCDEF0123456789ABCDEF01234567 " "$686F6C65696E746865626F74746F6D6F66746865 " "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " "bagel"); // "BAGEL" is still there, but it won't make a warning, because we already // warned about it. expect_single_log_msg_containing( "There is a router named \"$painauchocolat\" in my declared " "family, but that isn't a legal digest or nickname. Skipping it."); CLEAR(); // Let's introduce a node we can look up by nickname memset(&fake_node, 0, sizeof(fake_node)); memcpy(fake_node.identity, "whydoyouasknonononon", DIGEST_LEN); MOCK(node_get_by_nickname, mock_node_get_by_nickname); config_line_append(&options->MyFamily, "MyFamily", "CRUmpeT"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_NE, NULL); tt_int_op(smartlist_len(sl), OP_EQ, 5); join = smartlist_join_strings(sl, " ", 0, NULL); tt_str_op(join, OP_EQ, "$0123456789ABCDEF0123456789ABCDEF01234567 " "$686F6C65696E746865626F74746F6D6F66746865 " "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E " "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " "bagel"); // "BAGEL" is still there, but it won't make a warning, because we already // warned about it. Some with "$painauchocolat". expect_single_log_msg_containing( "There is a router named \"CRUmpeT\" in my declared " "family, but it wasn't listed by digest. Please consider saying " "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E instead, if that's " "what you meant."); CLEAR(); UNMOCK(node_get_by_nickname); // Try a singleton list containing only us: It should give us NULL. config_free_lines(options->MyFamily); config_line_append(&options->MyFamily, "MyFamily", "$686F6C65696E746865626F74746F6D6F66746865"); sl = get_my_declared_family(options); tt_ptr_op(sl, OP_EQ, NULL); expect_no_log_entry(); done: or_options_free(options); teardown_capture_of_logs(); CLEAR(); UNMOCK(node_get_by_nickname); #undef CLEAR }
static void test_config_addressmap(void *arg) { char buf[1024]; char address[256]; time_t expires = TIME_MAX; (void)arg; strlcpy(buf, "MapAddress .invalidwildcard.com *.torserver.exit\n" // invalid "MapAddress *invalidasterisk.com *.torserver.exit\n" // invalid "MapAddress *.google.com *.torserver.exit\n" "MapAddress *.yahoo.com *.google.com.torserver.exit\n" "MapAddress *.cn.com www.cnn.com\n" "MapAddress *.cnn.com www.cnn.com\n" "MapAddress ex.com www.cnn.com\n" "MapAddress ey.com *.cnn.com\n" "MapAddress www.torproject.org 1.1.1.1\n" "MapAddress other.torproject.org " "this.torproject.org.otherserver.exit\n" "MapAddress test.torproject.org 2.2.2.2\n" "MapAddress www.google.com 3.3.3.3\n" "MapAddress www.example.org 4.4.4.4\n" "MapAddress 4.4.4.4 7.7.7.7\n" "MapAddress 4.4.4.4 5.5.5.5\n" "MapAddress www.infiniteloop.org 6.6.6.6\n" "MapAddress 6.6.6.6 www.infiniteloop.org\n" , sizeof(buf)); config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); config_register_addressmaps(get_options()); /* Use old interface for now, so we don't need to rewrite the unit tests */ #define addressmap_rewrite(a,s,eo,ao) \ addressmap_rewrite((a),(s),AMR_FLAG_USE_IPV4_DNS|AMR_FLAG_USE_IPV6_DNS, \ (eo),(ao)) /* MapAddress .invalidwildcard.com .torserver.exit - no match */ strlcpy(address, "www.invalidwildcard.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* MapAddress *invalidasterisk.com .torserver.exit - no match */ strlcpy(address, "www.invalidasterisk.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* Where no mapping for FQDN match on top-level domain */ /* MapAddress .google.com .torserver.exit */ strlcpy(address, "reader.google.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "reader.torserver.exit"); /* MapAddress *.yahoo.com *.google.com.torserver.exit */ strlcpy(address, "reader.yahoo.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "reader.google.com.torserver.exit"); /*MapAddress *.cnn.com www.cnn.com */ strlcpy(address, "cnn.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "www.cnn.com"); /* MapAddress .cn.com www.cnn.com */ strlcpy(address, "www.cn.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "www.cnn.com"); /* MapAddress ex.com www.cnn.com - no match */ strlcpy(address, "www.ex.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* MapAddress ey.com *.cnn.com - invalid expression */ strlcpy(address, "ey.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* Where mapping for FQDN match on FQDN */ strlcpy(address, "www.google.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "3.3.3.3"); strlcpy(address, "www.torproject.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "1.1.1.1"); strlcpy(address, "other.torproject.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "this.torproject.org.otherserver.exit"); strlcpy(address, "test.torproject.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "2.2.2.2"); /* Test a chain of address mappings and the order in which they were added: "MapAddress www.example.org 4.4.4.4" "MapAddress 4.4.4.4 7.7.7.7" "MapAddress 4.4.4.4 5.5.5.5" */ strlcpy(address, "www.example.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "5.5.5.5"); /* Test infinite address mapping results in no change */ strlcpy(address, "www.infiniteloop.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "www.infiniteloop.org"); /* Test we don't find false positives */ strlcpy(address, "www.example.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); /* Test top-level-domain matching a bit harder */ config_free_lines(get_options_mutable()->AddressMap); addressmap_clear_configured(); strlcpy(buf, "MapAddress *.com *.torserver.exit\n" "MapAddress *.torproject.org 1.1.1.1\n" "MapAddress *.net 2.2.2.2\n" , sizeof(buf)); config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); config_register_addressmaps(get_options()); strlcpy(address, "www.abc.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "www.abc.torserver.exit"); strlcpy(address, "www.def.com", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "www.def.torserver.exit"); strlcpy(address, "www.torproject.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "1.1.1.1"); strlcpy(address, "test.torproject.org", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "1.1.1.1"); strlcpy(address, "torproject.net", sizeof(address)); test_assert(addressmap_rewrite(address, sizeof(address), &expires, NULL)); test_streq(address, "2.2.2.2"); /* We don't support '*' as a mapping directive */ config_free_lines(get_options_mutable()->AddressMap); addressmap_clear_configured(); strlcpy(buf, "MapAddress * *.torserver.exit\n", sizeof(buf)); config_get_lines(buf, &(get_options_mutable()->AddressMap), 0); config_register_addressmaps(get_options()); strlcpy(address, "www.abc.com", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); strlcpy(address, "www.def.net", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); strlcpy(address, "www.torproject.org", sizeof(address)); test_assert(!addressmap_rewrite(address, sizeof(address), &expires, NULL)); #undef addressmap_rewrite done: config_free_lines(get_options_mutable()->AddressMap); get_options_mutable()->AddressMap = NULL; }
/** * Decode a <b>line</b> containing a series of space-separated 'Key=Value' * pairs, using the provided <b>flags</b> to decode it. Return a newly * allocated list of pairs on success, or NULL on failure. * * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are * allowed. Otherwise, such values are not allowed. * * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are * allowed. Otherwise, such values are not allowed. */ config_line_t * kvline_parse(const char *line, unsigned flags) { const char *cp = line, *cplast = NULL; bool omit_keys = (flags & KV_OMIT_KEYS) != 0; bool quoted = (flags & KV_QUOTED) != 0; config_line_t *result = NULL; config_line_t **next_line = &result; char *key = NULL; char *val = NULL; while (*cp) { key = val = NULL; { size_t idx = strspn(cp, " \t\r\v\n"); cp += idx; } if (BUG(cp == cplast)) { /* If we didn't parse anything, this code is broken. */ goto err; // LCOV_EXCL_LINE } cplast = cp; if (! *cp) break; /* End of string; we're done. */ /* Possible formats are K=V, K="V", V, and "V", depending on flags. */ /* Find the key. */ if (*cp != '\"') { size_t idx = strcspn(cp, " \t\r\v\n="); if (cp[idx] == '=') { key = tor_memdup_nulterm(cp, idx); cp += idx + 1; } else { if (!omit_keys) goto err; } } if (*cp == '\"') { /* The type is "V". */ if (!quoted) goto err; size_t len=0; cp = unescape_string(cp, &val, &len); if (cp == NULL || len != strlen(val)) { // The string contains a NUL or is badly coded. goto err; } } else { size_t idx = strcspn(cp, " \t\r\v\n"); val = tor_memdup_nulterm(cp, idx); cp += idx; } if (key && strlen(key) == 0) { /* We don't allow empty keys. */ goto err; } *next_line = tor_malloc_zero(sizeof(config_line_t)); (*next_line)->key = key ? key : tor_strdup(""); (*next_line)->value = val; next_line = &(*next_line)->next; key = val = NULL; } if (!kvline_can_encode_lines(result, flags)) { goto err; } return result; err: tor_free(key); tor_free(val); config_free_lines(result); return NULL; }
static void test_storagedir_read_labeled(void *arg) { (void)arg; char *dirname = tor_strdup(get_fname_rnd("store_dir")); storage_dir_t *d = NULL; uint8_t *inp = tor_malloc_zero(8192); config_line_t *labels = NULL, *labels2 = NULL; char *fname = NULL; tor_mmap_t *map = NULL; uint8_t *as_read = NULL; d = storage_dir_new(dirname, 10); tt_assert(d); tor_snprintf((char*)inp, 8192, "Hello world\n" "This is a test\n" "Yadda yadda.\n"); size_t bodylen = 8192 - strlen((char*)inp) - 1; crypto_rand((char *)inp+strlen((char*)inp)+1, bodylen); int r = storage_dir_save_bytes_to_file(d, inp, 8192, 1, &fname); tt_int_op(r, OP_EQ, 0); /* Try mapping */ const uint8_t *datap = NULL; size_t sz = 0; map = storage_dir_map_labeled(d, fname, &labels, &datap, &sz); tt_assert(map); tt_assert(datap); tt_u64_op(sz, OP_EQ, bodylen); tt_mem_op(datap, OP_EQ, inp+strlen((char*)inp)+1, bodylen); tt_assert(labels); tt_str_op(labels->key, OP_EQ, "Hello"); tt_str_op(labels->value, OP_EQ, "world"); tt_assert(labels->next); tt_str_op(labels->next->key, OP_EQ, "This"); tt_str_op(labels->next->value, OP_EQ, "is a test"); tt_assert(labels->next->next); tt_str_op(labels->next->next->key, OP_EQ, "Yadda"); tt_str_op(labels->next->next->value, OP_EQ, "yadda."); tt_ptr_op(labels->next->next->next, OP_EQ, NULL); /* Try reading this time. */ sz = 0; as_read = storage_dir_read_labeled(d, fname, &labels2, &sz); tt_assert(as_read); tt_u64_op(sz, OP_EQ, bodylen); tt_mem_op(as_read, OP_EQ, inp+strlen((char*)inp)+1, bodylen); tt_assert(config_lines_eq(labels, labels2)); done: storage_dir_free(d); tor_free(dirname); tor_free(inp); tor_free(fname); config_free_lines(labels); config_free_lines(labels2); tor_munmap_file(map); tor_free(as_read); }