diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/compat.c | 39 | ||||
-rw-r--r-- | src/common/compat.h | 2 | ||||
-rw-r--r-- | src/common/memarea.c | 91 | ||||
-rw-r--r-- | src/common/util.c | 4 | ||||
-rw-r--r-- | src/or/buffers.c | 10 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 2 | ||||
-rw-r--r-- | src/or/circuituse.c | 4 | ||||
-rw-r--r-- | src/or/connection_edge.c | 6 | ||||
-rw-r--r-- | src/or/control.c | 13 | ||||
-rw-r--r-- | src/or/control.h | 5 | ||||
-rw-r--r-- | src/or/directory.c | 72 | ||||
-rw-r--r-- | src/or/dirserv.c | 4 | ||||
-rw-r--r-- | src/or/geoip.c | 2 | ||||
-rw-r--r-- | src/or/main.c | 2 | ||||
-rw-r--r-- | src/or/or.h | 8 | ||||
-rw-r--r-- | src/or/relay.c | 20 | ||||
-rw-r--r-- | src/or/rendservice.c | 37 | ||||
-rw-r--r-- | src/or/routerparse.c | 2 | ||||
-rw-r--r-- | src/test/log_test_helpers.h | 4 | ||||
-rwxr-xr-x | src/test/test-network.sh | 185 | ||||
-rw-r--r-- | src/test/test_controller.c | 40 | ||||
-rw-r--r-- | src/test/test_dir.c | 135 | ||||
-rw-r--r-- | src/test/test_util.c | 29 | ||||
-rw-r--r-- | src/win32/orconfig.h | 2 |
24 files changed, 512 insertions, 206 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index 0dbede6bed..da4283fbaa 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -3476,6 +3476,45 @@ tor_getpass(const char *prompt, char *output, size_t buflen) #endif } +/** Read at most one less than the number of characters specified by + * <b>size</b> from the given <b>stream</b> and store it in <b>str</b>. + * + * Reading stops when a newline character is found or at EOF or error. If any + * characters are read and there's no error, a trailing NUL byte is appended to + * the end of <b>str</b>. + * + * Upon successful completion, this function returns a pointer to the string + * <b>str</b>. If EOF occurs before any characters are read the function will + * return NULL and the content of <b>str</b> is unchanged. Upon error, the + * function returns NULL and the caller must check for error using foef(3) and + * ferror(3). + */ +char * +tor_fgets(char *str, int size, FILE *stream) +{ + char *ret; + + /* Reset errno before our call to fgets(3) to avoid a situation where the + * caller is calling us again because we just returned NULL and errno == + * EAGAIN, but when they call us again we will always return NULL because the + * error flag on the file handler remains set and errno is set to EAGAIN. + */ + errno = 0; + + ret = fgets(str, size, stream); + + /* FreeBSD, OpenBSD, Linux (glibc), and Linux (musl) seem to disagree about + * what to do in the given situation. We check if the stream has been flagged + * with an error-bit and return NULL in that situation if errno is also set + * to EAGAIN. + */ + if (ferror(stream) && errno == EAGAIN) { + return NULL; + } + + return ret; +} + /** Return the amount of free disk space we have permission to use, in * bytes. Return -1 if the amount of free space can't be determined. */ int64_t diff --git a/src/common/compat.h b/src/common/compat.h index ee1c9454de..1f51ece61f 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -740,6 +740,8 @@ STATIC int tor_ersatz_socketpair(int family, int type, int protocol, ssize_t tor_getpass(const char *prompt, char *output, size_t buflen); +char *tor_fgets(char *str, int size, FILE *stream); + /* This needs some of the declarations above so we include it here. */ #include "compat_threads.h" diff --git a/src/common/memarea.c b/src/common/memarea.c index 7d16b702e3..12781e76ea 100644 --- a/src/common/memarea.c +++ b/src/common/memarea.c @@ -12,6 +12,9 @@ #include "util.h" #include "compat.h" #include "torlog.h" +#include "container.h" + +#ifndef DISABLE_MEMORY_SENTINELS /** If true, we try to detect any attempts to write beyond the length of a * memarea. */ @@ -304,3 +307,91 @@ memarea_assert_ok(memarea_t *area) } } +#else + +struct memarea_t { + smartlist_t *pieces; +}; + +memarea_t * +memarea_new(void) +{ + memarea_t *ma = tor_malloc_zero(sizeof(memarea_t)); + ma->pieces = smartlist_new(); + return ma; +} +void +memarea_drop_all(memarea_t *area) +{ + memarea_clear(area); + smartlist_free(area->pieces); + tor_free(area); +} +void +memarea_clear(memarea_t *area) +{ + SMARTLIST_FOREACH(area->pieces, void *, p, tor_free_(p)); + smartlist_clear(area->pieces); +} +int +memarea_owns_ptr(const memarea_t *area, const void *ptr) +{ + SMARTLIST_FOREACH(area->pieces, const void *, p, if (ptr == p) return 1;); + return 0; +} + +void * +memarea_alloc(memarea_t *area, size_t sz) +{ + void *result = tor_malloc(sz); + smartlist_add(area->pieces, result); + return result; +} + +void * +memarea_alloc_zero(memarea_t *area, size_t sz) +{ + void *result = tor_malloc_zero(sz); + smartlist_add(area->pieces, result); + return result; +} +void * +memarea_memdup(memarea_t *area, const void *s, size_t n) +{ + void *r = memarea_alloc(area, n); + memcpy(r, s, n); + return r; +} +char * +memarea_strdup(memarea_t *area, const char *s) +{ + size_t n = strlen(s); + char *r = memarea_alloc(area, n+1); + memcpy(r, s, n); + r[n] = 0; + return r; +} +char * +memarea_strndup(memarea_t *area, const char *s, size_t n) +{ + size_t ln = strnlen(s, n); + char *r = memarea_alloc(area, ln+1); + memcpy(r, s, ln); + r[ln] = 0; + return r; +} +void +memarea_get_stats(memarea_t *area, + size_t *allocated_out, size_t *used_out) +{ + (void)area; + *allocated_out = *used_out = 128; +} +void +memarea_assert_ok(memarea_t *area) +{ + (void)area; +} + +#endif + diff --git a/src/common/util.c b/src/common/util.c index f980fa296c..a69d887e30 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -5020,7 +5020,7 @@ tor_read_all_handle(FILE *h, char *buf, size_t count, while (numread != count) { /* Use fgets because that is what we use in log_from_pipe() */ - retval = fgets(buf+numread, (int)(count-numread), h); + retval = tor_fgets(buf+numread, (int)(count-numread), h); if (NULL == retval) { if (feof(h)) { log_debug(LD_GENERAL, "fgets() reached end of file"); @@ -5355,7 +5355,7 @@ get_string_from_pipe(FILE *stream, char *buf_out, size_t count) tor_assert(count <= INT_MAX); - retval = fgets(buf_out, (int)count, stream); + retval = tor_fgets(buf_out, (int)count, stream); if (!retval) { if (feof(stream)) { diff --git a/src/or/buffers.c b/src/or/buffers.c index 603da1bb6e..155b1935ed 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -83,7 +83,11 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, #define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0]) /* We leave this many NUL bytes at the end of the buffer. */ +#ifdef DISABLE_MEMORY_SENTINELS +#define SENTINEL_LEN 0 +#else #define SENTINEL_LEN 4 +#endif /* Header size plus NUL bytes at the end */ #define CHUNK_OVERHEAD (CHUNK_HEADER_LEN + SENTINEL_LEN) @@ -97,18 +101,22 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, #define DEBUG_SENTINEL -#ifdef DEBUG_SENTINEL +#if defined(DEBUG_SENTINEL) && !defined(DISABLE_MEMORY_SENTINELS) #define DBG_S(s) s #else #define DBG_S(s) (void)0 #endif +#ifdef DISABLE_MEMORY_SENTINELS +#define CHUNK_SET_SENTINEL(chunk, alloclen) STMT_NIL +#else #define CHUNK_SET_SENTINEL(chunk, alloclen) do { \ uint8_t *a = (uint8_t*) &(chunk)->mem[(chunk)->memlen]; \ DBG_S(uint8_t *b = &((uint8_t*)(chunk))[(alloclen)-SENTINEL_LEN]); \ DBG_S(tor_assert(a == b)); \ memset(a,0,SENTINEL_LEN); \ } while (0) +#endif /** Return the next character in <b>chunk</b> onto which data can be appended. * If the chunk is full, this might be off the end of chunk->mem. */ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 79962e8dbb..14e829b03e 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -819,7 +819,7 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) } if (options->FastFirstHopPK == -1) { /* option is "auto", so look at the consensus. */ - return networkstatus_get_param(NULL, "usecreatefast", 1, 0, 1); + return networkstatus_get_param(NULL, "usecreatefast", 0, 0, 1); } return options->FastFirstHopPK; diff --git a/src/or/circuituse.c b/src/or/circuituse.c index c2b450606b..113643a4c9 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1240,8 +1240,8 @@ circuit_predict_and_launch_new(void) /** Build a new test circuit every 5 minutes */ #define TESTING_CIRCUIT_INTERVAL 300 -/** This function is called once a second, if router_have_min_dir_info() is - * true. Its job is to make sure all services we offer have enough circuits +/** This function is called once a second, if router_have_minimum_dir_info() + * is true. Its job is to make sure all services we offer have enough circuits * available. Some services just want enough circuits for current tasks, * whereas others want a minimum set of idle circuits hanging around. */ diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index dac0c01012..f892dd7186 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -29,7 +29,7 @@ * <li>DNS lookup streams, created on the exit side in response to * a RELAY_RESOLVE cell from a client. * <li>Tunneled directory streams, created on the directory cache side - * in response to a RELAY_BEGINDIR cell. These streams attach directly + * in response to a RELAY_BEGIN_DIR cell. These streams attach directly * to a dir_connection_t object without ever using TCP. * </ul> * @@ -1762,7 +1762,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, conn->entry_cfg.ipv6_traffic = 0; /* Still handling CONNECT. Now, check for exit enclaves. (Which we - * don't do on BEGINDIR, or when there is a chosen exit.) + * don't do on BEGIN_DIR, or when there is a chosen exit.) * * TODO: Should we remove this? Exit enclaves are nutty and don't * work very well @@ -3001,7 +3001,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, return; } -/** Read a RELAY_BEGIN or RELAY_BEGINDIR cell from <b>cell</b>, decode it, and +/** Read a RELAY_BEGIN or RELAY_BEGIN_DIR cell from <b>cell</b>, decode it, and * place the result in <b>bcell</b>. On success return 0; on failure return * <0 and set *<b>end_reason_out</b> to the end reason we should send back to * the client. diff --git a/src/or/control.c b/src/or/control.c index b0a687679d..8ab31f18f7 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2824,12 +2824,13 @@ getinfo_helper_events(control_connection_t *control_conn, /** Implementation helper for GETINFO: knows how to enumerate hidden services * created via the control port. */ -static int +STATIC int getinfo_helper_onions(control_connection_t *control_conn, const char *question, char **answer, const char **errmsg) { smartlist_t *onion_list = NULL; + (void) errmsg; /* no errors from this method */ if (control_conn && !strcmp(question, "onions/current")) { onion_list = control_conn->ephemeral_onion_services; @@ -2839,13 +2840,13 @@ getinfo_helper_onions(control_connection_t *control_conn, return 0; } if (!onion_list || smartlist_len(onion_list) == 0) { - if (errmsg) { - *errmsg = "No onion services of the specified type."; + if (answer) { + *answer = tor_strdup(""); } - return -1; - } - if (answer) { + } else { + if (answer) { *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL); + } } return 0; diff --git a/src/or/control.h b/src/or/control.h index 6330c85571..16ba1ed8f0 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -262,6 +262,11 @@ STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, STATIC rend_authorized_client_t * add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out); +STATIC int getinfo_helper_onions( + control_connection_t *control_conn, + const char *question, + char **answer, + const char **errmsg); STATIC void getinfo_helper_downloads_networkstatus( const char *flavor, download_status_t **dl_to_emit, diff --git a/src/or/directory.c b/src/or/directory.c index 70437fe755..e60952fcf4 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -14,6 +14,7 @@ #include "connection.h" #include "connection_edge.h" #include "control.h" +#include "compat.h" #define DIRECTORY_PRIVATE #include "directory.h" #include "dirserv.h" @@ -1079,12 +1080,10 @@ static int directory_command_should_use_begindir(const or_options_t *options, const tor_addr_t *or_addr, int or_port, const tor_addr_t *dir_addr, int dir_port, - uint8_t router_purpose, dir_indirection_t indirection, const char **reason) { - (void) router_purpose; - (void) dir_addr; + (void)dir_addr; tor_assert(reason); *reason = NULL; @@ -1198,8 +1197,9 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, const int use_begindir = directory_command_should_use_begindir(options, &or_addr_port->addr, or_addr_port->port, &dir_addr_port->addr, dir_addr_port->port, - router_purpose, indirection, + indirection, &begindir_reason); + /* Will the connection go via a three-hop Tor circuit? Note that this * is separate from whether it will use_begindir. */ const int anonymized_connection = dirind_is_anon(indirection); @@ -1477,7 +1477,9 @@ directory_send_command(dir_connection_t *conn, char decorated_address[128]; smartlist_t *headers = smartlist_new(); char *url; + size_t url_len; char request[8192]; + size_t request_len, total_request_len = 0; const char *httpcommand = NULL; tor_assert(conn); @@ -1623,8 +1625,14 @@ directory_send_command(dir_connection_t *conn, } tor_snprintf(request, sizeof(request), "%s %s", httpcommand, proxystring); - connection_write_to_buf(request, strlen(request), TO_CONN(conn)); - connection_write_to_buf(url, strlen(url), TO_CONN(conn)); + + request_len = strlen(request); + total_request_len += request_len; + connection_write_to_buf(request, request_len, TO_CONN(conn)); + + url_len = strlen(url); + total_request_len += url_len; + connection_write_to_buf(url, url_len, TO_CONN(conn)); tor_free(url); if (!strcmp(httpcommand, "POST") || payload) { @@ -1639,15 +1647,27 @@ directory_send_command(dir_connection_t *conn, tor_free(header); } - connection_write_to_buf(request, strlen(request), TO_CONN(conn)); + request_len = strlen(request); + total_request_len += request_len; + connection_write_to_buf(request, request_len, TO_CONN(conn)); if (payload) { /* then send the payload afterwards too */ connection_write_to_buf(payload, payload_len, TO_CONN(conn)); + total_request_len += payload_len; } SMARTLIST_FOREACH(headers, char *, h, tor_free(h)); smartlist_free(headers); + + log_debug(LD_DIR, + "Sent request to directory server '%s:%d': " + "(purpose: %d, request size: " U64_FORMAT ", " + "payload size: " U64_FORMAT ")", + conn->base_.address, conn->base_.port, + conn->base_.purpose, + U64_PRINTF_ARG(total_request_len), + U64_PRINTF_ARG(payload ? payload_len : 0)); } /** Parse an HTTP request string <b>headers</b> of the form @@ -1941,6 +1961,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn) conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC); time_t now = time(NULL); int src_code; + size_t received_bytes; + + received_bytes = connection_get_inbuf_len(TO_CONN(conn)); switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, @@ -1967,12 +1990,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn) } if (!reason) reason = tor_strdup("[no reason given]"); - log_debug(LD_DIR, + tor_log(LOG_DEBUG, LD_DIR, "Received response from directory server '%s:%d': %d %s " - "(purpose: %d)", + "(purpose: %d, response size: " U64_FORMAT +#ifdef MEASUREMENTS_21206 + ", data cells received: %d, data cells sent: %d" +#endif + ", compression: %d)", conn->base_.address, conn->base_.port, status_code, - escaped(reason), - conn->base_.purpose); + escaped(reason), conn->base_.purpose, + U64_PRINTF_ARG(received_bytes), +#ifdef MEASUREMENTS_21206 + conn->data_cells_received, conn->data_cells_sent, +#endif + compression); if (conn->guard_state) { /* we count the connection as successful once we can read from it. We do @@ -2104,7 +2135,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) networkstatus_consensus_download_failed(status_code, flavname); return -1; } - log_info(LD_DIR,"Received consensus directory (size %d) from server " + log_info(LD_DIR,"Received consensus directory (body size %d) from server " "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); if ((r=networkstatus_set_current_consensus(body, flavname, 0, conn->identity_digest))<0) { @@ -2143,8 +2174,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn) tor_free(body); tor_free(headers); tor_free(reason); return -1; } - log_info(LD_DIR,"Received authority certificates (size %d) from server " - "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); + log_info(LD_DIR,"Received authority certificates (body size %d) from " + "server '%s:%d'", + (int)body_len, conn->base_.address, conn->base_.port); /* * Tell trusted_dirs_load_certs_from_string() whether it was by fp @@ -2179,7 +2211,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { const char *msg; int st; - log_info(LD_DIR,"Got votes (size %d) from server %s:%d", + log_info(LD_DIR,"Got votes (body size %d) from server %s:%d", (int)body_len, conn->base_.address, conn->base_.port); if (status_code != 200) { log_warn(LD_DIR, @@ -2199,7 +2231,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) } if (conn->base_.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) { const char *msg = NULL; - log_info(LD_DIR,"Got detached signatures (size %d) from server %s:%d", + log_info(LD_DIR,"Got detached signatures (body size %d) from server %s:%d", (int)body_len, conn->base_.address, conn->base_.port); if (status_code != 200) { log_warn(LD_DIR, @@ -2223,7 +2255,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) int n_asked_for = 0; int descriptor_digests = conn->requested_resource && !strcmpstart(conn->requested_resource,"d/"); - log_info(LD_DIR,"Received %s (size %d) from server '%s:%d'", + log_info(LD_DIR,"Received %s (body size %d) from server '%s:%d'", was_ei ? "extra server info" : "server info", (int)body_len, conn->base_.address, conn->base_.port); if (conn->requested_resource && @@ -2301,7 +2333,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) { smartlist_t *which = NULL; log_info(LD_DIR,"Received answer to microdescriptor request (status %d, " - "size %d) from server '%s:%d'", + "body size %d) from server '%s:%d'", status_code, (int)body_len, conn->base_.address, conn->base_.port); tor_assert(conn->requested_resource && @@ -2453,7 +2485,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) conn->identity_digest, \ NULL) ) tor_assert(conn->rend_data); - log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " + log_info(LD_REND,"Received rendezvous descriptor (body size %d, status %d " "(%s))", (int)body_len, status_code, escaped(reason)); switch (status_code) { @@ -3727,7 +3759,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, if (connection_dir_is_encrypted(conn) && !strcmpstart(url,"/tor/rendezvous2/publish")) { if (rend_cache_store_v2_desc_as_dir(body) < 0) { - log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.", + log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.", (int)body_len, conn->base_.address); write_http_status_line(conn, 400, "Invalid v2 service descriptor rejected"); diff --git a/src/or/dirserv.c b/src/or/dirserv.c index f01668adcb..2c3ce100df 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -2701,7 +2701,7 @@ dirserv_read_measured_bandwidths(const char *from_file, return -1; } - if (!fgets(line, sizeof(line), fp) + if (!tor_fgets(line, sizeof(line), fp) || !strlen(line) || line[strlen(line)-1] != '\n') { log_warn(LD_DIRSERV, "Long or truncated time in bandwidth file: %s", escaped(line)); @@ -2731,7 +2731,7 @@ dirserv_read_measured_bandwidths(const char *from_file, while (!feof(fp)) { measured_bw_line_t parsed_line; - if (fgets(line, sizeof(line), fp) && strlen(line)) { + if (tor_fgets(line, sizeof(line), fp) && strlen(line)) { if (measured_bw_line_parse(&parsed_line, line) != -1) { /* Also cache the line for dirserv_get_bandwidth_for_router() */ dirserv_cache_measured_bw(&parsed_line, file_time); diff --git a/src/or/geoip.c b/src/or/geoip.c index 74811ea643..a8dc807c19 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -346,7 +346,7 @@ geoip_load_file(sa_family_t family, const char *filename) (family == AF_INET) ? "IPv4" : "IPv6", filename); while (!feof(f)) { char buf[512]; - if (fgets(buf, (int)sizeof(buf), f) == NULL) + if (tor_fgets(buf, (int)sizeof(buf), f) == NULL) break; crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf)); /* FFFF track full country name. */ diff --git a/src/or/main.c b/src/or/main.c index 5549f97998..475587eacd 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2405,7 +2405,7 @@ do_main_loop(void) } /* Setup shared random protocol subsystem. */ - if (authdir_mode_publishes_statuses(get_options())) { + if (authdir_mode_v3(get_options())) { if (sr_init(1) < 0) { return -1; } diff --git a/src/or/or.h b/src/or/or.h index 0db9f23604..0e2dc2401b 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1786,6 +1786,14 @@ typedef struct dir_connection_t { * that's going away and being used on channels instead. The dirserver still * needs this for the incoming side, so it's moved here. */ uint64_t dirreq_id; + +#ifdef MEASUREMENTS_21206 + /** Number of RELAY_DATA cells received. */ + uint32_t data_cells_received; + + /** Number of RELAY_DATA cells sent. */ + uint32_t data_cells_sent; +#endif } dir_connection_t; /** Subtype of connection_t for an connection to a controller. */ diff --git a/src/or/relay.c b/src/or/relay.c index 2e76a8ec36..6b3f34f3e5 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -732,6 +732,16 @@ connection_edge_send_command(edge_connection_t *fromconn, return -1; } +#ifdef MEASUREMENTS_21206 + /* Keep track of the number of RELAY_DATA cells sent for directory + * connections. */ + connection_t *linked_conn = TO_CONN(fromconn)->linked_conn; + + if (linked_conn && linked_conn->type == CONN_TYPE_DIR) { + ++(TO_DIR_CONN(linked_conn)->data_cells_sent); + } +#endif + return relay_send_command_from_edge(fromconn->stream_id, circ, relay_command, payload, payload_len, cpath_layer); @@ -1585,6 +1595,16 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, connection_write_to_buf((char*)(cell->payload + RELAY_HEADER_SIZE), rh.length, TO_CONN(conn)); +#ifdef MEASUREMENTS_21206 + /* Count number of RELAY_DATA cells received on a linked directory + * connection. */ + connection_t *linked_conn = TO_CONN(conn)->linked_conn; + + if (linked_conn && linked_conn->type == CONN_TYPE_DIR) { + ++(TO_DIR_CONN(linked_conn)->data_cells_received); + } +#endif + if (!optimistic_data) { /* Only send a SENDME if we're not getting optimistic data; otherwise * a SENDME could arrive before the CONNECTED. diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 79fec9fd5c..eeccd4347b 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -1032,7 +1032,6 @@ static void rend_service_update_descriptor(rend_service_t *service) { rend_service_descriptor_t *d; - origin_circuit_t *circ; int i; rend_service_descriptor_free(service->desc); @@ -1053,9 +1052,10 @@ rend_service_update_descriptor(rend_service_t *service) /* This intro point won't be listed in the descriptor... */ intro_svc->listed_in_last_desc = 0; - circ = find_intro_circuit(intro_svc, service->pk_digest); - if (!circ || circ->base_.purpose != CIRCUIT_PURPOSE_S_INTRO) { - /* This intro point's circuit isn't finished yet. Don't list it. */ + /* circuit_established is set in rend_service_intro_established(), and + * checked every second in rend_consider_services_intro_points(), so it's + * safe to use it here */ + if (!intro_svc->circuit_established) { continue; } @@ -1077,6 +1077,25 @@ rend_service_update_descriptor(rend_service_t *service) intro_svc->time_published = time(NULL); } } + + /* Check that we have the right number of intro points */ + unsigned int have_intro = (unsigned int)smartlist_len(d->intro_nodes); + if (have_intro != service->n_intro_points_wanted) { + int severity; + /* Getting less than we wanted or more than we're allowed is serious */ + if (have_intro < service->n_intro_points_wanted || + have_intro > NUM_INTRO_POINTS_MAX) { + severity = LOG_WARN; + } else { + /* Getting more than we wanted is weird, but less of a problem */ + severity = LOG_NOTICE; + } + log_fn(severity, LD_REND, "Hidden service %s wanted %d intro points, but " + "descriptor was updated with %d instead.", + service->service_id, + service->n_intro_points_wanted, have_intro); + rend_service_dump_stats(severity); + } } /* Allocate and return a string containing the path to file_name in @@ -4070,6 +4089,10 @@ rend_consider_services_intro_points(void) smartlist_clear(exclude_nodes); smartlist_clear(retry_nodes); + /* Cleanup the invalid intro points and save the node objects, if any, + * in the exclude_nodes and retry_nodes lists. */ + remove_invalid_intro_points(service, exclude_nodes, retry_nodes, now); + /* This retry period is important here so we don't stress circuit * creation. */ if (now > service->intro_period_started + INTRO_CIRC_RETRY_PERIOD) { @@ -4080,14 +4103,10 @@ rend_consider_services_intro_points(void) rend_max_intro_circs_per_period( service->n_intro_points_wanted)) { /* We have failed too many times in this period; wait for the next - * one before we try again. */ + * one before we try to initiate any more connections. */ continue; } - /* Cleanup the invalid intro points and save the node objects, if apply, - * in the exclude_nodes and retry_nodes list. */ - remove_invalid_intro_points(service, exclude_nodes, retry_nodes, now); - /* Let's try to rebuild circuit on the nodes we want to retry on. */ SMARTLIST_FOREACH_BEGIN(retry_nodes, rend_intro_point_t *, intro) { r = rend_service_launch_establish_intro(service, intro); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 98167d44f8..0336c035b4 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -4894,6 +4894,8 @@ tor_version_parse(const char *s, tor_version_t *out) #define NUMBER(m) \ do { \ + if (!cp || *cp < '0' || *cp > '9') \ + return -1; \ out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \ if (!ok) \ return -1; \ diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 922c68b42f..19c5a881a4 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -71,14 +71,14 @@ void mock_dump_saved_logs(void); \ assert_log_predicate(mock_saved_log_has_message_containing(str) && \ mock_saved_log_n_entries() == 1, \ - "expected log to contain exactly 1 message: " # str); \ + "expected log to contain exactly 1 message " # str); \ } while (0); #define expect_single_log_msg_containing(str) \ do { \ assert_log_predicate(mock_saved_log_has_message_containing(str)&& \ mock_saved_log_n_entries() == 1 , \ - "expected log to contain 1 message, containing" # str); \ + "expected log to contain 1 message, containing " # str); \ } while (0); #define expect_no_log_msg(str) \ diff --git a/src/test/test-network.sh b/src/test/test-network.sh index 10bd370ff3..6e0f286573 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -1,119 +1,45 @@ #!/bin/sh -# use bash if it is available, as this script doesn't work well in non-bash sh -# this will be fixed in #19699 -# there is no simple, portable way of checking the name of the shell, so we -# exec bash even when sh is bash -if [ -x /bin/bash -a "$USING_BASH" != true ]; then - # only do this once - export USING_BASH=true - exec /bin/bash "$0" "$@" +# This script calls the equivalent script in chutney/tools + +# If we already know CHUTNEY_PATH, don't bother with argument parsing +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + # we can't produce any output, because we might be --quiet + # this preserves arguments with spaces correctly + exec "$TEST_NETWORK" "$@" fi -# Please do not modify this script, it has been moved to chutney/tools - -export ECHO="${ECHO:-echo}" -export ECHO_N="${ECHO_N:-/bin/echo -n}" +# We need to go looking for CHUTNEY_PATH +# Do we output anything at all? +ECHO="${ECHO:-echo}" # Output is prefixed with the name of the script myname=$(basename $0) -# We need to find CHUTNEY_PATH, so that we can call the version of this script -# in chutney/tools. And we want to pass any arguments to that script as well. -# So we source this script, which processes its arguments to find CHUTNEY_PATH. - -# Avoid recursively sourcing this script, and don't call the chutney version -# while recursing, either -if [ "$TEST_NETWORK_RECURSING" != true ]; then - # Process the arguments into environmental variables with this script - # to make sure $CHUTNEY_PATH is set - # When we switch to using test-network.sh in chutney/tools, --dry-run - # can be removed, because this script will find chutney, then pass all - # arguments to chutney's test-network.sh - export TEST_NETWORK_RECURSING=true - # passing arguments to a sourced script only works in bash - # this will be fixed in #19699 - . "$0" --dry-run "$@" - - # Call the chutney version of this script, if it exists, and we can find it - if [ -d "$CHUTNEY_PATH" -a -x "$CHUTNEY_PATH/tools/test-network.sh" ]; then - unset NETWORK_DRY_RUN - $ECHO "$myname: Calling newer chutney script \ -$CHUTNEY_PATH/tools/test-network.sh" - "$CHUTNEY_PATH/tools/test-network.sh" "$@" - exit $? - else - $ECHO "$myname: This script has moved to chutney/tools." - $ECHO "$myname: Please update your chutney using 'git pull'." - # When we switch to using test-network.sh in chutney/tools, we should - # exit with a very loud failure here - $ECHO "$myname: Falling back to the old tor version of the script." - fi -fi +# Save the arguments before we destroy them +# This might not preserve arguments with spaces in them +ORIGINAL_ARGS="$@" +# We need to find CHUTNEY_PATH, so that we can call the version of this script +# in chutney/tools with the same arguments. We also need to respect --quiet. until [ -z "$1" ] do case "$1" in --chutney-path) - export CHUTNEY_PATH="$2" + CHUTNEY_PATH="$2" shift ;; --tor-path) - export TOR_DIR="$2" - shift - ;; - # When we switch to using test-network.sh in chutney/tools, only the - # --chutney-path and --tor-path arguments need to be processed by this - # script, everything else can be handled by chutney's test-network.sh - --flavor|--flavour|--network-flavor|--network-flavour) - export NETWORK_FLAVOUR="$2" + TOR_DIR="$2" shift ;; - --delay|--sleep|--bootstrap-time|--time) - export BOOTSTRAP_TIME="$2" - shift - ;; - # Environmental variables used by chutney verify performance tests - # Send this many bytes per client connection (10 KBytes) - --data|--data-bytes|--data-byte|--bytes|--byte) - export CHUTNEY_DATA_BYTES="$2" - shift - ;; - # Make this many connections per client (1) - # Note: If you create 7 or more connections to a hidden service from - # a single Tor 0.2.7 client, you'll likely get a verification failure due - # to #15937. This is fixed in 0.2.8. - --connections|--connection|--connection-count|--count) - export CHUTNEY_CONNECTIONS="$2" - shift - ;; - # Make each client connect to each HS (0) - # 0 means a single client connects to each HS - # 1 means every client connects to every HS - --hs-multi-client|--hs-multi-clients|--hs-client|--hs-clients) - export CHUTNEY_HS_MULTI_CLIENT="$2" - shift - ;; - --coverage) - export USE_COVERAGE_BINARY=true - ;; - --dry-run) - # process arguments, but don't call any other scripts - export NETWORK_DRY_RUN=true - ;; --quiet) - export ECHO=true - export ECHO_N=true - ;; + ECHO=true + ;; *) - $ECHO "$myname: Sorry, I don't know what to do with '$1'." - $ECHO "$myname: Maybe chutney's test-network.sh understands '$1'." - $ECHO "$myname: Please update your chutney using 'git pull', and set \ -\$CHUTNEY_PATH" - # continue processing arguments during a dry run - if [ "$NETWORK_DRY_RUN" != true ]; then - exit 2 - fi + # maybe chutney's test-network.sh can handle it ;; esac shift @@ -122,7 +48,7 @@ done # optional: $TOR_DIR is the tor build directory # it's used to find the location of tor binaries # if it's not set: -# - set it ro $BUILDDIR, or +# - set it to $BUILDDIR, or # - if $PWD looks like a tor build directory, set it to $PWD, or # - unset $TOR_DIR, and let chutney fall back to finding tor binaries in $PATH if [ ! -d "$TOR_DIR" ]; then @@ -130,12 +56,12 @@ if [ ! -d "$TOR_DIR" ]; then # Choose the build directory # But only if it looks like one $ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR" - export TOR_DIR="$BUILDDIR" + TOR_DIR="$BUILDDIR" elif [ -d "$PWD/src/or" -a -d "$PWD/src/tools" ]; then # Guess the tor directory is the current directory # But only if it looks like one $ECHO "$myname: \$TOR_DIR not set, trying \$PWD" - export TOR_DIR="$PWD" + TOR_DIR="$PWD" else $ECHO "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" unset TOR_DIR @@ -150,14 +76,12 @@ fi if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then if [ -x "$PWD/chutney" ]; then $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" - export CHUTNEY_PATH="$PWD" + CHUTNEY_PATH="$PWD" elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \ -x "$TOR_DIR/../chutney/chutney" ]; then $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" - export CHUTNEY_PATH="$TOR_DIR/../chutney" + CHUTNEY_PATH="$TOR_DIR/../chutney" else - # TODO: work out how to package and install chutney, - # so users can find it in $PATH $ECHO "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" $ECHO "$myname: Get chutney: git clone https://git.torproject.org/\ chutney.git" @@ -168,46 +92,17 @@ CHUTNEY_PATH=\`pwd\`/chutney" fi fi -# When we switch to using test-network.sh in chutney/tools, this comment and -# everything below it can be removed - -# For picking up the right tor binaries. -# If these varibles aren't set, chutney looks for tor binaries in $PATH -if [ -d "$TOR_DIR" ]; then - tor_name=tor - tor_gencert_name=tor-gencert - if [ "$USE_COVERAGE_BINARY" = true ]; then - tor_name=tor-cov - fi - export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}" - export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}" -fi - -# Set the variables for the chutney network flavour -export NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"} -export CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR - -# And finish up if we're doing a dry run -if [ "$NETWORK_DRY_RUN" = true ]; then - # we can't exit here, it breaks argument processing - # this only works in bash: return semantics are shell-specific - # this will be fixed in #19699 - return 2>/dev/null || exit +TEST_NETWORK="$CHUTNEY_PATH/tools/test-network.sh" +# Call the chutney version of this script, if it exists, and we can find it +if [ -d "$CHUTNEY_PATH" -a -x "$TEST_NETWORK" ]; then + $ECHO "$myname: Calling newer chutney script $TEST_NETWORK" + # this may fail if some arguments have spaces in them + # if so, set CHUTNEY_PATH before calling test-network.sh, and spaces + # will be handled correctly + exec "$TEST_NETWORK" $ORIGINAL_ARGS +else + $ECHO "$myname: Could not find tools/test-network.sh in CHUTNEY_PATH." + $ECHO "$myname: Please update your chutney using 'git pull'." + # We have failed to do what the user asked + exit 1 fi - -cd "$CHUTNEY_PATH" -./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 3 - -# Sleep some, waiting for the network to bootstrap. -# TODO: Add chutney command 'bootstrap-status' and use that instead. -BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-35} -$ECHO_N "$myname: sleeping for $BOOTSTRAP_TIME seconds" -n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do - sleep 1; n=$(expr $n - 1); $ECHO_N . -done; $ECHO "" -./chutney verify $CHUTNEY_NETWORK -VERIFY_EXIT_STATUS=$? -# work around a bug/feature in make -j2 (or more) -# where make hangs if any child processes are still alive -./chutney stop $CHUTNEY_NETWORK -exit $VERIFY_EXIT_STATUS diff --git a/src/test/test_controller.c b/src/test/test_controller.c index d9c0a1eaac..206315e289 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -109,6 +109,45 @@ test_add_onion_helper_keyarg(void *arg) } static void +test_getinfo_helper_onion(void *arg) +{ + (void)arg; + control_connection_t dummy; + /* Get results out */ + char *answer = NULL; + const char *errmsg = NULL; + char *service_id = NULL; + int rt = 0; + + dummy.ephemeral_onion_services = NULL; + + /* successfully get an empty answer */ + rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, ""); + tor_free(answer); + + /* successfully get an empty answer */ + rt = getinfo_helper_onions(&dummy, "onions/detached", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, ""); + tor_free(answer); + + /* get an answer for one onion service */ + service_id = tor_strdup("dummy_onion_id"); + dummy.ephemeral_onion_services = smartlist_new(); + smartlist_add(dummy.ephemeral_onion_services, service_id); + rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg); + tt_assert(rt == 0); + tt_str_op(answer, OP_EQ, "dummy_onion_id"); + + done: + tor_free(answer); + tor_free(service_id); + smartlist_free(dummy.ephemeral_onion_services); +} + +static void test_rend_service_parse_port_config(void *arg) { const char *sep = ","; @@ -1332,6 +1371,7 @@ test_download_status_bridge(void *arg) struct testcase_t controller_tests[] = { { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL }, { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, NULL, NULL }, { "add_onion_helper_clientauth", test_add_onion_helper_clientauth, 0, NULL, diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 4e5876fa3c..3906206696 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -1065,6 +1065,7 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha", OP_EQ, ver1.status_tag); + /* Go through the full set of status tags */ tt_int_op(0, OP_EQ, tor_version_parse("2.1.700-alpha", &ver1)); tt_int_op(2, OP_EQ, ver1.major); tt_int_op(1, OP_EQ, ver1.minor); @@ -1079,6 +1080,60 @@ test_dir_versions(void *arg) tt_int_op(0, OP_EQ, ver1.patchlevel); tt_int_op(VER_RELEASE, OP_EQ, ver1.status); tt_str_op("alpha-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.5-rc", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(5, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.6-rc-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(6, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("rc-dev", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.8", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(8, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(0, OP_EQ, tor_version_parse("0.2.9.9-dev", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2, OP_EQ, ver1.minor); + tt_int_op(9, OP_EQ, ver1.micro); + tt_int_op(9, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("dev", OP_EQ, ver1.status_tag); + /* In #21450, we fixed an inconsistency in parsing versions > INT32_MAX + * between i386 and x86_64, as we used tor_parse_long, and then cast to int + */ + tt_int_op(0, OP_EQ, tor_version_parse("0.2147483647.0", &ver1)); + tt_int_op(0, OP_EQ, ver1.major); + tt_int_op(2147483647, OP_EQ, ver1.minor); + tt_int_op(0, OP_EQ, ver1.micro); + tt_int_op(0, OP_EQ, ver1.patchlevel); + tt_int_op(VER_RELEASE, OP_EQ, ver1.status); + tt_str_op("", OP_EQ, ver1.status_tag); + tt_int_op(-1, OP_EQ, tor_version_parse("0.2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.4294967295.0", &ver1)); + /* In #21278, we reject negative version components */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-1.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-2147483648.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.-4294967295.0", &ver1)); + /* In #21507, we reject version components with non-numeric prefixes */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.-0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("+1.0.0", &ver1)); + /* use the list in isspace() */ + tt_int_op(-1, OP_EQ, tor_version_parse("0.\t0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\n0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\v0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\f0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0.\r0.0", &ver1)); + tt_int_op(-1, OP_EQ, tor_version_parse("0. 0.0", &ver1)); #define tt_versionstatus_op(vs1, op, vs2) \ tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \ @@ -1098,6 +1153,7 @@ test_dir_versions(void *arg) test_v_i_o(VS_RECOMMENDED, "0.0.7rc2", "0.0.7,Tor 0.0.7rc2,Tor 0.0.8"); test_v_i_o(VS_OLD, "0.0.5.0", "0.0.5.1-cvs"); test_v_i_o(VS_NEW_IN_SERIES, "0.0.5.1-cvs", "0.0.5, 0.0.6"); + test_v_i_o(VS_NEW, "0.2.9.9-dev", "0.2.9.9"); /* Not on list, but newer than any in same series. */ test_v_i_o(VS_NEW_IN_SERIES, "0.1.0.3", "Tor 0.1.0.2,Tor 0.0.9.5,Tor 0.1.1.0"); @@ -1136,6 +1192,70 @@ test_dir_versions(void *arg) "Tor 0.2.1.0-dev (r99)")); tt_int_op(1,OP_EQ, tor_version_as_new_as("Tor 0.2.1.1", "Tor 0.2.1.0-dev (r99)")); + /* And git revisions */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + /* a git revision is newer than no git revision */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072)", + "Tor 0.2.9.9")); + /* a longer git revision is newer than a shorter git revision + * this should be true if they prefix-match, but if they don't, they are + * incomparable, because hashes aren't ordered (but we compare their bytes + * anyway) */ + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-56788a2489127072d513cf4baf35a8ff475f3c7b)", + "Tor 0.2.9.9 (git-56788a2489127072)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-03)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-0102)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-01)", + "Tor 0.2.9.9 (git-00)")); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2.9.9 (git-00)", + "Tor 0.2.9.9 (git-01)")); + /* In #21278, we comapre without integer overflows. + * But since #21450 limits version components to [0, INT32_MAX], it is no + * longer possible to cause an integer overflow in tor_version_compare() */ + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 2147483647.0.0.0")); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 2147483647.0.0.0", + "Tor 0.0.0.0")); + /* These versions used to cause an overflow, now they don't parse + * (and authorities reject their descriptors), and log a BUG message */ + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.0.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-1.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.2147483647.0.0", + "Tor 0.-2147483648.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + tt_int_op(1,OP_EQ, tor_version_as_new_as( + "Tor 4294967295.0.0.0", + "Tor 0.0.0.0")); + expect_no_log_entry(); + tt_int_op(0,OP_EQ, tor_version_as_new_as( + "Tor 0.4294967295.0.0", + "Tor 0.-4294967295.0.0")); + expect_single_log_msg_containing("unparseable"); + mock_clean_saved_logs(); + teardown_capture_of_logs(); /* Now try git revisions */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00ff)", &ver1)); @@ -1145,11 +1265,24 @@ test_dir_versions(void *arg) tt_int_op(7,OP_EQ, ver1.patchlevel); tt_int_op(3,OP_EQ, ver1.git_tag_len); tt_mem_op(ver1.git_tag,OP_EQ, "\xff\x00\xff", 3); + /* reject bad hex digits */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00xx)", &ver1)); + /* reject odd hex digit count */ tt_int_op(-1,OP_EQ, tor_version_parse("0.5.6.7 (git-ff00fff)", &ver1)); + /* ignore "git " */ tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git ff00fff)", &ver1)); + /* standard length is 16 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse("0.5.6.7 (git-0010203040506070)", + &ver1)); + /* length limit is 40 hex digits */ + tt_int_op(0,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f10111213)", + &ver1)); + tt_int_op(-1,OP_EQ, tor_version_parse( + "0.5.6.7 (git-000102030405060708090a0b0c0d0e0f1011121314)", + &ver1)); done: - ; + teardown_capture_of_logs(); } /** Run unit tests for directory fp_pair functions. */ diff --git a/src/test/test_util.c b/src/test/test_util.c index e80201737a..3e4d45d35b 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -3340,6 +3340,13 @@ test_util_memarea(void *arg) void *malloced_ptr = NULL; int i; +#ifdef DISABLE_MEMORY_SENTINELS + /* If memory sentinels are disabled, this whole module is just an alias for + malloc(), which is free to lay out memory most any way it wants. */ + if (1) + tt_skip(); +#endif + (void)arg; tt_assert(area); @@ -3965,47 +3972,50 @@ test_util_fgets_eagain(void *ptr) /* Send in a partial line */ retlen = write(test_pipe[1], "A", 1); tt_int_op(retlen, OP_EQ, 1); - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); + tt_ptr_op(retptr, OP_EQ, NULL); tt_str_op(buf, OP_EQ, "A"); errno = 0; /* Send in the rest */ retlen = write(test_pipe[1], "B\n", 2); tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, 0); tt_ptr_op(retptr, OP_EQ, buf); tt_str_op(buf, OP_EQ, "B\n"); errno = 0; + memset(buf, '\0', sizeof(buf)); /* Send in a full line */ retlen = write(test_pipe[1], "CD\n", 3); tt_int_op(retlen, OP_EQ, 3); - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, 0); tt_ptr_op(retptr, OP_EQ, buf); tt_str_op(buf, OP_EQ, "CD\n"); errno = 0; + memset(buf, '\0', sizeof(buf)); /* Send in a partial line */ retlen = write(test_pipe[1], "E", 1); tt_int_op(retlen, OP_EQ, 1); - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, EAGAIN); - tt_ptr_op(retptr, OP_EQ, buf); + tt_ptr_op(retptr, OP_EQ, NULL); tt_str_op(buf, OP_EQ, "E"); errno = 0; /* Send in the rest */ retlen = write(test_pipe[1], "F\n", 2); tt_int_op(retlen, OP_EQ, 2); - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, 0); tt_ptr_op(retptr, OP_EQ, buf); tt_str_op(buf, OP_EQ, "F\n"); errno = 0; + memset(buf, '\0', sizeof(buf)); /* Send in a full line and close */ retlen = write(test_pipe[1], "GH", 2); @@ -4013,14 +4023,14 @@ test_util_fgets_eagain(void *ptr) retval = close(test_pipe[1]); tt_int_op(retval, OP_EQ, 0); test_pipe[1] = -1; - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, 0); tt_ptr_op(retptr, OP_EQ, buf); tt_str_op(buf, OP_EQ, "GH"); errno = 0; /* Check for EOF */ - retptr = fgets(buf, sizeof(buf), test_stream); + retptr = tor_fgets(buf, sizeof(buf), test_stream); tt_int_op(errno, OP_EQ, 0); tt_ptr_op(retptr, OP_EQ, NULL); retval = feof(test_stream); @@ -4029,6 +4039,7 @@ test_util_fgets_eagain(void *ptr) /* Check that buf is unchanged according to C99 and C11 */ tt_str_op(buf, OP_EQ, "GH"); + memset(buf, '\0', sizeof(buf)); done: if (test_stream != NULL) diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 5272cd6c6c..f7f4f19505 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -218,7 +218,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.3.0.4-rc-dev" +#define VERSION "0.3.1.0-alpha-dev" |