From 8c85eef9b065573da2be12c631e906dc149b1a4f Mon Sep 17 00:00:00 2001 From: Roger Dingledine Date: Wed, 11 Jun 2008 01:14:23 +0000 Subject: [PATCH] start sending "bootstrap problem" status events when we're having troubles reaching relays. svn:r15116 --- doc/spec/control-spec.txt | 2 +- src/or/connection.c | 24 +++++++++----- src/or/connection_edge.c | 7 ++-- src/or/connection_or.c | 10 +++--- src/or/directory.c | 3 +- src/or/or.h | 22 +++++++------ src/or/reasons.c | 68 ++++++++++++++++++++++++++++++--------- 7 files changed, 95 insertions(+), 41 deletions(-) diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt index da149c602c..4693407de6 100644 --- a/doc/spec/control-spec.txt +++ b/doc/spec/control-spec.txt @@ -1070,7 +1070,7 @@ $Id$ Reason = "MISC" / "DONE" / "CONNECTREFUSED" / "IDENTITY" / "CONNECTRESET" / "TIMEOUT" / "NOROUTE" / - "IOERROR" + "IOERROR" / "RESOURCELIMIT" NumCircuits counts both established and pending circuits. diff --git a/src/or/connection.c b/src/or/connection.c index 8cbead64e3..8cfea46ea4 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -500,14 +500,18 @@ connection_about_to_close_connection(connection_t *conn) if (!get_options()->HttpsProxy) router_set_status(or_conn->identity_digest, 0); if (conn->state == OR_CONN_STATE_CONNECTING) { - control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, 0); + control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, + errno_to_orconn_end_reason(or_conn->socket_error)); control_event_bootstrap_problem( - tor_socket_strerror(or_conn->socket_error), 0); + tor_socket_strerror(or_conn->socket_error), + errno_to_orconn_end_reason(or_conn->socket_error)); } else { int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED, reason); - control_event_bootstrap_problem("foo", reason); + /* XXX021 come up with a better string for the first arg */ + control_event_bootstrap_problem( + orconn_end_reason_to_control_string(reason), reason); } } /* Inform any pending (not attached) circs that they should @@ -1097,8 +1101,9 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type) } /** Take conn, make a nonblocking socket; try to connect to - * addr:port (they arrive in *host order*). If fail, return -1. Else - * assign s to conn-\>s: if connected return 1, if EAGAIN return 0. + * addr:port (they arrive in *host order*). If fail, return -1 and if + * applicable put your best guess about errno into *socket_error. + * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0. * * address is used to make the logs useful. * @@ -1106,7 +1111,7 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type) */ int connection_connect(connection_t *conn, const char *address, - uint32_t addr, uint16_t port) + uint32_t addr, uint16_t port, int *socket_error) { int s, inprogress = 0; struct sockaddr_in dest_addr; @@ -1123,8 +1128,9 @@ connection_connect(connection_t *conn, const char *address, s = tor_open_socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); if (s < 0) { + *socket_error = tor_socket_errno(-1); log_warn(LD_NET,"Error creating network socket: %s", - tor_socket_strerror(tor_socket_errno(-1))); + tor_socket_strerror(*socket_error)); return -1; } @@ -1140,8 +1146,9 @@ connection_connect(connection_t *conn, const char *address, } else { if (bind(s, (struct sockaddr*)&ext_addr, (socklen_t)sizeof(ext_addr)) < 0) { + *socket_error = tor_socket_errno(s); log_warn(LD_NET,"Error binding network socket: %s", - tor_socket_strerror(tor_socket_errno(s))); + tor_socket_strerror(*socket_error)); tor_close_socket(s); return -1; } @@ -1165,6 +1172,7 @@ connection_connect(connection_t *conn, const char *address, int e = tor_socket_errno(s); if (!ERRNO_IS_CONN_EINPROGRESS(e)) { /* yuck. kill it. */ + *socket_error = e; log_info(LD_NET, "connect() to %s:%u failed: %s",escaped_safe_str(address), port, tor_socket_strerror(e)); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 84f80eb18d..964824c15e 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -236,7 +236,7 @@ connection_edge_end_errno(edge_connection_t *conn) { uint8_t reason; tor_assert(conn); - reason = (uint8_t)errno_to_stream_end_reason(tor_socket_errno(conn->_base.s)); + reason = errno_to_stream_end_reason(tor_socket_errno(conn->_base.s)); return connection_edge_end(conn, reason); } @@ -2609,6 +2609,7 @@ connection_exit_connect(edge_connection_t *edge_conn) uint32_t addr; uint16_t port; connection_t *conn = TO_CONN(edge_conn); + int socket_error = 0; if (!connection_edge_is_rendezvous_stream(edge_conn) && router_compare_to_my_exit_policy(edge_conn)) { @@ -2644,8 +2645,10 @@ connection_exit_connect(edge_connection_t *edge_conn) } log_debug(LD_EXIT,"about to try connecting"); - switch (connection_connect(conn, conn->address, addr, port)) { + switch (connection_connect(conn, conn->address, addr, port, &socket_error)) { case -1: + /* XXX021 use socket_error below rather than trying to piece things + * together from the current errno, which may have been clobbered. */ connection_edge_end_errno(edge_conn); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); connection_free(conn); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 5a15bddbca..1081a46437 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -513,6 +513,7 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest) { or_connection_t *conn; or_options_t *options = get_options(); + int socket_error = 0; tor_assert(id_digest); @@ -534,7 +535,8 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest) port = options->HttpsProxyPort; } - switch (connection_connect(TO_CONN(conn), conn->_base.address, addr, port)) { + switch (connection_connect(TO_CONN(conn), conn->_base.address, + addr, port, &socket_error)) { case -1: /* If the connection failed immediately, and we're using * an https proxy, our https proxy is down. Don't blame the @@ -545,9 +547,9 @@ connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest) router_set_status(conn->identity_digest, 0); } control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, - END_OR_CONN_REASON_TCP_REFUSED); - /* XXX connection_connect() can fail for all sorts of other reasons */ - control_event_bootstrap_problem("foo", END_OR_CONN_REASON_TCP_REFUSED); + errno_to_orconn_end_reason(socket_error)); + control_event_bootstrap_problem(tor_socket_strerror(socket_error), + errno_to_orconn_end_reason(socket_error)); connection_free(TO_CONN(conn)); return NULL; case 0: diff --git a/src/or/directory.c b/src/or/directory.c index ee1940e9b8..1cf6f2b38d 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -659,6 +659,7 @@ directory_initiate_command(const char *address, uint32_t addr, { dir_connection_t *conn; or_options_t *options = get_options(); + int socket_error = 0; int use_begindir = supports_begindir && directory_command_should_use_begindir(options, addr, or_port, router_purpose, anonymized_connection); @@ -699,7 +700,7 @@ directory_initiate_command(const char *address, uint32_t addr, } switch (connection_connect(TO_CONN(conn), conn->_base.address, addr, - dir_port)) { + dir_port, &socket_error)) { case -1: connection_dir_request_failed(conn); /* retry if we want */ /* XXX we only pass 'conn' above, not 'resource', 'payload', diff --git a/src/or/or.h b/src/or/or.h index d561fe4ea9..1851808397 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -501,14 +501,15 @@ typedef enum { #define RELAY_COMMAND_INTRODUCE_ACK 40 /* Reasons why an OR connection is closed */ -#define END_OR_CONN_REASON_DONE 1 -#define END_OR_CONN_REASON_TCP_REFUSED 2 -#define END_OR_CONN_REASON_OR_IDENTITY 3 -#define END_OR_CONN_REASON_TLS_CONNRESET 4 /* tls connection reset by peer */ -#define END_OR_CONN_REASON_TLS_TIMEOUT 5 -#define END_OR_CONN_REASON_TLS_NO_ROUTE 6 /* no route to host/net */ -#define END_OR_CONN_REASON_TLS_IO_ERROR 7 /* tls read/write error */ -#define END_OR_CONN_REASON_TLS_MISC 8 +#define END_OR_CONN_REASON_DONE 1 +#define END_OR_CONN_REASON_REFUSED 2 /* connection refused */ +#define END_OR_CONN_REASON_OR_IDENTITY 3 +#define END_OR_CONN_REASON_CONNRESET 4 /* connection reset by peer */ +#define END_OR_CONN_REASON_TIMEOUT 5 +#define END_OR_CONN_REASON_NO_ROUTE 6 /* no route to host/net */ +#define END_OR_CONN_REASON_IO_ERROR 7 /* read/write error */ +#define END_OR_CONN_REASON_RESOURCE_LIMIT 8 /* sockets, buffers, etc */ +#define END_OR_CONN_REASON_MISC 9 /* Reasons why we (or a remote OR) might close a stream. See tor-spec.txt for * documentation of these. */ @@ -2742,7 +2743,7 @@ void _connection_mark_for_close(connection_t *conn,int line, const char *file); void connection_expire_held_open(void); int connection_connect(connection_t *conn, const char *address, uint32_t addr, - uint16_t port); + uint16_t port, int *socket_error); int retry_all_listeners(smartlist_t *replaced_conns, smartlist_t *new_conns); @@ -3589,10 +3590,11 @@ void policies_free_all(void); const char *stream_end_reason_to_control_string(int reason); const char *stream_end_reason_to_string(int reason); socks5_reply_status_t stream_end_reason_to_socks5_response(int reason); -int errno_to_stream_end_reason(int e); +uint8_t errno_to_stream_end_reason(int e); const char *orconn_end_reason_to_control_string(int r); int tls_error_to_orconn_end_reason(int e); +int errno_to_orconn_end_reason(int e); const char *circuit_end_reason_to_control_string(int reason); diff --git a/src/or/reasons.c b/src/or/reasons.c index 3052176e50..7ce73bafd2 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -144,9 +144,8 @@ stream_end_reason_to_socks5_response(int reason) #endif /** Given an errno from a failed exit connection, return a reason code - * appropriate for use in a RELAY END cell. - */ -int + * appropriate for use in a RELAY END cell. */ +uint8_t errno_to_stream_end_reason(int e) { switch (e) { @@ -192,19 +191,21 @@ orconn_end_reason_to_control_string(int r) switch (r) { case END_OR_CONN_REASON_DONE: return "DONE"; - case END_OR_CONN_REASON_TCP_REFUSED: + case END_OR_CONN_REASON_REFUSED: return "CONNECTREFUSED"; case END_OR_CONN_REASON_OR_IDENTITY: return "IDENTITY"; - case END_OR_CONN_REASON_TLS_CONNRESET: + case END_OR_CONN_REASON_CONNRESET: return "CONNECTRESET"; - case END_OR_CONN_REASON_TLS_TIMEOUT: + case END_OR_CONN_REASON_TIMEOUT: return "TIMEOUT"; - case END_OR_CONN_REASON_TLS_NO_ROUTE: + case END_OR_CONN_REASON_NO_ROUTE: return "NOROUTE"; - case END_OR_CONN_REASON_TLS_IO_ERROR: + case END_OR_CONN_REASON_IO_ERROR: return "IOERROR"; - case END_OR_CONN_REASON_TLS_MISC: + case END_OR_CONN_REASON_RESOURCE_LIMIT: + return "RESOURCELIMIT"; + case END_OR_CONN_REASON_MISC: return "MISC"; case 0: return ""; @@ -220,22 +221,59 @@ tls_error_to_orconn_end_reason(int e) { switch (e) { case TOR_TLS_ERROR_IO: - return END_OR_CONN_REASON_TLS_IO_ERROR; + return END_OR_CONN_REASON_IO_ERROR; case TOR_TLS_ERROR_CONNREFUSED: - return END_OR_CONN_REASON_TCP_REFUSED; + return END_OR_CONN_REASON_REFUSED; case TOR_TLS_ERROR_CONNRESET: - return END_OR_CONN_REASON_TLS_CONNRESET; + return END_OR_CONN_REASON_CONNRESET; case TOR_TLS_ERROR_NO_ROUTE: - return END_OR_CONN_REASON_TLS_NO_ROUTE; + return END_OR_CONN_REASON_NO_ROUTE; case TOR_TLS_ERROR_TIMEOUT: - return END_OR_CONN_REASON_TLS_TIMEOUT; + return END_OR_CONN_REASON_TIMEOUT; case TOR_TLS_WANTREAD: case TOR_TLS_WANTWRITE: case TOR_TLS_CLOSE: case TOR_TLS_DONE: return END_OR_CONN_REASON_DONE; default: - return END_OR_CONN_REASON_TLS_MISC; + return END_OR_CONN_REASON_MISC; + } +} + +/** Given an errno from a failed ORConn connection, return a reason code + * appropriate for use in the controller orconn events. */ +/* XXX021 somebody should think about whether the assignments I've made + * are accurate or useful. -RD */ +int +errno_to_orconn_end_reason(int e) +{ + switch (e) { + case EPIPE: + return END_OR_CONN_REASON_DONE; + S_CASE(ENOTCONN): + S_CASE(ENETUNREACH): + case ENETDOWN: /* << somebody should look into the Windows equiv */ + case EHOSTUNREACH: + return END_OR_CONN_REASON_NO_ROUTE; + S_CASE(ECONNREFUSED): + return END_OR_CONN_REASON_REFUSED; + S_CASE(ECONNRESET): + return END_OR_CONN_REASON_CONNRESET; + S_CASE(ETIMEDOUT): + return END_OR_CONN_REASON_TIMEOUT; + S_CASE(ENOBUFS): + case ENOMEM: + case ENFILE: + E_CASE(EMFILE): + E_CASE(EACCES): + E_CASE(EBADF): + E_CASE(EFAULT): + E_CASE(EINVAL): + return END_OR_CONN_REASON_RESOURCE_LIMIT; + default: + log_info(LD_OR, "Didn't recognize errno %d (%s).", + e, tor_socket_strerror(e)); + return END_OR_CONN_REASON_MISC; } }