commit c35c43d7d9df793c890deb14800e24327fbca452 Merge: bd6aa4f c189cb5 Author: Nick Mathewson nickm@torproject.org Date: Fri Nov 4 13:26:37 2016 -0400
Merge branch 'ticket17238_029_02-resquash'
Conflicts: src/or/rendclient.c src/or/rendcommon.c src/or/routerparse.c src/test/test_dir.c src/trunnel/ed25519_cert.h
src/or/circuitlist.c | 38 +- src/or/circuitlist.h | 2 +- src/or/circuituse.c | 9 +- src/or/connection.c | 5 +- src/or/connection_edge.c | 10 +- src/or/control.c | 21 +- src/or/directory.c | 179 +++- src/or/directory.h | 15 +- src/or/hs_cache.c | 384 ++++++++ src/or/hs_cache.h | 61 ++ src/or/hs_common.c | 280 ++++++ src/or/hs_common.h | 39 + src/or/hs_descriptor.c | 1939 ++++++++++++++++++++++++++++++++++++++ src/or/hs_descriptor.h | 238 +++++ src/or/include.am | 8 + src/or/main.c | 4 +- src/or/or.h | 42 +- src/or/parsecommon.c | 450 +++++++++ src/or/parsecommon.h | 314 ++++++ src/or/relay.c | 5 +- src/or/rendcache.c | 79 +- src/or/rendcache.h | 13 +- src/or/rendclient.c | 148 +-- src/or/rendclient.h | 2 +- src/or/rendcommon.c | 149 +-- src/or/rendcommon.h | 24 +- src/or/rendservice.c | 87 +- src/or/routerparse.c | 723 +------------- src/or/torcert.h | 15 +- src/test/include.am | 2 + src/test/test.c | 2 + src/test/test.h | 2 + src/test/test_connection.c | 12 +- src/test/test_dir.c | 62 ++ src/test/test_dir_handle_get.c | 13 - src/test/test_helpers.c | 13 + src/test/test_helpers.h | 4 + src/test/test_hs.c | 103 +- src/test/test_hs_cache.c | 479 ++++++++++ src/test/test_hs_descriptor.c | 1130 ++++++++++++++++++++++ src/test/test_rendcache.c | 57 +- src/trunnel/ed25519_cert.c | 881 +++++++++++++++++ src/trunnel/ed25519_cert.h | 300 ++++++ src/trunnel/ed25519_cert.trunnel | 7 +- 44 files changed, 7168 insertions(+), 1182 deletions(-)
diff --cc src/or/connection_edge.c index 0c18de0,2726349..875c911 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@@ -72,9 -27,9 +72,10 @@@ #include "control.h" #include "dns.h" #include "dnsserv.h" +#include "directory.h" #include "dirserv.h" #include "hibernate.h" + #include "hs_common.h" #include "main.h" #include "nodelist.h" #include "policies.h" @@@ -1861,11 -1708,12 +1862,12 @@@ connection_ap_handshake_rewrite_and_att if (rend_data == NULL) { return -1; } + const char *onion_address = rend_data_get_address(rend_data); log_info(LD_REND,"Got a hidden service request for ID '%s'", - safe_str_client(rend_data->onion_address)); + safe_str_client(onion_address));
- /* Lookup the given onion address. If invalid, stop right now else we - * might have it in the cache or not, it will be tested later on. */ + /* Lookup the given onion address. If invalid, stop right now. + * Otherwise, we might have it in the cache or not. */ unsigned int refetch_desc = 0; rend_cache_entry_t *entry = NULL; const int rend_cache_lookup_result = diff --cc src/or/directory.h index f1cdd9f,210d623..acb7394 --- a/src/or/directory.h +++ b/src/or/directory.h @@@ -132,11 -132,18 +132,19 @@@ int download_status_get_n_failures(cons int download_status_get_n_attempts(const download_status_t *dls); time_t download_status_get_next_attempt_at(const download_status_t *dls);
-int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose); +int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose, + const char *resource);
+ #ifdef DIRECTORY_PRIVATE + + struct get_handler_args_t; + STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn, + const struct get_handler_args_t *args); + + #endif + #ifdef TOR_UNIT_TESTS - /* Used only by directory.c and test_dir.c */ + /* Used only by test_dir.c */
STATIC int parse_http_url(const char *headers, char **url); STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose, diff --cc src/or/include.am index b4554aa,02d67fa..10f8b85 --- a/src/or/include.am +++ b/src/or/include.am @@@ -59,8 -62,8 +62,9 @@@ LIBTOR_A_SOURCES = src/or/shared_random.c \ src/or/shared_random_state.c \ src/or/transports.c \ + src/or/parsecommon.c \ src/or/periodic.c \ + src/or/protover.c \ src/or/policies.c \ src/or/reasons.c \ src/or/relay.c \ @@@ -171,9 -177,9 +178,10 @@@ ORHEADERS = src/or/shared_random.h \ src/or/shared_random_state.h \ src/or/transports.h \ + src/or/parsecommon.h \ src/or/periodic.h \ src/or/policies.h \ + src/or/protover.h \ src/or/reasons.h \ src/or/relay.h \ src/or/rendcache.h \ diff --cc src/or/parsecommon.h index 0000000,be7cba4..3a86c52 mode 000000,100644..100644 --- a/src/or/parsecommon.h +++ b/src/or/parsecommon.h @@@ -1,0 -1,306 +1,314 @@@ + /* Copyright (c) 2016, The Tor Project, Inc. */ + /* See LICENSE for licensing information */ + + /** + * \file parsecommon.h + * \brief Header file for parsecommon.c + **/ + + #ifndef TOR_PARSECOMMON_H + #define TOR_PARSECOMMON_H + + #include "container.h" + #include "crypto.h" + #include "memarea.h" + + /** Enumeration of possible token types. The ones starting with K_ correspond + * to directory 'keywords'. A_ is for an annotation, R or C is related to + * hidden services, ERR_ is an error in the tokenizing process, EOF_ is an + * end-of-file marker, and NIL_ is used to encode not-a-token. + */ + typedef enum { + K_ACCEPT = 0, + K_ACCEPT6, + K_DIRECTORY_SIGNATURE, + K_RECOMMENDED_SOFTWARE, + K_REJECT, + K_REJECT6, + K_ROUTER, + K_SIGNED_DIRECTORY, + K_SIGNING_KEY, + K_ONION_KEY, + K_ONION_KEY_NTOR, + K_ROUTER_SIGNATURE, + K_PUBLISHED, + K_RUNNING_ROUTERS, + K_ROUTER_STATUS, + K_PLATFORM, ++ K_PROTO, + K_OPT, + K_BANDWIDTH, + K_CONTACT, + K_NETWORK_STATUS, + K_UPTIME, + K_DIR_SIGNING_KEY, + K_FAMILY, + K_FINGERPRINT, + K_HIBERNATING, + K_READ_HISTORY, + K_WRITE_HISTORY, + K_NETWORK_STATUS_VERSION, + K_DIR_SOURCE, + K_DIR_OPTIONS, + K_CLIENT_VERSIONS, + K_SERVER_VERSIONS, ++ K_RECOMMENDED_CLIENT_PROTOCOLS, ++ K_RECOMMENDED_RELAY_PROTOCOLS, ++ K_REQUIRED_CLIENT_PROTOCOLS, ++ K_REQUIRED_RELAY_PROTOCOLS, + K_OR_ADDRESS, + K_ID, + K_P, + K_P6, + K_R, + K_A, + K_S, + K_V, + K_W, + K_M, + K_EXTRA_INFO, + K_EXTRA_INFO_DIGEST, + K_CACHES_EXTRA_INFO, + K_HIDDEN_SERVICE_DIR, + K_ALLOW_SINGLE_HOP_EXITS, + K_IPV6_POLICY, + K_ROUTER_SIG_ED25519, + K_IDENTITY_ED25519, + K_MASTER_KEY_ED25519, + K_ONION_KEY_CROSSCERT, + K_NTOR_ONION_KEY_CROSSCERT, + + K_DIRREQ_END, + K_DIRREQ_V2_IPS, + K_DIRREQ_V3_IPS, + K_DIRREQ_V2_REQS, + K_DIRREQ_V3_REQS, + K_DIRREQ_V2_SHARE, + K_DIRREQ_V3_SHARE, + K_DIRREQ_V2_RESP, + K_DIRREQ_V3_RESP, + K_DIRREQ_V2_DIR, + K_DIRREQ_V3_DIR, + K_DIRREQ_V2_TUN, + K_DIRREQ_V3_TUN, + K_ENTRY_END, + K_ENTRY_IPS, + K_CELL_END, + K_CELL_PROCESSED, + K_CELL_QUEUED, + K_CELL_TIME, + K_CELL_CIRCS, + K_EXIT_END, + K_EXIT_WRITTEN, + K_EXIT_READ, + K_EXIT_OPENED, + + K_DIR_KEY_CERTIFICATE_VERSION, + K_DIR_IDENTITY_KEY, + K_DIR_KEY_PUBLISHED, + K_DIR_KEY_EXPIRES, + K_DIR_KEY_CERTIFICATION, + K_DIR_KEY_CROSSCERT, + K_DIR_ADDRESS, + K_DIR_TUNNELLED, + + K_VOTE_STATUS, + K_VALID_AFTER, + K_FRESH_UNTIL, + K_VALID_UNTIL, + K_VOTING_DELAY, + + K_KNOWN_FLAGS, + K_PARAMS, + K_BW_WEIGHTS, + K_VOTE_DIGEST, + K_CONSENSUS_DIGEST, + K_ADDITIONAL_DIGEST, + K_ADDITIONAL_SIGNATURE, + K_CONSENSUS_METHODS, + K_CONSENSUS_METHOD, + K_LEGACY_DIR_KEY, + K_DIRECTORY_FOOTER, + K_SIGNING_CERT_ED, + K_SR_FLAG, + K_COMMIT, + K_PREVIOUS_SRV, + K_CURRENT_SRV, + K_PACKAGE, + + A_PURPOSE, + A_LAST_LISTED, + A_UNKNOWN_, + + R_RENDEZVOUS_SERVICE_DESCRIPTOR, + R_VERSION, + R_PERMANENT_KEY, + R_SECRET_ID_PART, + R_PUBLICATION_TIME, + R_PROTOCOL_VERSIONS, + R_INTRODUCTION_POINTS, + R_SIGNATURE, + + R_HS_DESCRIPTOR, /* From version 3, this MUST be generic to all future + descriptor versions thus making it R_. */ + R3_DESC_LIFETIME, + R3_DESC_SIGNING_CERT, + R3_REVISION_COUNTER, + R3_ENCRYPTED, + R3_SIGNATURE, + R3_CREATE2_FORMATS, + R3_AUTHENTICATION_REQUIRED, + R3_INTRODUCTION_POINT, + R3_INTRO_AUTH_KEY, + R3_INTRO_ENC_KEY, + R3_INTRO_ENC_KEY_CERTIFICATION, + + R_IPO_IDENTIFIER, + R_IPO_IP_ADDRESS, + R_IPO_ONION_PORT, + R_IPO_ONION_KEY, + R_IPO_SERVICE_KEY, + + C_CLIENT_NAME, + C_DESCRIPTOR_COOKIE, + C_CLIENT_KEY, + + ERR_, + EOF_, + NIL_ + } directory_keyword; + + /** Structure to hold a single directory token. + * + * We parse a directory by breaking it into "tokens", each consisting + * of a keyword, a line full of arguments, and a binary object. The + * arguments and object are both optional, depending on the keyword + * type. + * + * This structure is only allocated in memareas; do not allocate it on + * the heap, or token_clear() won't work. + */ + typedef struct directory_token_t { + directory_keyword tp; /**< Type of the token. */ + int n_args:30; /**< Number of elements in args */ + char **args; /**< Array of arguments from keyword line. */ + + char *object_type; /**< -----BEGIN [object_type]-----*/ + size_t object_size; /**< Bytes in object_body */ + char *object_body; /**< Contents of object, base64-decoded. */ + + crypto_pk_t *key; /**< For public keys only. Heap-allocated. */ + + char *error; /**< For ERR_ tokens only. */ + } directory_token_t; + + /** We use a table of rules to decide how to parse each token type. */ + + /** Rules for whether the keyword needs an object. */ + typedef enum { + NO_OBJ, /**< No object, ever. */ + NEED_OBJ, /**< Object is required. */ + NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */ + NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */ + NEED_KEY, /**< Object is required, and must be a public key. */ + OBJ_OK, /**< Object is optional. */ + } obj_syntax; + + #define AT_START 1 + #define AT_END 2 + + #define TS_ANNOTATIONS_OK 1 + #define TS_NOCHECK 2 + #define TS_NO_NEW_ANNOTATIONS 4 + -/* ++/** ++ * @name macros for defining token rules ++ * + * Helper macros to define token tables. 's' is a string, 't' is a + * directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an + * object syntax. - * + */ ++/**@{*/ + + /** Appears to indicate the end of a table. */ + #define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 } + /** An item with no restrictions: used for obsolete document types */ + #define T(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 } + /** An item with no restrictions on multiplicity or location. */ + #define T0N(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 } + /** An item that must appear exactly once */ + #define T1(s,t,a,o) { s, t, a, o, 1, 1, 0, 0 } + /** An item that must appear exactly once, at the start of the document */ + #define T1_START(s,t,a,o) { s, t, a, o, 1, 1, AT_START, 0 } + /** An item that must appear exactly once, at the end of the document */ + #define T1_END(s,t,a,o) { s, t, a, o, 1, 1, AT_END, 0 } + /** An item that must appear one or more times */ + #define T1N(s,t,a,o) { s, t, a, o, 1, INT_MAX, 0, 0 } + /** An item that must appear no more than once */ + #define T01(s,t,a,o) { s, t, a, o, 0, 1, 0, 0 } + /** An annotation that must appear no more than once */ + #define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 } + -/* Argument multiplicity: any number of arguments. */ ++/** Argument multiplicity: any number of arguments. */ + #define ARGS 0,INT_MAX,0 -/* Argument multiplicity: no arguments. */ ++/** Argument multiplicity: no arguments. */ + #define NO_ARGS 0,0,0 -/* Argument multiplicity: concatenate all arguments. */ ++/** Argument multiplicity: concatenate all arguments. */ + #define CONCAT_ARGS 1,1,1 -/* Argument multiplicity: at least <b>n</b> arguments. */ ++/** Argument multiplicity: at least <b>n</b> arguments. */ + #define GE(n) n,INT_MAX,0 -/* Argument multiplicity: exactly <b>n</b> arguments. */ ++/** Argument multiplicity: exactly <b>n</b> arguments. */ + #define EQ(n) n,n,0 ++/**@}*/ + + /** Determines the parsing rules for a single token type. */ + typedef struct token_rule_t { + /** The string value of the keyword identifying the type of item. */ + const char *t; + /** The corresponding directory_keyword enum. */ + directory_keyword v; + /** Minimum number of arguments for this item */ + int min_args; + /** Maximum number of arguments for this item */ + int max_args; + /** If true, we concatenate all arguments for this item into a single + * string. */ + int concat_args; + /** Requirements on object syntax for this item. */ + obj_syntax os; + /** Lowest number of times this item may appear in a document. */ + int min_cnt; + /** Highest number of times this item may appear in a document. */ + int max_cnt; + /** One or more of AT_START/AT_END to limit where the item may appear in a + * document. */ + int pos; + /** True iff this token is an annotation. */ + int is_annotation; + } token_rule_t; + + void token_clear(directory_token_t *tok); + + int tokenize_string(memarea_t *area, + const char *start, const char *end, + smartlist_t *out, + token_rule_t *table, + int flags); + directory_token_t *get_next_token(memarea_t *area, + const char **s, + const char *eos, + token_rule_t *table); + + directory_token_t *find_by_keyword_(smartlist_t *s, + directory_keyword keyword, + const char *keyword_str); + + #define find_by_keyword(s, keyword) \ + find_by_keyword_((s), (keyword), #keyword) + + directory_token_t *find_opt_by_keyword(smartlist_t *s, + directory_keyword keyword); + smartlist_t * find_all_by_keyword(smartlist_t *s, directory_keyword k); + + #endif /* TOR_PARSECOMMON_H */ + diff --cc src/or/rendclient.c index a93bc94,0bfc1a1..b0dcf52 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@@ -149,13 -150,15 +151,13 @@@ rend_client_send_introduction(origin_ci tor_assert(rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY); tor_assert(introcirc->rend_data); tor_assert(rendcirc->rend_data); - tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address, - rendcirc->rend_data->onion_address)); + tor_assert(!rend_cmp_service_ids(rend_data_get_address(introcirc->rend_data), + rend_data_get_address(rendcirc->rend_data))); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(introcirc->build_state->onehop_tunnel)); - tor_assert(!(rendcirc->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(introcirc, options); + assert_circ_anonymity_ok(rendcirc, options); + onion_address = rend_data_get_address(introcirc->rend_data);
- r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, - &entry); + r = rend_cache_lookup_entry(onion_address, -1, &entry); /* An invalid onion address is not possible else we have a big issue. */ tor_assert(r != -EINVAL); if (r < 0 || !rend_client_any_intro_points_usable(entry)) { diff --cc src/or/rendcommon.c index d9d39b1,125aa0f..1e5d1ab --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@@ -1067,52 -950,32 +950,82 @@@ rend_auth_decode_cookie(const char *coo return res; }
+/* Is this a rend client or server that allows direct (non-anonymous) + * connections? + * Clients must be specifically compiled and configured in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_allow_non_anonymous_connection() or + * rend_service_allow_non_anonymous_connection() whenever possible, so that + * checks are specific to Single Onion Services or Tor2web. */ +int +rend_allow_non_anonymous_connection(const or_options_t* options) +{ + return (rend_client_allow_non_anonymous_connection(options) + || rend_service_allow_non_anonymous_connection(options)); +} + +/* Is this a rend client or server in non-anonymous mode? + * Clients must be specifically compiled in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_non_anonymous_mode_enabled() or + * rend_service_non_anonymous_mode_enabled() whenever possible, so that checks + * are specific to Single Onion Services or Tor2web. */ +int +rend_non_anonymous_mode_enabled(const or_options_t *options) +{ + return (rend_client_non_anonymous_mode_enabled(options) + || rend_service_non_anonymous_mode_enabled(options)); +} + +/* Make sure that tor only builds one-hop circuits when they would not + * compromise user anonymity. + * + * One-hop circuits are permitted in Tor2web or Single Onion modes. + * + * Tor2web or Single Onion modes are also allowed to make multi-hop circuits. + * For example, single onion HSDir circuits are 3-hop to prevent denial of + * service. + */ +void +assert_circ_anonymity_ok(origin_circuit_t *circ, + const or_options_t *options) +{ + tor_assert(options); + tor_assert(circ); + tor_assert(circ->build_state); + + if (circ->build_state->onehop_tunnel) { + tor_assert(rend_allow_non_anonymous_connection(options)); + } +} + + /* Return 1 iff the given <b>digest</b> of a permenanent hidden service key is + * equal to the digest in the origin circuit <b>ocirc</b> of its rend data . + * If the rend data doesn't exist, 0 is returned. This function is agnostic to + * the rend data version. */ + int + rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc, + const uint8_t *digest) + { + size_t rend_pk_digest_len; + const uint8_t *rend_pk_digest; + + tor_assert(ocirc); + tor_assert(digest); + + if (ocirc->rend_data == NULL) { + goto no_match; + } + + rend_pk_digest = rend_data_get_pk_digest(ocirc->rend_data, + &rend_pk_digest_len); + if (tor_memeq(rend_pk_digest, digest, rend_pk_digest_len)) { + goto match; + } + no_match: + return 0; + match: + return 1; + } + ++ diff --cc src/or/rendservice.c index 7083051,21e88eb..b6bf63d --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@@ -1681,8 -1474,12 +1682,10 @@@ rend_service_receive_introduction(origi goto err; }
-#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, options); tor_assert(circuit->rend_data); + /* XXX: This is version 2 specific (only one supported). */ + rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
/* We'll use this in a bazillion log messages */ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, @@@ -2970,17 -2702,21 +2971,19 @@@ rend_service_intro_has_opened(origin_ci char auth[DIGEST_LEN + 9]; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; int reason = END_CIRC_REASON_TORPROTOCOL; + const char *rend_pk_digest;
tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->cpath); tor_assert(circuit->rend_data); + /* XXX: This is version 2 specific (only on supported). */ + rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, - circuit->rend_data->rend_pk_digest, REND_SERVICE_ID_LEN); + rend_pk_digest, REND_SERVICE_ID_LEN);
- service = rend_service_get_by_pk_digest( - circuit->rend_data->rend_pk_digest); + service = rend_service_get_by_pk_digest(rend_pk_digest); if (!service) { log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.", safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id); @@@ -3168,9 -2906,16 +3173,14 @@@ rend_service_rendezvous_has_opened(orig tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); tor_assert(circuit->cpath); tor_assert(circuit->build_state); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->rend_data);
+ /* XXX: This is version 2 specific (only one supported). */ + rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, + NULL); + rend_cookie = circuit->rend_data->rend_cookie; + /* Declare the circuit dirty to avoid reuse, and for path-bias */ if (!circuit->base_.timestamp_dirty) circuit->base_.timestamp_dirty = time(NULL); diff --cc src/or/routerparse.c index cb2bc72,ef6273b..5bc2d39 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@@ -60,8 -16,8 +60,9 @@@ #include "circuitstats.h" #include "dirserv.h" #include "dirvote.h" + #include "parsecommon.h" #include "policies.h" +#include "protover.h" #include "rendcommon.h" #include "router.h" #include "routerlist.h" diff --cc src/test/test_dir.c index cf0b94c,4a2538b..c8daa18 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@@ -5698,48 -5346,66 +5698,109 @@@ test_dir_find_dl_schedule(void* data }
static void +test_dir_assumed_flags(void *arg) +{ + (void)arg; + smartlist_t *tokens = smartlist_new(); + memarea_t *area = memarea_new(); + routerstatus_t *rs = NULL; + + /* First, we should always assume that the Running flag is set, even + * when it isn't listed, since the consensus method is always + * higher than 4. */ + const char *str1 = + "r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 " + "192.168.0.1 9001 0\n" + "m thisoneislongerbecauseitisa256bitmddigest33\n" + "s Fast Guard Stable\n"; + + const char *cp = str1; + rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL, + 23, FLAV_MICRODESC); + tt_assert(rs); + tt_assert(rs->is_flagged_running); + tt_assert(! rs->is_valid); + tt_assert(! rs->is_exit); + tt_assert(rs->is_fast); + routerstatus_free(rs); + + /* With method 24 or later, we can assume "valid" is set. */ + cp = str1; + rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL, + 24, FLAV_MICRODESC); + tt_assert(rs); + tt_assert(rs->is_flagged_running); + tt_assert(rs->is_valid); + tt_assert(! rs->is_exit); + tt_assert(rs->is_fast); + + done: + smartlist_free(tokens); + memarea_drop_all(area); + routerstatus_free(rs); +} + ++static void + test_dir_post_parsing(void *arg) + { + (void) arg; + + /* Test the version parsing from an HS descriptor publish request. */ + { + const char *end; + const char *prefix = "/tor/hs/"; + int version = parse_hs_version_from_post("/tor/hs//publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/a/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/42/publish", prefix, &end); + tt_int_op(version, OP_EQ, 42); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/18163/publish", prefix, &end); + tt_int_op(version, OP_EQ, 18163); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("JUNKJUNKJUNK", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", "blah", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* Missing the '/' at the end of the prefix. */ + version = parse_hs_version_from_post("/tor/hs/3/publish", "/tor/hs", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/random/blah/tor/hs/3/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish/random/junk", + prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish/random/junk"); + version = parse_hs_version_from_post("/tor/hs/-1/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* INT_MAX */ + version = parse_hs_version_from_post("/tor/hs/2147483647/publish", + prefix, &end); + tt_int_op(version, OP_EQ, INT_MAX); + tt_str_op(end, OP_EQ, "/publish"); + /* INT_MAX + 1*/ + version = parse_hs_version_from_post("/tor/hs/2147483648/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + } + + done: + ; + } + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@@@ -5773,11 -5439,8 +5834,12 @@@ struct testcase_t dir_tests[] = DIR(fmt_control_ns, 0), DIR(dirserv_set_routerstatus_testing, 0), DIR(http_handling, 0), + DIR(purpose_needs_anonymity_returns_true_for_bridges, 0), + DIR(purpose_needs_anonymity_returns_false_for_own_bridge_desc, 0), + DIR(purpose_needs_anonymity_returns_true_by_default, 0), + DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0), + DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0), + DIR(post_parsing, 0), - DIR(purpose_needs_anonymity, 0), DIR(fetch_type, 0), DIR(packages, 0), DIR(download_status_schedule, 0), diff --cc src/test/test_hs.c index fd5ab15,3bf4e6d..67ce1cf --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@@ -14,8 -13,8 +14,9 @@@ #include "test.h" #include "control.h" #include "config.h" + #include "hs_common.h" #include "rendcommon.h" +#include "rendservice.h" #include "routerset.h" #include "circuitbuild.h" #include "test_helpers.h" diff --cc src/test/test_rendcache.c index a5d3f35,afcd117..7f72e44 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@@ -10,9 -10,9 +10,10 @@@ #include "router.h" #include "routerlist.h" #include "config.h" + #include "hs_common.h" #include <openssl/rsa.h> #include "rend_test_helpers.h" +#include "log_test_helpers.h"
#define NS_MODULE rend_cache
diff --cc src/trunnel/ed25519_cert.c index a492ada,dc1485d..dd5088b --- a/src/trunnel/ed25519_cert.c +++ b/src/trunnel/ed25519_cert.c @@@ -430,6 -410,557 +430,597 @@@ ed25519_cert_extension_parse(ed25519_ce } return result; } + link_specifier_t * + link_specifier_new(void) + { + link_specifier_t *val = trunnel_calloc(1, sizeof(link_specifier_t)); + if (NULL == val) + return NULL; + return val; + } + + /** Release all storage held inside 'obj', but do not free 'obj'. + */ + static void + link_specifier_clear(link_specifier_t *obj) + { + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->un_unrecognized); + TRUNNEL_DYNARRAY_CLEAR(&obj->un_unrecognized); + } + + void + link_specifier_free(link_specifier_t *obj) + { + if (obj == NULL) + return; + link_specifier_clear(obj); + trunnel_memwipe(obj, sizeof(link_specifier_t)); + trunnel_free_(obj); + } + + uint8_t + link_specifier_get_ls_type(link_specifier_t *inp) + { + return inp->ls_type; + } + int + link_specifier_set_ls_type(link_specifier_t *inp, uint8_t val) + { + inp->ls_type = val; + return 0; + } + uint8_t + link_specifier_get_ls_len(link_specifier_t *inp) + { + return inp->ls_len; + } + int + link_specifier_set_ls_len(link_specifier_t *inp, uint8_t val) + { + inp->ls_len = val; + return 0; + } + uint32_t + link_specifier_get_un_ipv4_addr(link_specifier_t *inp) + { + return inp->un_ipv4_addr; + } + int + link_specifier_set_un_ipv4_addr(link_specifier_t *inp, uint32_t val) + { + inp->un_ipv4_addr = val; + return 0; + } + uint16_t + link_specifier_get_un_ipv4_port(link_specifier_t *inp) + { + return inp->un_ipv4_port; + } + int + link_specifier_set_un_ipv4_port(link_specifier_t *inp, uint16_t val) + { + inp->un_ipv4_port = val; + return 0; + } + size_t + link_specifier_getlen_un_ipv6_addr(const link_specifier_t *inp) + { + (void)inp; return 16; + } + + uint8_t -link_specifier_get_un_ipv6_addr(const link_specifier_t *inp, size_t idx) ++link_specifier_get_un_ipv6_addr(link_specifier_t *inp, size_t idx) + { + trunnel_assert(idx < 16); + return inp->un_ipv6_addr[idx]; + } + ++uint8_t ++link_specifier_getconst_un_ipv6_addr(const link_specifier_t *inp, size_t idx) ++{ ++ return link_specifier_get_un_ipv6_addr((link_specifier_t*)inp, idx); ++} + int + link_specifier_set_un_ipv6_addr(link_specifier_t *inp, size_t idx, uint8_t elt) + { + trunnel_assert(idx < 16); + inp->un_ipv6_addr[idx] = elt; + return 0; + } + + uint8_t * + link_specifier_getarray_un_ipv6_addr(link_specifier_t *inp) + { + return inp->un_ipv6_addr; + } ++const uint8_t * ++link_specifier_getconstarray_un_ipv6_addr(const link_specifier_t *inp) ++{ ++ return (const uint8_t *)link_specifier_getarray_un_ipv6_addr((link_specifier_t*)inp); ++} + uint16_t + link_specifier_get_un_ipv6_port(link_specifier_t *inp) + { + return inp->un_ipv6_port; + } + int + link_specifier_set_un_ipv6_port(link_specifier_t *inp, uint16_t val) + { + inp->un_ipv6_port = val; + return 0; + } + size_t + link_specifier_getlen_un_legacy_id(const link_specifier_t *inp) + { + (void)inp; return 20; + } + + uint8_t -link_specifier_get_un_legacy_id(const link_specifier_t *inp, size_t idx) ++link_specifier_get_un_legacy_id(link_specifier_t *inp, size_t idx) + { + trunnel_assert(idx < 20); + return inp->un_legacy_id[idx]; + } + ++uint8_t ++link_specifier_getconst_un_legacy_id(const link_specifier_t *inp, size_t idx) ++{ ++ return link_specifier_get_un_legacy_id((link_specifier_t*)inp, idx); ++} + int + link_specifier_set_un_legacy_id(link_specifier_t *inp, size_t idx, uint8_t elt) + { + trunnel_assert(idx < 20); + inp->un_legacy_id[idx] = elt; + return 0; + } + + uint8_t * + link_specifier_getarray_un_legacy_id(link_specifier_t *inp) + { + return inp->un_legacy_id; + } ++const uint8_t * ++link_specifier_getconstarray_un_legacy_id(const link_specifier_t *inp) ++{ ++ return (const uint8_t *)link_specifier_getarray_un_legacy_id((link_specifier_t*)inp); ++} + size_t + link_specifier_getlen_un_ed25519_id(const link_specifier_t *inp) + { + (void)inp; return 32; + } + + uint8_t -link_specifier_get_un_ed25519_id(const link_specifier_t *inp, size_t idx) ++link_specifier_get_un_ed25519_id(link_specifier_t *inp, size_t idx) + { + trunnel_assert(idx < 32); + return inp->un_ed25519_id[idx]; + } + ++uint8_t ++link_specifier_getconst_un_ed25519_id(const link_specifier_t *inp, size_t idx) ++{ ++ return link_specifier_get_un_ed25519_id((link_specifier_t*)inp, idx); ++} + int + link_specifier_set_un_ed25519_id(link_specifier_t *inp, size_t idx, uint8_t elt) + { + trunnel_assert(idx < 32); + inp->un_ed25519_id[idx] = elt; + return 0; + } + + uint8_t * + link_specifier_getarray_un_ed25519_id(link_specifier_t *inp) + { + return inp->un_ed25519_id; + } ++const uint8_t * ++link_specifier_getconstarray_un_ed25519_id(const link_specifier_t *inp) ++{ ++ return (const uint8_t *)link_specifier_getarray_un_ed25519_id((link_specifier_t*)inp); ++} + size_t + link_specifier_getlen_un_unrecognized(const link_specifier_t *inp) + { + return TRUNNEL_DYNARRAY_LEN(&inp->un_unrecognized); + } + + uint8_t + link_specifier_get_un_unrecognized(link_specifier_t *inp, size_t idx) + { + return TRUNNEL_DYNARRAY_GET(&inp->un_unrecognized, idx); + } + ++uint8_t ++link_specifier_getconst_un_unrecognized(const link_specifier_t *inp, size_t idx) ++{ ++ return link_specifier_get_un_unrecognized((link_specifier_t*)inp, idx); ++} + int + link_specifier_set_un_unrecognized(link_specifier_t *inp, size_t idx, uint8_t elt) + { + TRUNNEL_DYNARRAY_SET(&inp->un_unrecognized, idx, elt); + return 0; + } + int + link_specifier_add_un_unrecognized(link_specifier_t *inp, uint8_t elt) + { + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->un_unrecognized, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + + uint8_t * + link_specifier_getarray_un_unrecognized(link_specifier_t *inp) + { + return inp->un_unrecognized.elts_; + } ++const uint8_t * ++link_specifier_getconstarray_un_unrecognized(const link_specifier_t *inp) ++{ ++ return (const uint8_t *)link_specifier_getarray_un_unrecognized((link_specifier_t*)inp); ++} + int + link_specifier_setlen_un_unrecognized(link_specifier_t *inp, size_t newlen) + { + uint8_t *newptr; + newptr = trunnel_dynarray_setlen(&inp->un_unrecognized.allocated_, + &inp->un_unrecognized.n_, inp->un_unrecognized.elts_, newlen, + sizeof(inp->un_unrecognized.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); - if (newptr == NULL) ++ if (newlen != 0 && newptr == NULL) + goto trunnel_alloc_failed; + inp->un_unrecognized.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + const char * + link_specifier_check(const link_specifier_t *obj) + { + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + switch (obj->ls_type) { + + case LS_IPV4: + break; + + case LS_IPV6: + break; + + case LS_LEGACY_ID: + break; + + case LS_ED25519_ID: + break; + + default: + break; + } + return NULL; + } + + ssize_t + link_specifier_encoded_len(const link_specifier_t *obj) + { + ssize_t result = 0; + + if (NULL != link_specifier_check(obj)) + return -1; + + + /* Length of u8 ls_type */ + result += 1; + + /* Length of u8 ls_len */ + result += 1; + switch (obj->ls_type) { + + case LS_IPV4: + + /* Length of u32 un_ipv4_addr */ + result += 4; + + /* Length of u16 un_ipv4_port */ + result += 2; + break; + + case LS_IPV6: + + /* Length of u8 un_ipv6_addr[16] */ + result += 16; + + /* Length of u16 un_ipv6_port */ + result += 2; + break; + + case LS_LEGACY_ID: + + /* Length of u8 un_legacy_id[20] */ + result += 20; + break; + + case LS_ED25519_ID: + + /* Length of u8 un_ed25519_id[32] */ + result += 32; + break; + + default: + + /* Length of u8 un_unrecognized[] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->un_unrecognized); + break; + } + return result; + } + int + link_specifier_clear_errors(link_specifier_t *obj) + { + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; + } + ssize_t + link_specifier_encode(uint8_t *output, const size_t avail, const link_specifier_t *obj) + { + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; + #ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = link_specifier_encoded_len(obj); + #endif + + uint8_t *backptr_ls_len = NULL; + + if (NULL != (msg = link_specifier_check(obj))) + goto check_failed; + + #ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); + #endif + + /* Encode u8 ls_type */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->ls_type)); + written += 1; ptr += 1; + + /* Encode u8 ls_len */ + backptr_ls_len = ptr; + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->ls_len)); + written += 1; ptr += 1; + { + size_t written_before_union = written; + + /* Encode union un[ls_type] */ + trunnel_assert(written <= avail); + switch (obj->ls_type) { + + case LS_IPV4: + + /* Encode u32 un_ipv4_addr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->un_ipv4_addr)); + written += 4; ptr += 4; + + /* Encode u16 un_ipv4_port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->un_ipv4_port)); + written += 2; ptr += 2; + break; + + case LS_IPV6: + + /* Encode u8 un_ipv6_addr[16] */ + trunnel_assert(written <= avail); + if (avail - written < 16) + goto truncated; + memcpy(ptr, obj->un_ipv6_addr, 16); + written += 16; ptr += 16; + + /* Encode u16 un_ipv6_port */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->un_ipv6_port)); + written += 2; ptr += 2; + break; + + case LS_LEGACY_ID: + + /* Encode u8 un_legacy_id[20] */ + trunnel_assert(written <= avail); + if (avail - written < 20) + goto truncated; + memcpy(ptr, obj->un_legacy_id, 20); + written += 20; ptr += 20; + break; + + case LS_ED25519_ID: + + /* Encode u8 un_ed25519_id[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->un_ed25519_id, 32); + written += 32; ptr += 32; + break; + + default: + + /* Encode u8 un_unrecognized[] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->un_unrecognized); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + if (elt_len) + memcpy(ptr, obj->un_unrecognized.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + break; + } + /* Write the length field back to ls_len */ + trunnel_assert(written >= written_before_union); + #if UINT8_MAX < SIZE_MAX + if (written - written_before_union > UINT8_MAX) + goto check_failed; + #endif + trunnel_set_uint8(backptr_ls_len, (written - written_before_union)); + } + + + trunnel_assert(ptr == output + written); + #ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + + #endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; + } + + /** As link_specifier_parse(), but do not allocate the output object. + */ + static ssize_t + link_specifier_parse_into(link_specifier_t *obj, const uint8_t *input, const size_t len_in) + { + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 ls_type */ + CHECK_REMAINING(1, truncated); + obj->ls_type = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 ls_len */ + CHECK_REMAINING(1, truncated); + obj->ls_len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + { + size_t remaining_after; + CHECK_REMAINING(obj->ls_len, truncated); + remaining_after = remaining - obj->ls_len; + remaining = obj->ls_len; + + /* Parse union un[ls_type] */ + switch (obj->ls_type) { + + case LS_IPV4: + + /* Parse u32 un_ipv4_addr */ + CHECK_REMAINING(4, fail); + obj->un_ipv4_addr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + + /* Parse u16 un_ipv4_port */ + CHECK_REMAINING(2, fail); + obj->un_ipv4_port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + break; + + case LS_IPV6: + + /* Parse u8 un_ipv6_addr[16] */ + CHECK_REMAINING(16, fail); + memcpy(obj->un_ipv6_addr, ptr, 16); + remaining -= 16; ptr += 16; + + /* Parse u16 un_ipv6_port */ + CHECK_REMAINING(2, fail); + obj->un_ipv6_port = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + break; + + case LS_LEGACY_ID: + + /* Parse u8 un_legacy_id[20] */ + CHECK_REMAINING(20, fail); + memcpy(obj->un_legacy_id, ptr, 20); + remaining -= 20; ptr += 20; + break; + + case LS_ED25519_ID: + + /* Parse u8 un_ed25519_id[32] */ + CHECK_REMAINING(32, fail); + memcpy(obj->un_ed25519_id, ptr, 32); + remaining -= 32; ptr += 32; + break; + + default: + + /* Parse u8 un_unrecognized[] */ + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unrecognized, remaining, {}); + obj->un_unrecognized.n_ = remaining; + if (remaining) + memcpy(obj->un_unrecognized.elts_, ptr, remaining); + ptr += remaining; remaining -= remaining; + break; + } + if (remaining != 0) + goto fail; + remaining = remaining_after; + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; + } + + ssize_t + link_specifier_parse(link_specifier_t **output, const uint8_t *input, const size_t len_in) + { + ssize_t result; + *output = link_specifier_new(); + if (NULL == *output) + return -1; + result = link_specifier_parse_into(*output, input, len_in); + if (result < 0) { + link_specifier_free(*output); + *output = NULL; + } + return result; + } ed25519_cert_t * ed25519_cert_new(void) { @@@ -937,3 -1438,283 +1528,293 @@@ ed25519_cert_parse(ed25519_cert_t **out } return result; } + link_specifier_list_t * + link_specifier_list_new(void) + { + link_specifier_list_t *val = trunnel_calloc(1, sizeof(link_specifier_list_t)); + if (NULL == val) + return NULL; + return val; + } + + /** Release all storage held inside 'obj', but do not free 'obj'. + */ + static void + link_specifier_list_clear(link_specifier_list_t *obj) + { + (void) obj; + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) { + link_specifier_free(TRUNNEL_DYNARRAY_GET(&obj->spec, idx)); + } + } + TRUNNEL_DYNARRAY_WIPE(&obj->spec); + TRUNNEL_DYNARRAY_CLEAR(&obj->spec); + } + + void + link_specifier_list_free(link_specifier_list_t *obj) + { + if (obj == NULL) + return; + link_specifier_list_clear(obj); + trunnel_memwipe(obj, sizeof(link_specifier_list_t)); + trunnel_free_(obj); + } + + uint8_t + link_specifier_list_get_n_spec(link_specifier_list_t *inp) + { + return inp->n_spec; + } + int + link_specifier_list_set_n_spec(link_specifier_list_t *inp, uint8_t val) + { + inp->n_spec = val; + return 0; + } + size_t + link_specifier_list_getlen_spec(const link_specifier_list_t *inp) + { + return TRUNNEL_DYNARRAY_LEN(&inp->spec); + } + + struct link_specifier_st * + link_specifier_list_get_spec(link_specifier_list_t *inp, size_t idx) + { + return TRUNNEL_DYNARRAY_GET(&inp->spec, idx); + } + ++ const struct link_specifier_st * ++link_specifier_list_getconst_spec(const link_specifier_list_t *inp, size_t idx) ++{ ++ return link_specifier_list_get_spec((link_specifier_list_t*)inp, idx); ++} + int + link_specifier_list_set_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt) + { + link_specifier_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->spec, idx); + if (oldval && oldval != elt) + link_specifier_free(oldval); + return link_specifier_list_set0_spec(inp, idx, elt); + } + int + link_specifier_list_set0_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt) + { + TRUNNEL_DYNARRAY_SET(&inp->spec, idx, elt); + return 0; + } + int + link_specifier_list_add_spec(link_specifier_list_t *inp, struct link_specifier_st * elt) + { + #if SIZE_MAX >= UINT8_MAX + if (inp->spec.n_ == UINT8_MAX) + goto trunnel_alloc_failed; + #endif + TRUNNEL_DYNARRAY_ADD(struct link_specifier_st *, &inp->spec, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + + struct link_specifier_st * * + link_specifier_list_getarray_spec(link_specifier_list_t *inp) + { + return inp->spec.elts_; + } ++const struct link_specifier_st * const * ++link_specifier_list_getconstarray_spec(const link_specifier_list_t *inp) ++{ ++ return (const struct link_specifier_st * const *)link_specifier_list_getarray_spec((link_specifier_list_t*)inp); ++} + int + link_specifier_list_setlen_spec(link_specifier_list_t *inp, size_t newlen) + { + struct link_specifier_st * *newptr; + #if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; + #endif + newptr = trunnel_dynarray_setlen(&inp->spec.allocated_, + &inp->spec.n_, inp->spec.elts_, newlen, + sizeof(inp->spec.elts_[0]), (trunnel_free_fn_t) link_specifier_free, + &inp->trunnel_error_code_); - if (newptr == NULL) ++ if (newlen != 0 && newptr == NULL) + goto trunnel_alloc_failed; + inp->spec.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + const char * + link_specifier_list_check(const link_specifier_list_t *obj) + { + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + { + const char *msg; + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) { + if (NULL != (msg = link_specifier_check(TRUNNEL_DYNARRAY_GET(&obj->spec, idx)))) + return msg; + } + } + if (TRUNNEL_DYNARRAY_LEN(&obj->spec) != obj->n_spec) + return "Length mismatch for spec"; + return NULL; + } + + ssize_t + link_specifier_list_encoded_len(const link_specifier_list_t *obj) + { + ssize_t result = 0; + + if (NULL != link_specifier_list_check(obj)) + return -1; + + + /* Length of u8 n_spec */ + result += 1; + + /* Length of struct link_specifier spec[n_spec] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) { + result += link_specifier_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->spec, idx)); + } + } + return result; + } + int + link_specifier_list_clear_errors(link_specifier_list_t *obj) + { + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; + } + ssize_t + link_specifier_list_encode(uint8_t *output, const size_t avail, const link_specifier_list_t *obj) + { + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; + #ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = link_specifier_list_encoded_len(obj); + #endif + + if (NULL != (msg = link_specifier_list_check(obj))) + goto check_failed; + + #ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); + #endif + + /* Encode u8 n_spec */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->n_spec)); + written += 1; ptr += 1; + + /* Encode struct link_specifier spec[n_spec] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->spec); ++idx) { + trunnel_assert(written <= avail); + result = link_specifier_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->spec, idx)); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + } + } + + + trunnel_assert(ptr == output + written); + #ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + + #endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; + } + + /** As link_specifier_list_parse(), but do not allocate the output + * object. + */ + static ssize_t + link_specifier_list_parse_into(link_specifier_list_t *obj, const uint8_t *input, const size_t len_in) + { + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 n_spec */ + CHECK_REMAINING(1, truncated); + obj->n_spec = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse struct link_specifier spec[n_spec] */ + TRUNNEL_DYNARRAY_EXPAND(link_specifier_t *, &obj->spec, obj->n_spec, {}); + { + link_specifier_t * elt; + unsigned idx; + for (idx = 0; idx < obj->n_spec; ++idx) { + result = link_specifier_parse(&elt, ptr, remaining); + if (result < 0) + goto relay_fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + TRUNNEL_DYNARRAY_ADD(link_specifier_t *, &obj->spec, elt, {link_specifier_free(elt);}); + } + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + relay_fail: + trunnel_assert(result < 0); + return result; + trunnel_alloc_failed: + return -1; + } + + ssize_t + link_specifier_list_parse(link_specifier_list_t **output, const uint8_t *input, const size_t len_in) + { + ssize_t result; + *output = link_specifier_list_new(); + if (NULL == *output) + return -1; + result = link_specifier_list_parse_into(*output, input, len_in); + if (result < 0) { + link_specifier_list_free(*output); + *output = NULL; + } + return result; + } diff --cc src/trunnel/ed25519_cert.h index 9804d84,1893957..571e6d1 --- a/src/trunnel/ed25519_cert.h +++ b/src/trunnel/ed25519_cert.h @@@ -157,6 -168,164 +184,196 @@@ const uint8_t * ed25519_cert_extension * success; return -1 and set the error code on 'inp' on failure. */ int ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen); + /** Return a newly allocated link_specifier with all elements set to + * zero. + */ + link_specifier_t *link_specifier_new(void); + /** Release all storage held by the link_specifier in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ + void link_specifier_free(link_specifier_t *victim); + /** Try to parse a link_specifier from the buffer in 'input', using up + * to 'len_in' bytes from the input buffer. On success, return the + * number of bytes consumed and set *output to the newly allocated + * link_specifier_t. On failure, return -2 if the input appears + * truncated, and -1 if the input is otherwise invalid. + */ + ssize_t link_specifier_parse(link_specifier_t **output, const uint8_t *input, const size_t len_in); + /** Return the number of bytes we expect to need to encode the + * link_specifier in 'obj'. On failure, return a negative value. Note + * that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ + ssize_t link_specifier_encoded_len(const link_specifier_t *obj); + /** Try to encode the link_specifier from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ + ssize_t link_specifier_encode(uint8_t *output, size_t avail, const link_specifier_t *input); + /** Check whether the internal state of the link_specifier in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ + const char *link_specifier_check(const link_specifier_t *obj); + /** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ + int link_specifier_clear_errors(link_specifier_t *obj); + /** Return the value of the ls_type field of the link_specifier_t in + * 'inp' + */ + uint8_t link_specifier_get_ls_type(link_specifier_t *inp); + /** Set the value of the ls_type field of the link_specifier_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ + int link_specifier_set_ls_type(link_specifier_t *inp, uint8_t val); + /** Return the value of the ls_len field of the link_specifier_t in + * 'inp' + */ + uint8_t link_specifier_get_ls_len(link_specifier_t *inp); + /** Set the value of the ls_len field of the link_specifier_t in 'inp' + * to 'val'. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ + int link_specifier_set_ls_len(link_specifier_t *inp, uint8_t val); + /** Return the value of the un_ipv4_addr field of the link_specifier_t + * in 'inp' + */ + uint32_t link_specifier_get_un_ipv4_addr(link_specifier_t *inp); + /** Set the value of the un_ipv4_addr field of the link_specifier_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ + int link_specifier_set_un_ipv4_addr(link_specifier_t *inp, uint32_t val); + /** Return the value of the un_ipv4_port field of the link_specifier_t + * in 'inp' + */ + uint16_t link_specifier_get_un_ipv4_port(link_specifier_t *inp); + /** Set the value of the un_ipv4_port field of the link_specifier_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ + int link_specifier_set_un_ipv4_port(link_specifier_t *inp, uint16_t val); + /** Return the (constant) length of the array holding the un_ipv6_addr + * field of the link_specifier_t in 'inp'. + */ + size_t link_specifier_getlen_un_ipv6_addr(const link_specifier_t *inp); + /** Return the element at position 'idx' of the fixed array field + * un_ipv6_addr of the link_specifier_t in 'inp'. + */ -uint8_t link_specifier_get_un_ipv6_addr(const link_specifier_t *inp, size_t idx); ++uint8_t link_specifier_get_un_ipv6_addr(link_specifier_t *inp, size_t idx); ++/** As link_specifier_get_un_ipv6_addr, but take and return a const ++ * pointer ++ */ ++uint8_t link_specifier_getconst_un_ipv6_addr(const link_specifier_t *inp, size_t idx); + /** Change the element at position 'idx' of the fixed array field + * un_ipv6_addr of the link_specifier_t in 'inp', so that it will hold + * the value 'elt'. + */ + int link_specifier_set_un_ipv6_addr(link_specifier_t *inp, size_t idx, uint8_t elt); + /** Return a pointer to the 16-element array field un_ipv6_addr of + * 'inp'. + */ + uint8_t * link_specifier_getarray_un_ipv6_addr(link_specifier_t *inp); ++/** As link_specifier_get_un_ipv6_addr, but take and return a const ++ * pointer ++ */ ++const uint8_t * link_specifier_getconstarray_un_ipv6_addr(const link_specifier_t *inp); + /** Return the value of the un_ipv6_port field of the link_specifier_t + * in 'inp' + */ + uint16_t link_specifier_get_un_ipv6_port(link_specifier_t *inp); + /** Set the value of the un_ipv6_port field of the link_specifier_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ + int link_specifier_set_un_ipv6_port(link_specifier_t *inp, uint16_t val); + /** Return the (constant) length of the array holding the un_legacy_id + * field of the link_specifier_t in 'inp'. + */ + size_t link_specifier_getlen_un_legacy_id(const link_specifier_t *inp); + /** Return the element at position 'idx' of the fixed array field + * un_legacy_id of the link_specifier_t in 'inp'. + */ -uint8_t link_specifier_get_un_legacy_id(const link_specifier_t *inp, size_t idx); ++uint8_t link_specifier_get_un_legacy_id(link_specifier_t *inp, size_t idx); ++/** As link_specifier_get_un_legacy_id, but take and return a const ++ * pointer ++ */ ++uint8_t link_specifier_getconst_un_legacy_id(const link_specifier_t *inp, size_t idx); + /** Change the element at position 'idx' of the fixed array field + * un_legacy_id of the link_specifier_t in 'inp', so that it will hold + * the value 'elt'. + */ + int link_specifier_set_un_legacy_id(link_specifier_t *inp, size_t idx, uint8_t elt); + /** Return a pointer to the 20-element array field un_legacy_id of + * 'inp'. + */ + uint8_t * link_specifier_getarray_un_legacy_id(link_specifier_t *inp); ++/** As link_specifier_get_un_legacy_id, but take and return a const ++ * pointer ++ */ ++const uint8_t * link_specifier_getconstarray_un_legacy_id(const link_specifier_t *inp); + /** Return the (constant) length of the array holding the + * un_ed25519_id field of the link_specifier_t in 'inp'. + */ + size_t link_specifier_getlen_un_ed25519_id(const link_specifier_t *inp); + /** Return the element at position 'idx' of the fixed array field + * un_ed25519_id of the link_specifier_t in 'inp'. + */ -uint8_t link_specifier_get_un_ed25519_id(const link_specifier_t *inp, size_t idx); ++uint8_t link_specifier_get_un_ed25519_id(link_specifier_t *inp, size_t idx); ++/** As link_specifier_get_un_ed25519_id, but take and return a const ++ * pointer ++ */ ++uint8_t link_specifier_getconst_un_ed25519_id(const link_specifier_t *inp, size_t idx); + /** Change the element at position 'idx' of the fixed array field + * un_ed25519_id of the link_specifier_t in 'inp', so that it will + * hold the value 'elt'. + */ + int link_specifier_set_un_ed25519_id(link_specifier_t *inp, size_t idx, uint8_t elt); + /** Return a pointer to the 32-element array field un_ed25519_id of + * 'inp'. + */ + uint8_t * link_specifier_getarray_un_ed25519_id(link_specifier_t *inp); ++/** As link_specifier_get_un_ed25519_id, but take and return a const ++ * pointer ++ */ ++const uint8_t * link_specifier_getconstarray_un_ed25519_id(const link_specifier_t *inp); + /** Return the length of the dynamic array holding the un_unrecognized + * field of the link_specifier_t in 'inp'. + */ + size_t link_specifier_getlen_un_unrecognized(const link_specifier_t *inp); + /** Return the element at position 'idx' of the dynamic array field + * un_unrecognized of the link_specifier_t in 'inp'. + */ + uint8_t link_specifier_get_un_unrecognized(link_specifier_t *inp, size_t idx); ++/** As link_specifier_get_un_unrecognized, but take and return a const ++ * pointer ++ */ ++uint8_t link_specifier_getconst_un_unrecognized(const link_specifier_t *inp, size_t idx); + /** Change the element at position 'idx' of the dynamic array field + * un_unrecognized of the link_specifier_t in 'inp', so that it will + * hold the value 'elt'. + */ + int link_specifier_set_un_unrecognized(link_specifier_t *inp, size_t idx, uint8_t elt); + /** Append a new element 'elt' to the dynamic array field + * un_unrecognized of the link_specifier_t in 'inp'. + */ + int link_specifier_add_un_unrecognized(link_specifier_t *inp, uint8_t elt); + /** Return a pointer to the variable-length array field + * un_unrecognized of 'inp'. + */ + uint8_t * link_specifier_getarray_un_unrecognized(link_specifier_t *inp); ++/** As link_specifier_get_un_unrecognized, but take and return a const ++ * pointer ++ */ ++const uint8_t * link_specifier_getconstarray_un_unrecognized(const link_specifier_t *inp); + /** Change the length of the variable-length array field + * un_unrecognized of 'inp' to 'newlen'.Fill extra elements with 0. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ + int link_specifier_setlen_un_unrecognized(link_specifier_t *inp, size_t newlen); /** Return a newly allocated ed25519_cert with all elements set to * zero. */ @@@ -316,9 -468,81 +533,92 @@@ int ed25519_cert_set_signature(ed25519_ /** Return a pointer to the 64-element array field signature of 'inp'. */ uint8_t * ed25519_cert_getarray_signature(ed25519_cert_t *inp); +/** As ed25519_cert_get_signature, but take and return a const pointer + */ +const uint8_t * ed25519_cert_getconstarray_signature(const ed25519_cert_t *inp); + /** Return a newly allocated link_specifier_list with all elements set + * to zero. + */ + link_specifier_list_t *link_specifier_list_new(void); + /** Release all storage held by the link_specifier_list in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ + void link_specifier_list_free(link_specifier_list_t *victim); + /** Try to parse a link_specifier_list from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated link_specifier_list_t. On failure, return -2 if the input + * appears truncated, and -1 if the input is otherwise invalid. + */ + ssize_t link_specifier_list_parse(link_specifier_list_t **output, const uint8_t *input, const size_t len_in); + /** Return the number of bytes we expect to need to encode the + * link_specifier_list in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ + ssize_t link_specifier_list_encoded_len(const link_specifier_list_t *obj); + /** Try to encode the link_specifier_list from 'input' into the buffer + * at 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ + ssize_t link_specifier_list_encode(uint8_t *output, size_t avail, const link_specifier_list_t *input); + /** Check whether the internal state of the link_specifier_list in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ + const char *link_specifier_list_check(const link_specifier_list_t *obj); + /** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ + int link_specifier_list_clear_errors(link_specifier_list_t *obj); + /** Return the value of the n_spec field of the link_specifier_list_t + * in 'inp' + */ + uint8_t link_specifier_list_get_n_spec(link_specifier_list_t *inp); + /** Set the value of the n_spec field of the link_specifier_list_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ + int link_specifier_list_set_n_spec(link_specifier_list_t *inp, uint8_t val); + /** Return the length of the dynamic array holding the spec field of + * the link_specifier_list_t in 'inp'. + */ + size_t link_specifier_list_getlen_spec(const link_specifier_list_t *inp); + /** Return the element at position 'idx' of the dynamic array field + * spec of the link_specifier_list_t in 'inp'. + */ + struct link_specifier_st * link_specifier_list_get_spec(link_specifier_list_t *inp, size_t idx); ++/** As link_specifier_list_get_spec, but take and return a const ++ * pointer ++ */ ++ const struct link_specifier_st * link_specifier_list_getconst_spec(const link_specifier_list_t *inp, size_t idx); + /** Change the element at position 'idx' of the dynamic array field + * spec of the link_specifier_list_t in 'inp', so that it will hold + * the value 'elt'. Free the previous value, if any. + */ + int link_specifier_list_set_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt); + /** As link_specifier_list_set_spec, but does not free the previous + * value. + */ + int link_specifier_list_set0_spec(link_specifier_list_t *inp, size_t idx, struct link_specifier_st * elt); + /** Append a new element 'elt' to the dynamic array field spec of the + * link_specifier_list_t in 'inp'. + */ + int link_specifier_list_add_spec(link_specifier_list_t *inp, struct link_specifier_st * elt); + /** Return a pointer to the variable-length array field spec of 'inp'. + */ + struct link_specifier_st * * link_specifier_list_getarray_spec(link_specifier_list_t *inp); ++/** As link_specifier_list_get_spec, but take and return a const ++ * pointer ++ */ ++const struct link_specifier_st * const * link_specifier_list_getconstarray_spec(const link_specifier_list_t *inp); + /** Change the length of the variable-length array field spec of 'inp' + * to 'newlen'.Fill extra elements with NULL; free removed elements. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ + int link_specifier_list_setlen_spec(link_specifier_list_t *inp, size_t newlen);
#endif
tor-commits@lists.torproject.org