diff options
Diffstat (limited to 'src')
30 files changed, 377 insertions, 133 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index 32b487dd24..22070c346b 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -132,6 +132,9 @@ #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index a2e1080720..ca0fefca71 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -2166,11 +2166,9 @@ check_expired_networkstatus_callback(time_t now, const or_options_t *options) (void)options; /* Check whether our networkstatus has expired. */ networkstatus_t *ns = networkstatus_get_latest_consensus(); - /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in - * networkstatus_get_reasonably_live_consensus(), but that value is way - * way too high. Arma: is the bridge issue there resolved yet? -NM */ -#define NS_EXPIRY_SLOP (24*60*60) - if (ns && ns->valid_until < (now - NS_EXPIRY_SLOP) && + /* Use reasonably live consensuses until they are no longer reasonably live. + */ + if (ns && !networkstatus_consensus_reasonably_live(ns, now) && router_have_minimum_dir_info()) { router_dir_info_changed(); } diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index e230ad1005..b7a4ab1b9e 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -2377,8 +2377,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } else { hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk); } - connection_ap_mark_as_non_pending_circuit(conn); - ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT; + connection_ap_mark_as_waiting_for_renddesc(conn); return 0; } log_info(LD_REND,"Chose %s as intro point for '%s'.", diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 9f76929e53..93383a4e01 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -140,6 +140,9 @@ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) #include <net/if.h> @@ -1365,6 +1368,21 @@ connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn) smartlist_remove(pending_entry_connections, entry_conn); } +/** Mark <b>entry_conn</b> as waiting for a rendezvous descriptor. This + * function will remove the entry connection from the waiting for a circuit + * list (pending_entry_connections). + * + * This pattern is used across the code base because a connection in state + * AP_CONN_STATE_RENDDESC_WAIT must not be in the pending list. */ +void +connection_ap_mark_as_waiting_for_renddesc(entry_connection_t *entry_conn) +{ + tor_assert(entry_conn); + + connection_ap_mark_as_non_pending_circuit(entry_conn); + ENTRY_TO_CONN(entry_conn)->state = AP_CONN_STATE_RENDDESC_WAIT; +} + /* DOCDOC */ void connection_ap_warn_and_unmark_if_pending_circ(entry_connection_t *entry_conn, diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 55b90d3eae..b8a7365a05 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -126,6 +126,9 @@ void connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, #define connection_ap_mark_as_pending_circuit(c) \ connection_ap_mark_as_pending_circuit_((c), __FILE__, __LINE__) void connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn); +void connection_ap_mark_as_waiting_for_renddesc( + entry_connection_t *entry_conn); + #define CONNECTION_AP_EXPECT_NONPENDING(c) do { \ if (ENTRY_TO_CONN(c)->state == AP_CONN_STATE_CIRCUIT_WAIT) { \ log_warn(LD_BUG, "At %s:%d: %p was unexpectedly in circuit_wait.", \ diff --git a/src/ext/byteorder.h b/src/ext/byteorder.h index c8ba52184b..95e080b14d 100644 --- a/src/ext/byteorder.h +++ b/src/ext/byteorder.h @@ -29,6 +29,10 @@ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) */ +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + /* This code is extracted from csiphash.h */ #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ diff --git a/src/ext/ed25519/donna/ed25519-donna-portable-identify.h b/src/ext/ed25519/donna/ed25519-donna-portable-identify.h index 26a264cf9e..3e0f9cfc50 100644 --- a/src/ext/ed25519/donna/ed25519-donna-portable-identify.h +++ b/src/ext/ed25519/donna/ed25519-donna-portable-identify.h @@ -14,7 +14,7 @@ #define OS_OSX #elif defined(macintosh) || defined(Macintosh) #define OS_MAC - #elif defined(__OpenBSD__) + #elif defined(OpenBSD) #define OS_OPENBSD #endif #endif diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c index b749d8136f..2442bc3909 100644 --- a/src/ext/trunnel/trunnel.c +++ b/src/ext/trunnel/trunnel.c @@ -14,6 +14,10 @@ #include <stdlib.h> #include <string.h> +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define IS_LITTLE_ENDIAN 1 @@ -31,7 +35,7 @@ # define IS_LITTLE_ENDIAN # endif #else -# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(OpenBSD) # include <sys/endian.h> # else # include <endian.h> diff --git a/src/feature/client/circpathbias.c b/src/feature/client/circpathbias.c index 9f2ed9347c..eaeef5fe5d 100644 --- a/src/feature/client/circpathbias.c +++ b/src/feature/client/circpathbias.c @@ -1332,7 +1332,7 @@ pathbias_measure_use_rate(entry_guard_t *guard) if (pathbias_get_dropguards(options)) { if (!pb->path_bias_disabled) { log_warn(LD_CIRC, - "Your Guard %s is failing to carry an extremely large " + "Guard %s is failing to carry an extremely large " "amount of stream on its circuits. " "To avoid potential route manipulation attacks, Tor has " "disabled use of this guard. " @@ -1356,7 +1356,7 @@ pathbias_measure_use_rate(entry_guard_t *guard) } else if (!pb->path_bias_use_extreme) { pb->path_bias_use_extreme = 1; log_warn(LD_CIRC, - "Your Guard %s is failing to carry an extremely large " + "Guard %s is failing to carry an extremely large " "amount of streams on its circuits. " "This could indicate a route manipulation attack, network " "overload, bad local network connectivity, or a bug. " @@ -1380,7 +1380,7 @@ pathbias_measure_use_rate(entry_guard_t *guard) if (!pb->path_bias_use_noticed) { pb->path_bias_use_noticed = 1; log_notice(LD_CIRC, - "Your Guard %s is failing to carry more streams on its " + "Guard %s is failing to carry more streams on its " "circuits than usual. " "Most likely this means the Tor network is overloaded " "or your network connection is poor. " @@ -1437,7 +1437,7 @@ pathbias_measure_close_rate(entry_guard_t *guard) if (pathbias_get_dropguards(options)) { if (!pb->path_bias_disabled) { log_warn(LD_CIRC, - "Your Guard %s is failing an extremely large " + "Guard %s is failing an extremely large " "amount of circuits. " "To avoid potential route manipulation attacks, Tor has " "disabled use of this guard. " @@ -1461,7 +1461,7 @@ pathbias_measure_close_rate(entry_guard_t *guard) } else if (!pb->path_bias_extreme) { pb->path_bias_extreme = 1; log_warn(LD_CIRC, - "Your Guard %s is failing an extremely large " + "Guard %s is failing an extremely large " "amount of circuits. " "This could indicate a route manipulation attack, " "extreme network overload, or a bug. " @@ -1485,7 +1485,7 @@ pathbias_measure_close_rate(entry_guard_t *guard) if (!pb->path_bias_warned) { pb->path_bias_warned = 1; log_warn(LD_CIRC, - "Your Guard %s is failing a very large " + "Guard %s is failing a very large " "amount of circuits. " "Most likely this means the Tor network is " "overloaded, but it could also mean an attack against " @@ -1510,7 +1510,7 @@ pathbias_measure_close_rate(entry_guard_t *guard) if (!pb->path_bias_noticed) { pb->path_bias_noticed = 1; log_notice(LD_CIRC, - "Your Guard %s is failing more circuits than " + "Guard %s is failing more circuits than " "usual. " "Most likely this means the Tor network is overloaded. " "Success counts are %ld/%ld. Use counts are %ld/%ld. " diff --git a/src/feature/control/control.c b/src/feature/control/control.c index da62c94981..170037e060 100644 --- a/src/feature/control/control.c +++ b/src/feature/control/control.c @@ -1743,6 +1743,26 @@ handle_control_takeownership(control_connection_t *conn, uint32_t len, return 0; } +/** Called when we get a DROPOWNERSHIP command. Mark this connection + * as a non-owning connection, so that we will not exit if the connection + * closes. */ +static int +handle_control_dropownership(control_connection_t *conn, uint32_t len, + const char *body) +{ + (void)len; + (void)body; + + conn->is_owning_control_connection = 0; + + log_info(LD_CONTROL, "Control connection %d has dropped ownership of this " + "Tor instance.", + (int)(conn->base_.s)); + + send_control_done(conn); + return 0; +} + /** Return true iff <b>addr</b> is unusable as a mapaddress target because of * containing funny characters. */ static int @@ -3069,11 +3089,6 @@ getinfo_helper_events(control_connection_t *control_conn, case VS_UNKNOWN: *answer = tor_strdup("unknown"); break; default: tor_fragile_assert(); } - } else if (!strcmp(question, "status/version/num-versioning") || - !strcmp(question, "status/version/num-concurring")) { - tor_asprintf(answer, "%d", get_n_authorities(V3_DIRINFO)); - log_warn(LD_GENERAL, "%s is deprecated; it no longer gives useful " - "information", question); } } else if (!strcmp(question, "status/clients-seen")) { char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL)); @@ -3366,10 +3381,6 @@ static const getinfo_item_t getinfo_items[] = { "A fresh relay/ei descriptor pair for Tor's current state. Not stored."), DOC("status/version/recommended", "List of currently recommended versions."), DOC("status/version/current", "Status of the current version."), - DOC("status/version/num-versioning", "Number of versioning authorities."), - DOC("status/version/num-concurring", - "Number of versioning authorities agreeing on the status of the " - "current version"), ITEM("address", misc, "IP address of this Tor host, if we can guess it."), ITEM("traffic/read", misc,"Bytes read since the process was started."), ITEM("traffic/written", misc, @@ -5550,6 +5561,9 @@ connection_control_process_inbuf(control_connection_t *conn) } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) { if (handle_control_takeownership(conn, cmd_data_len, args)) return -1; + } else if (!strcasecmp(conn->incoming_cmd, "DROPOWNERSHIP")) { + if (handle_control_dropownership(conn, cmd_data_len, args)) + return -1; } else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) { if (handle_control_mapaddress(conn, cmd_data_len, args)) return -1; diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index 4032223db4..8b89d05e98 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -489,28 +489,47 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args) } /** Warn that the cached consensus <b>consensus</b> of type - * <b>flavor</b> is too old and will not be served to clients. Rate-limit the - * warning to avoid logging an entry on every request. + * <b>flavor</b> too new or too old, based on <b>is_too_new</b>, + * and will not be served to clients. Rate-limit the warning to avoid logging + * an entry on every request. */ static void -warn_consensus_is_too_old(const struct consensus_cache_entry_t *consensus, - const char *flavor, time_t now) +warn_consensus_is_not_reasonably_live( + const struct consensus_cache_entry_t *consensus, + const char *flavor, time_t now, bool is_too_new) { -#define TOO_OLD_WARNING_INTERVAL (60*60) - static ratelim_t warned = RATELIM_INIT(TOO_OLD_WARNING_INTERVAL); +#define NOT_REASONABLY_LIVE_WARNING_INTERVAL (60*60) + static ratelim_t warned[2] = { RATELIM_INIT( + NOT_REASONABLY_LIVE_WARNING_INTERVAL), + RATELIM_INIT( + NOT_REASONABLY_LIVE_WARNING_INTERVAL) }; char timestamp[ISO_TIME_LEN+1]; - time_t valid_until; - char *dupes; + /* valid_after if is_too_new, valid_until if !is_too_new */ + time_t valid_time = 0; + char *dupes = NULL; - if (consensus_cache_entry_get_valid_until(consensus, &valid_until)) - return; - - if ((dupes = rate_limit_log(&warned, now))) { - format_local_iso_time(timestamp, valid_until); - log_warn(LD_DIRSERV, "Our %s%sconsensus is too old, so we will not " - "serve it to clients. It was valid until %s local time and we " - "continued to serve it for up to 24 hours after it expired.%s", - flavor ? flavor : "", flavor ? " " : "", timestamp, dupes); + if (is_too_new) { + if (consensus_cache_entry_get_valid_after(consensus, &valid_time)) + return; + dupes = rate_limit_log(&warned[1], now); + } else { + if (consensus_cache_entry_get_valid_until(consensus, &valid_time)) + return; + dupes = rate_limit_log(&warned[0], now); + } + + if (dupes) { + format_local_iso_time(timestamp, valid_time); + log_warn(LD_DIRSERV, "Our %s%sconsensus is too %s, so we will not " + "serve it to clients. It was valid %s %s local time and we " + "continued to serve it for up to 24 hours %s.%s", + flavor ? flavor : "", + flavor ? " " : "", + is_too_new ? "new" : "old", + is_too_new ? "after" : "until", + timestamp, + is_too_new ? "before it was valid" : "after it expired", + dupes); tor_free(dupes); } } @@ -853,7 +872,6 @@ handle_get_current_consensus(dir_connection_t *conn, if (req.diff_only && !cached_consensus) { write_short_http_response(conn, 404, "No such diff available"); - // XXXX warn_consensus_is_too_old(v, req.flavor, now); geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); goto done; } @@ -864,19 +882,30 @@ handle_get_current_consensus(dir_connection_t *conn, &compression_used); } - time_t fresh_until, valid_until; - int have_fresh_until = 0, have_valid_until = 0; + time_t valid_after, fresh_until, valid_until; + int have_valid_after = 0, have_fresh_until = 0, have_valid_until = 0; if (cached_consensus) { + have_valid_after = + !consensus_cache_entry_get_valid_after(cached_consensus, &valid_after); have_fresh_until = !consensus_cache_entry_get_fresh_until(cached_consensus, &fresh_until); have_valid_until = !consensus_cache_entry_get_valid_until(cached_consensus, &valid_until); } - if (cached_consensus && have_valid_until && + if (cached_consensus && have_valid_after && + !networkstatus_valid_after_is_reasonably_live(valid_after, now)) { + write_short_http_response(conn, 404, "Consensus is too new"); + warn_consensus_is_not_reasonably_live(cached_consensus, req.flavor, now, + 1); + geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); + goto done; + } else if ( + cached_consensus && have_valid_until && !networkstatus_valid_until_is_reasonably_live(valid_until, now)) { write_short_http_response(conn, 404, "Consensus is too old"); - warn_consensus_is_too_old(cached_consensus, req.flavor, now); + warn_consensus_is_not_reasonably_live(cached_consensus, req.flavor, now, + 0); geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); goto done; } diff --git a/src/feature/dirparse/parsecommon.c b/src/feature/dirparse/parsecommon.c index b53b0fe193..88df5eec6f 100644 --- a/src/feature/dirparse/parsecommon.c +++ b/src/feature/dirparse/parsecommon.c @@ -15,6 +15,7 @@ #include "lib/string/printf.h" #include "lib/memarea/memarea.h" #include "lib/crypt_ops/crypto_rsa.h" +#include "lib/ctime/di_ops.h" #include <string.h> @@ -250,6 +251,16 @@ token_check_object(memarea_t *area, const char *kwd, return tok; } +/** Return true iff the <b>memlen</b>-byte chunk of memory at + * <b>memlen</b> is the same length as <b>token</b>, and their + * contents are equal. */ +static bool +mem_eq_token(const void *mem, size_t memlen, const char *token) +{ + size_t len = strlen(token); + return memlen == len && fast_memeq(mem, token, len); +} + /** Helper function: read the next token from *s, advance *s to the end of the * token, and return the parsed token. Parse *<b>s</b> according to the list * of tokens in <b>table</b>. @@ -289,7 +300,7 @@ get_next_token(memarea_t *area, next = find_whitespace_eos(*s, eol); - if (!strcmp_len(*s, "opt", next-*s)) { + if (mem_eq_token(*s, next-*s, "opt")) { /* Skip past an "opt" at the start of the line. */ *s = eat_whitespace_eos_no_nl(next, eol); next = find_whitespace_eos(*s, eol); @@ -300,7 +311,7 @@ get_next_token(memarea_t *area, /* Search the table for the appropriate entry. (I tried a binary search * instead, but it wasn't any faster.) */ for (i = 0; table[i].t ; ++i) { - if (!strcmp_len(*s, table[i].t, next-*s)) { + if (mem_eq_token(*s, next-*s, table[i].t)) { /* We've found the keyword. */ kwd = table[i].t; tok->tp = table[i].v; @@ -352,7 +363,7 @@ get_next_token(memarea_t *area, goto check_object; if (eol - *s <= 16 || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */ - strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */ + !mem_eq_token(eol-5, 5, "-----") || /* nuls or invalid endings */ (eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */ RET_ERR("Malformed object: bad begin line"); } @@ -371,8 +382,8 @@ get_next_token(memarea_t *area, eol = eos; /* Validate the ending tag, which should be 9 + NAME + 5 + eol */ if ((size_t)(eol-next) != 9+obname_len+5 || - strcmp_len(next+9, tok->object_type, obname_len) || - strcmp_len(eol-5, "-----", 5)) { + !mem_eq_token(next+9, obname_len, tok->object_type) || + !mem_eq_token(eol-5, 5, "-----")) { tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s", tok->object_type); ebuf[sizeof(ebuf)-1] = '\0'; diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index dfad216abb..5fded92fe3 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -142,8 +142,7 @@ flag_all_conn_wait_desc(const ed25519_public_key_t *service_identity_pk) if (edge_conn->hs_ident && ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk, service_identity_pk)) { - connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); - conn->state = AP_CONN_STATE_RENDDESC_WAIT; + connection_ap_mark_as_waiting_for_renddesc(TO_ENTRY_CONN(conn)); } } SMARTLIST_FOREACH_END(conn); @@ -201,6 +200,26 @@ directory_request_is_pending(const ed25519_public_key_t *identity_pk) return ret; } +/* Helper function that changes the state of an entry connection to waiting + * for a circuit. For this to work properly, the connection timestamps are set + * to now and the connection is then marked as pending for a circuit. */ +static void +mark_conn_as_waiting_for_circuit(connection_t *conn, time_t now) +{ + tor_assert(conn); + + /* Because the connection can now proceed to opening circuit and ultimately + * connect to the service, reset those timestamp so the connection is + * considered "fresh" and can continue without being closed too early. */ + conn->timestamp_created = now; + conn->timestamp_last_read_allowed = now; + conn->timestamp_last_write_allowed = now; + /* Change connection's state into waiting for a circuit. */ + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + + connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn)); +} + /* We failed to fetch a descriptor for the service with <b>identity_pk</b> * because of <b>status</b>. Find all pending SOCKS connections for this * service that are waiting on the descriptor and close them with @@ -277,12 +296,19 @@ retry_all_socks_conn_waiting_for_desc(void) /* Order a refetch in case it works this time. */ status = hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk); - if (BUG(status == HS_CLIENT_FETCH_HAVE_DESC)) { - /* This case is unique because it can NOT happen in theory. Once we get - * a new descriptor, the HS client subsystem is notified immediately and - * the connections waiting for it are handled which means the state will - * change from renddesc wait state. Log this and continue to next - * connection. */ + if (status == HS_CLIENT_FETCH_HAVE_DESC) { + /* This is a rare case where a SOCKS connection is in state waiting for + * a descriptor but we do have it in the cache. + * + * This can happen is tor comes back from suspend where it previously + * had the descriptor but the intro points were not usuable. Once it + * came back to life, the intro point failure cache was cleaned up and + * thus the descriptor became usable again leaving us in this code path. + * + * We'll mark the connection as waiting for a circuit so the descriptor + * can be retried. This is safe because a connection in state waiting + * for a descriptor can not be in the entry connection pending list. */ + mark_conn_as_waiting_for_circuit(base_conn, approx_time()); continue; } /* In the case of an error, either all SOCKS connections have been @@ -1701,17 +1727,9 @@ hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident) log_info(LD_REND, "Descriptor has arrived. Launching circuits."); - /* Because the connection can now proceed to opening circuit and - * ultimately connect to the service, reset those timestamp so the - * connection is considered "fresh" and can continue without being closed - * too early. */ - base_conn->timestamp_created = now; - base_conn->timestamp_last_read_allowed = now; - base_conn->timestamp_last_write_allowed = now; - /* Change connection's state into waiting for a circuit. */ - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - - connection_ap_mark_as_pending_circuit(entry_conn); + /* Mark connection as waiting for a circuit since we do have a usable + * descriptor now. */ + mark_conn_as_waiting_for_circuit(base_conn, now); } SMARTLIST_FOREACH_END(base_conn); end: diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c index eadca69548..82070afb98 100644 --- a/src/feature/nodelist/microdesc.c +++ b/src/feature/nodelist/microdesc.c @@ -111,8 +111,9 @@ microdesc_note_outdated_dirserver(const char *relay_digest) /* If we have a reasonably live consensus, then most of our dirservers should * still be caching all the microdescriptors in it. Reasonably live - * consensuses are up to a day old. But microdescriptors expire 7 days after - * the last consensus that referenced them. */ + * consensuses are up to a day old (or a day in the future). But + * microdescriptors expire 7 days after the last consensus that referenced + * them. */ if (!networkstatus_get_reasonably_live_consensus(approx_time(), FLAV_MICRODESC)) { return; @@ -545,8 +546,8 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force) size_t bytes_dropped = 0; time_t now = time(NULL); - /* If we don't know a live consensus, don't believe last_listed values: we - * might be starting up after being down for a while. */ + /* If we don't know a reasonably live consensus, don't believe last_listed + * values: we might be starting up after being down for a while. */ if (! force && ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC)) return; @@ -971,6 +972,7 @@ update_microdesc_downloads(time_t now) if (directory_too_idle_to_fetch_descriptors(options, now)) return; + /* Give up if we don't have a reasonably live consensus. */ consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC); if (!consensus) return; diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index 65ea3cc491..e1063a0eac 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -1388,7 +1388,7 @@ networkstatus_get_dl_status_by_flavor_running,(consensus_flavor_t flavor)) } /** Return the most recent consensus that we have downloaded, or NULL if we - * don't have one. */ + * don't have one. May return future or expired consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_latest_consensus,(void)) { @@ -1399,7 +1399,7 @@ networkstatus_get_latest_consensus,(void)) } /** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL - * if we don't have one. */ + * if we don't have one. May return future or expired consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f)) { @@ -1433,10 +1433,11 @@ networkstatus_is_live(const networkstatus_t *ns, time_t now) return (ns->valid_after <= now && now <= ns->valid_until); } -/** Determine if <b>consensus</b> is valid or expired recently enough that - * we can still use it. +/** Determine if <b>consensus</b> is valid, or expired recently enough, or not + * too far in the future, so that we can still use it. * - * Return 1 if the consensus is reasonably live, or 0 if it is too old. + * Return 1 if the consensus is reasonably live, or 0 if it is too old or + * too new. */ int networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, @@ -1445,29 +1446,42 @@ networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, if (BUG(!consensus)) return 0; - return networkstatus_valid_until_is_reasonably_live(consensus->valid_until, + return networkstatus_valid_after_is_reasonably_live(consensus->valid_after, + now) && + networkstatus_valid_until_is_reasonably_live(consensus->valid_until, now); } +#define REASONABLY_LIVE_TIME (24*60*60) + +/** As networkstatus_consensus_reasonably_live, but takes a valid_after + * time, and checks to see if it is in the past, or not too far in the future. + */ +int +networkstatus_valid_after_is_reasonably_live(time_t valid_after, + time_t now) +{ + return (now >= valid_after - REASONABLY_LIVE_TIME); +} + /** As networkstatus_consensus_reasonably_live, but takes a valid_until - * time rather than an entire consensus. */ + * time, and checks to see if it is in the future, or not too far in the past. + */ int networkstatus_valid_until_is_reasonably_live(time_t valid_until, time_t now) { -#define REASONABLY_LIVE_TIME (24*60*60) return (now <= valid_until + REASONABLY_LIVE_TIME); } /** As networkstatus_get_live_consensus(), but is way more tolerant of expired - * consensuses. */ + * and future consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_reasonably_live_consensus,(time_t now, int flavor)) { networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor(flavor); if (consensus && - consensus->valid_after <= now && networkstatus_consensus_reasonably_live(consensus, now)) return consensus; else @@ -2090,7 +2104,6 @@ networkstatus_set_current_consensus(const char *consensus, nodelist_set_consensus(c); - /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/ update_consensus_networkstatus_fetch_time(now); /* Change the cell EWMA settings */ diff --git a/src/feature/nodelist/networkstatus.h b/src/feature/nodelist/networkstatus.h index 3d212dd304..572b42cc5a 100644 --- a/src/feature/nodelist/networkstatus.h +++ b/src/feature/nodelist/networkstatus.h @@ -88,6 +88,8 @@ MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now)); int networkstatus_is_live(const networkstatus_t *ns, time_t now); int networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, time_t now); +int networkstatus_valid_after_is_reasonably_live(time_t valid_after, + time_t now); int networkstatus_valid_until_is_reasonably_live(time_t valid_until, time_t now); MOCK_DECL(networkstatus_t *,networkstatus_get_reasonably_live_consensus, diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c index 10b67ceda9..6ecb3eb3c6 100644 --- a/src/feature/rend/rendclient.c +++ b/src/feature/rend/rendclient.c @@ -150,8 +150,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT, onion_address))) { - connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); - conn->state = AP_CONN_STATE_RENDDESC_WAIT; + connection_ap_mark_as_waiting_for_renddesc(TO_ENTRY_CONN(conn)); } } @@ -864,8 +863,7 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT, onion_address))) { - connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); - conn->state = AP_CONN_STATE_RENDDESC_WAIT; + connection_ap_mark_as_waiting_for_renddesc(TO_ENTRY_CONN(conn)); } return 0; diff --git a/src/lib/err/backtrace.c b/src/lib/err/backtrace.c index d18a595c34..b568c888c5 100644 --- a/src/lib/err/backtrace.c +++ b/src/lib/err/backtrace.c @@ -32,6 +32,9 @@ #ifdef HAVE_SIGNAL_H #include <signal.h> #endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif #include <errno.h> #include <stdlib.h> #include <string.h> diff --git a/src/lib/net/inaddr_st.h b/src/lib/net/inaddr_st.h index dc4c6e3a00..a6b7796268 100644 --- a/src/lib/net/inaddr_st.h +++ b/src/lib/net/inaddr_st.h @@ -28,6 +28,9 @@ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif #ifdef _WIN32 #include <winsock2.h> @@ -60,7 +63,7 @@ struct in6_addr /** @{ */ /** Many BSD variants seem not to define these. */ #if defined(__APPLE__) || defined(__darwin__) || \ - defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + defined(__FreeBSD__) || defined(__NetBSD__) || defined(OpenBSD) #ifndef s6_addr16 #define s6_addr16 __u6_addr.__u6_addr16 #endif diff --git a/src/lib/net/socketpair.c b/src/lib/net/socketpair.c index b23f08cab2..b4aeb6eba7 100644 --- a/src/lib/net/socketpair.c +++ b/src/lib/net/socketpair.c @@ -62,7 +62,7 @@ get_local_listener(int family, int type) len = sizeof(sin); } else { sa = (struct sockaddr *) &sin6; - sin6.sin6_family = AF_INET; + sin6.sin6_family = AF_INET6; sin6.sin6_addr.s6_addr[15] = 1; len = sizeof(sin6); } diff --git a/src/lib/process/process_unix.c b/src/lib/process/process_unix.c index 57ca69a768..dd4ccbf603 100644 --- a/src/lib/process/process_unix.c +++ b/src/lib/process/process_unix.c @@ -183,8 +183,8 @@ process_unix_exec(process_t *process) close(stdin_pipe[1]); /** Cleanup standard out pipe. */ - close(stdin_pipe[0]); - close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); return PROCESS_STATUS_ERROR; } @@ -281,8 +281,8 @@ process_unix_exec(process_t *process) close(stdin_pipe[1]); /** Cleanup standard out pipe. */ - close(stdin_pipe[0]); - close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); /** Cleanup standard error pipe. */ close(stderr_pipe[0]); diff --git a/src/lib/string/util_string.c b/src/lib/string/util_string.c index e76e73046f..36e19d029c 100644 --- a/src/lib/string/util_string.c +++ b/src/lib/string/util_string.c @@ -212,21 +212,6 @@ strcmpstart(const char *s1, const char *s2) return strncmp(s1, s2, n); } -/** Compare the s1_len-byte string <b>s1</b> with <b>s2</b>, - * without depending on a terminating nul in s1. Sorting order is first by - * length, then lexically; return values are as for strcmp. - */ -int -strcmp_len(const char *s1, const char *s2, size_t s1_len) -{ - size_t s2_len = strlen(s2); - if (s1_len < s2_len) - return -1; - if (s1_len > s2_len) - return 1; - return fast_memcmp(s1, s2, s2_len); -} - /** Compares the first strlen(s2) characters of s1 with s2. Returns as for * strcasecmp. */ diff --git a/src/lib/string/util_string.h b/src/lib/string/util_string.h index 99467a27c3..6541afa4cb 100644 --- a/src/lib/string/util_string.h +++ b/src/lib/string/util_string.h @@ -33,7 +33,6 @@ int tor_strisnonupper(const char *s); int tor_strisspace(const char *s); int strcmp_opt(const char *s1, const char *s2); int strcmpstart(const char *s1, const char *s2); -int strcmp_len(const char *s1, const char *s2, size_t len); int strcasecmpstart(const char *s1, const char *s2); int strcmpend(const char *s1, const char *s2); int strcasecmpend(const char *s1, const char *s2); diff --git a/src/lib/time/compat_time.h b/src/lib/time/compat_time.h index 44fab62de5..c5337e9998 100644 --- a/src/lib/time/compat_time.h +++ b/src/lib/time/compat_time.h @@ -15,6 +15,102 @@ * of tens of milliseconds. */ +/* Q: Should you use monotime or monotime_coarse as your source? + * + * A: Generally, you get better precision with monotime, but better + * performance with monotime_coarse. + * + * Q: Should you use monotime_t or monotime_coarse_t directly? Should you use + * usec? msec? "stamp units?" + * + * A: Using monotime_t and monotime_coarse_t directly is most time-efficient, + * since no conversion needs to happen. But they can potentially use more + * memory than you would need for a usec/msec/"stamp unit" count. + * + * Converting to usec or msec on some platforms, and working with them in + * general, creates a risk of doing a 64-bit division. 64-bit division is + * expensive on 32-bit platforms, which still do exist. + * + * The "stamp unit" type is designed to give a type that is cheap to convert + * from monotime_coarse, has resolution of about 1-2ms, and fits nicely in a + * 32-bit integer. Its downside is that it does not correspond directly + * to a natural unit of time. + * + * There is not much point in using "coarse usec" or "coarse nsec", since the + * current coarse monotime implementations give you on the order of + * milliseconds of precision. + * + * Q: So, what backends is monotime_coarse using? + * + * A: Generally speaking, it uses "whatever monotonic-ish time implemenation + * does not require a context switch." The various implementations provide + * this by having a view of the current time in a read-only memory page that + * is updated with a frequency corresponding to the kernel's tick count. + * + * On Windows, monotime_coarse uses GetCount64() [or GetTickCount() on + * obsolete systems]. MSDN claims that the resolution is "typically in the + * range of 10-16 msec", but it has said that for years. Storing + * monotime_coarse_t uses 8 bytes. + * + * On OSX/iOS, monotime_coarse uses uses mach_approximate_time() where + * available, and falls back to regular monotime. The precision is not + * documented, but the implementation is open-source: it reads from a page + * that the kernel updates. Storing monotime_coarse_t uses 8 bytes. + * + * On unixy systems, monotime_coarse uses clock_gettime() with + * CLOCK_MONOTONIC_COARSE where available, and falls back to CLOCK_MONOTONIC. + * It typically uses vdso tricks to read from a page that the kernel updates. + * Its precision fixed, but you can get it with clock_getres(): on my Linux + * desktop, it claims to be 1 msec, but it will depend on the system HZ + * setting. Storing monotime_coarse_t uses 16 bytes. + * + * [TODO: Try CLOCK_MONOTONIC_FAST on foobsd.] + * + * Q: What backends is regular monotonic time using? + * + * A: In general, regular monotime uses something that requires a system call. + * On platforms where system calls are cheap, you win! Otherwise, you lose. + * + * On Windows, monotonic time uses QuereyPerformanceCounter. Storing + * monotime_t costs 8 bytes. + * + * On OSX/Apple, monotonic time uses mach_absolute_time. Storing + * monotime_t costs 8 bytes. + * + * On unixy systems, monotonic time uses CLOCK_MONOTONIC. Storing + * monotime_t costs 16 bytes. + * + * Q: Tell me about the costs of converting to a 64-bit nsec, usec, or msec + * count. + * + * A: Windows, coarse: Cheap, since it's all multiplication. + * + * Windows, precise: Expensive on 32-bit: it needs 64-bit division. + * + * Apple, all: Expensive on 32-bit: it needs 64-bit division. + * + * Unixy, all: Fairly cheap, since the only division required is dividing + * tv_nsec 1000, and nanoseconds-per-second fits in a 32-bit value. + * + * All, "timestamp units": Cheap everywhere: it never divides. + * + * Q: This is only somewhat related, but how much precision could I hope for + * from a libevent time.? + * + * A: Actually, it's _very_ related if you're timing in order to have a + * timeout happen. + * + * On Windows, it uses select: you could in theory have a microsecond + * resolution, but it usually isn't that accurate. + * + * On OSX, iOS, and BSD, you have kqueue: You could in theory have a nanosecond + * resolution, but it usually isn't that accurate. + * + * On Linux, you have epoll: It has a millisecond resolution. Some recent + * Libevents can also use timerfd for higher resolution if + * EVENT_BASE_FLAG_PRECISE_TIMER is set: Tor doesn't set that flag. + */ + #ifndef TOR_COMPAT_TIME_H #define TOR_COMPAT_TIME_H diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index c879013ed6..b00e854a1c 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -12,6 +12,10 @@ #include <sys/types.h> #include <stdlib.h> +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); static unsigned fill_a_buffer_nothing(void) __attribute__((noinline)); diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 16b14c0b73..069440a8ce 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -127,6 +127,9 @@ big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr) return 1; /* NOP */ } +#define REASONABLY_FUTURE " reasonably-future" +#define REASONABLY_PAST " reasonably-past" + /* Unittest setup function: Setup a fake network. */ static void * big_fake_network_setup(const struct testcase_t *testcase) @@ -138,9 +141,10 @@ big_fake_network_setup(const struct testcase_t *testcase) const int N_NODES = 271; const char *argument = testcase->setup_data; - int reasonably_live_consensus = 0; + int reasonably_future_consensus = 0, reasonably_past_consensus = 0; if (argument) { - reasonably_live_consensus = strstr(argument, "reasonably-live") != NULL; + reasonably_future_consensus = strstr(argument, REASONABLY_FUTURE) != NULL; + reasonably_past_consensus = strstr(argument, REASONABLY_PAST) != NULL; } big_fake_net_nodes = smartlist_new(); @@ -198,11 +202,15 @@ big_fake_network_setup(const struct testcase_t *testcase) dummy_state = tor_malloc_zero(sizeof(or_state_t)); dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t)); - if (reasonably_live_consensus) { - /* Make the dummy consensus valid from 4 hours ago, but expired an hour + if (reasonably_future_consensus) { + /* Make the dummy consensus valid in 6 hours, and expiring in 7 hours. */ + dummy_consensus->valid_after = approx_time() + 6*3600; + dummy_consensus->valid_until = approx_time() + 7*3600; + } else if (reasonably_past_consensus) { + /* Make the dummy consensus valid from 16 hours ago, but expired 12 hours * ago. */ - dummy_consensus->valid_after = approx_time() - 4*3600; - dummy_consensus->valid_until = approx_time() - 3600; + dummy_consensus->valid_after = approx_time() - 16*3600; + dummy_consensus->valid_until = approx_time() - 12*3600; } else { /* Make the dummy consensus valid for an hour either side of now. */ dummy_consensus->valid_after = approx_time() - 3600; @@ -3038,13 +3046,17 @@ static const struct testcase_setup_t upgrade_circuits = { #define BFN_TEST(name) \ EN_TEST_BASE(name, TT_FORK, &big_fake_network, NULL), \ - { #name "_reasonably_live", test_entry_guard_ ## name, TT_FORK, \ - &big_fake_network, (void*)("reasonably-live") } + { #name "_reasonably_future", test_entry_guard_ ## name, TT_FORK, \ + &big_fake_network, (void*)(REASONABLY_FUTURE) }, \ + { #name "_reasonably_past", test_entry_guard_ ## name, TT_FORK, \ + &big_fake_network, (void*)(REASONABLY_PAST) } #define UPGRADE_TEST(name, arg) \ EN_TEST_BASE(name, TT_FORK, &upgrade_circuits, arg), \ - { #name "_reasonably_live", test_entry_guard_ ## name, TT_FORK, \ - &upgrade_circuits, (void*)(arg " reasonably-live") } + { #name "_reasonably_future", test_entry_guard_ ## name, TT_FORK, \ + &upgrade_circuits, (void*)(arg REASONABLY_FUTURE) }, \ + { #name "_reasonably_past", test_entry_guard_ ## name, TT_FORK, \ + &upgrade_circuits, (void*)(arg REASONABLY_PAST) } struct testcase_t entrynodes_tests[] = { NO_PREFIX_TEST(node_preferred_orport), diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index b2aafc1cd6..ccb4d93feb 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -267,7 +267,7 @@ helper_clone_authorized_client(const hs_service_authorized_client_t *client) /* Helper: Return a newly allocated service object with the identity keypair * sets and the current descriptor. Then register it to the global map. - * Caller should us hs_free_all() to free this service or remove it from the + * Caller should use hs_free_all() to free this service or remove it from the * global map before freeing. */ static hs_service_t * helper_create_service(void) @@ -289,6 +289,20 @@ helper_create_service(void) return service; } +/* Helper: Deallocate a given service object, its child objects and + * remove it from onion service map. + * */ +static void +helper_destroy_service(hs_service_t *service) +{ + if (!service) + return; + + remove_service(get_hs_service_map(), service); + + hs_service_free(service); +} + /* Helper: Return a newly allocated service object with clients. */ static hs_service_t * helper_create_service_with_clients(int num_clients) @@ -1626,6 +1640,7 @@ test_build_descriptors(void *arg) { int ret; time_t now = time(NULL); + hs_service_t *last_service = NULL; (void) arg; @@ -1650,6 +1665,7 @@ test_build_descriptors(void *arg) * is disabled. */ { hs_service_t *service = helper_create_service(); + last_service = service; service_descriptor_free(service->desc_current); service->desc_current = NULL; @@ -1660,12 +1676,16 @@ test_build_descriptors(void *arg) hs_desc_superencrypted_data_t *superencrypted; superencrypted = &service->desc_current->desc->superencrypted_data; tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16); + + helper_destroy_service(service); + last_service = NULL; } /* Generate a valid number of fake auth clients when the number of * clients is zero. */ { hs_service_t *service = helper_create_service_with_clients(0); + last_service = service; service_descriptor_free(service->desc_current); service->desc_current = NULL; @@ -1673,12 +1693,16 @@ test_build_descriptors(void *arg) hs_desc_superencrypted_data_t *superencrypted; superencrypted = &service->desc_current->desc->superencrypted_data; tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16); + + helper_destroy_service(service); + last_service = NULL; } /* Generate a valid number of fake auth clients when the number of * clients is not a multiple of 16. */ { hs_service_t *service = helper_create_service_with_clients(20); + last_service = service; service_descriptor_free(service->desc_current); service->desc_current = NULL; @@ -1686,12 +1710,16 @@ test_build_descriptors(void *arg) hs_desc_superencrypted_data_t *superencrypted; superencrypted = &service->desc_current->desc->superencrypted_data; tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32); + + helper_destroy_service(service); + last_service = NULL; } /* Do not generate any fake desc client when the number of clients is * a multiple of 16 but not zero. */ { hs_service_t *service = helper_create_service_with_clients(32); + last_service = service; service_descriptor_free(service->desc_current); service->desc_current = NULL; @@ -1699,9 +1727,13 @@ test_build_descriptors(void *arg) hs_desc_superencrypted_data_t *superencrypted; superencrypted = &service->desc_current->desc->superencrypted_data; tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32); + + helper_destroy_service(service); + last_service = NULL; } done: + helper_destroy_service(last_service); hs_free_all(); } diff --git a/src/test/test_options.c b/src/test/test_options.c index 376d77626f..6506731823 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -23,6 +23,10 @@ #include "test/test_helpers.h" #include "lib/net/resolve.h" +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + #define NS_MODULE test_options typedef struct { diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 167b7a35ce..3e1e727429 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -305,7 +305,6 @@ test_router_pick_directory_server_impl(void *arg) tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until + 24*60*60)); /* These times are outside the test validity period */ - tt_assert(networkstatus_consensus_is_bootstrapping(now)); tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60)); tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60)); diff --git a/src/test/test_util.c b/src/test/test_util.c index b983cbb0bf..5ffe2d6b1a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -2153,15 +2153,6 @@ test_util_strmisc(void *arg) tt_int_op(strcmp_opt(NULL, "foo"), OP_LT, 0); tt_int_op(strcmp_opt("foo", NULL), OP_GT, 0); - /* Test strcmp_len */ - tt_int_op(strcmp_len("foo", "bar", 3), OP_GT, 0); - tt_int_op(strcmp_len("foo", "bar", 2), OP_LT, 0); - tt_int_op(strcmp_len("foo2", "foo1", 4), OP_GT, 0); - tt_int_op(strcmp_len("foo2", "foo1", 3), OP_LT, 0); /* Really stop at len */ - tt_int_op(strcmp_len("foo2", "foo", 3), OP_EQ, 0); /* Really stop at len */ - tt_int_op(strcmp_len("blah", "", 4), OP_GT, 0); - tt_int_op(strcmp_len("blah", "", 0), OP_EQ, 0); - done: tor_free(cp_tmp); } |