diff --git a/src/common/tortls.c b/src/common/tortls.c index 94ca81ba49..8db47a6f21 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -222,6 +222,37 @@ ssl_state_to_string(int ssl_state) return buf; } +/** DOCDOC 3116 */ +void +tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz) +{ + const char *ssl_state; + const char *tortls_state; + + if (PREDICT_UNLIKELY(!tls || !tls->ssl)) { + strlcpy(buf, "(No SSL object)", sz); + return; + } + + ssl_state = ssl_state_to_string(tls->ssl->state); + switch (tls->state) { +#define CASE(st) case TOR_TLS_ST_##st: tortls_state = #st ; break + CASE(HANDSHAKE); + CASE(OPEN); + CASE(GOTCLOSE); + CASE(SENTCLOSE); + CASE(CLOSED); + CASE(RENEGOTIATE); + CASE(BUFFEREVENT); +#undef CASE + default: + tortls_state = "unknown"; + break; + } + + tor_snprintf(buf, sz, "%s in %s", ssl_state, tortls_state); +} + void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err, int severity, int domain, const char *doing) diff --git a/src/common/tortls.h b/src/common/tortls.h index ecb5bd2fbe..9b8108b42b 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -48,6 +48,7 @@ typedef struct tor_tls_t tor_tls_t; #define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE) const char *tor_tls_err_to_string(int err); +void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz); void tor_tls_free_all(void); int tor_tls_context_init(int is_public_server, diff --git a/src/or/connection.c b/src/or/connection.c index e8a17e7641..b0e0f84371 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -554,6 +554,9 @@ connection_free_all(void) /* Unlink everything from the identity map. */ connection_or_clear_identity_map(); + /* Clear out our list of broken connections */ + clear_broken_connection_map(); + SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn)); if (outgoing_addrs) { diff --git a/src/or/connection_or.c b/src/or/connection_or.c index fa8624135b..85df78bd6c 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -150,6 +150,111 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest) #endif } +/**************************************************************/ + +/** DOCDOC */ +static strmap_t *broken_connection_counts; + +/** DOCDOC */ +static void +note_broken_connection(const char *state) +{ + void *ptr; + intptr_t val; + if (!broken_connection_counts) + broken_connection_counts = strmap_new(); + + ptr = strmap_get(broken_connection_counts, state); + val = (intptr_t)ptr; + val++; + ptr = (void*)val; + strmap_set(broken_connection_counts, state, ptr); +} + +/** DOCDOC */ +void +clear_broken_connection_map(void) +{ + if (broken_connection_counts) + strmap_free(broken_connection_counts, NULL); + broken_connection_counts = NULL; +} + +/** DOCDOC */ +static void +connection_or_get_state_description(or_connection_t *orconn, + char *buf, size_t buflen) +{ + connection_t *conn = TO_CONN(orconn); + const char *conn_state; + char tls_state[256]; + + tor_assert(conn->type == CONN_TYPE_OR); + + conn_state = conn_state_to_string(conn->type, conn->state); + tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state)); + + tor_snprintf(buf, buflen, "%s with SSL state %s", conn_state, tls_state); +} + +/** DOCDOC */ +static void +connection_or_note_state_when_broken(or_connection_t *orconn) +{ + char buf[256]; + connection_or_get_state_description(orconn, buf, sizeof(buf)); + log_info(LD_HANDSHAKE,"Connection died in state '%s'", buf); + note_broken_connection(buf); +} + +/** DOCDOC */ +typedef struct broken_state_count_t { + intptr_t count; + const char *state; +} broken_state_count_t; + +/** DOCDOC */ +static int +broken_state_count_compare(const void **a_ptr, const void **b_ptr) +{ + const broken_state_count_t *a = *a_ptr, *b = *b_ptr; + return a->count - b->count; +} + +/** DOCDOC */ +void +connection_or_report_broken_states(int severity, int domain) +{ + int total = 0; + smartlist_t *items; + + if (!broken_connection_counts) { + log(severity, domain, "No broken connections reported"); + return; + } + items = smartlist_create(); + STRMAP_FOREACH(broken_connection_counts, state, void *, countptr) { + broken_state_count_t *c = tor_malloc(sizeof(broken_state_count_t)); + total += c->count = (intptr_t)countptr; + c->state = state; + smartlist_add(items, c); + } STRMAP_FOREACH_END; + + smartlist_sort(items, broken_state_count_compare); + + log(severity, domain, "%d connections have failed:", total); + + SMARTLIST_FOREACH_BEGIN(items, const broken_state_count_t *, c) { + log(severity, domain, + " %d connections died in state %s", (int)c->count, c->state); + } SMARTLIST_FOREACH_END(c); + + SMARTLIST_FOREACH(items, broken_state_count_t *, c, tor_free(c)); + smartlist_free(items); +} + +/**************************************************************/ + /** Pack the cell_t host-order structure src into network-order * in the buffer dest. See tor-spec.txt for details about the * wire format. @@ -364,6 +469,7 @@ connection_or_about_to_close(or_connection_t *or_conn) /* now mark things down as needed */ if (connection_or_nonopen_was_started_here(or_conn)) { const or_options_t *options = get_options(); + connection_or_note_state_when_broken(or_conn); rep_hist_note_connect_failed(or_conn->identity_digest, now); entry_guard_register_connect_status(or_conn->identity_digest,0, !options->HTTPSProxy, now); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index f1acfd187b..abcfe83b99 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -14,6 +14,7 @@ void connection_or_remove_from_identity_map(or_connection_t *conn); void connection_or_clear_identity_map(void); +void clear_broken_connection_map(void); or_connection_t *connection_or_get_for_extend(const char *digest, const tor_addr_t *target_addr, const char **msg_out, @@ -35,6 +36,8 @@ void connection_or_connect_failed(or_connection_t *conn, or_connection_t *connection_or_connect(const tor_addr_t *addr, uint16_t port, const char *id_digest); +void connection_or_report_broken_states(int severity, int domain); + int connection_tls_start_handshake(or_connection_t *conn, int receiving); int connection_tls_continue_handshake(or_connection_t *conn);