From efbe563b4e247c6a39c0373f066bad3ec585d936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?okhowang=28=E7=8E=8B=E6=B2=9B=E6=96=87=29?= Date: Thu, 28 May 2020 17:32:05 +0800 Subject: [PATCH] recreate socket when udp failed --- evdns.c | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/evdns.c b/evdns.c index ae4ac3ca..aeee5811 100644 --- a/evdns.c +++ b/evdns.c @@ -716,7 +716,7 @@ request_swap_ns(struct request *req, struct nameserver *ns) { /* called when a nameserver has been deemed to have failed. For example, too */ /* many packets have timed out etc */ static void -nameserver_failed(struct nameserver *const ns, const char *msg) { +nameserver_failed(struct nameserver *const ns, const char *msg, int err) { struct request *req, *started_at; struct evdns_base *base = ns->base; int i; @@ -742,9 +742,39 @@ nameserver_failed(struct nameserver *const ns, const char *msg) { ns->state = 0; ns->failed_times = 1; - disconnect_and_free_connection(ns->connection); - ns->connection = NULL; + if (ns->connection) { + disconnect_and_free_connection(ns->connection); + ns->connection = NULL; + } else if (err == ENOTCONN) { + /* XXX: If recvfrom results in ENOTCONN, the socket remains readable + * which triggers another recvfrom. The observed behavior is 100% CPU use. + * This occurs on iOS (kqueue) after the process has been backgrounded + * for a long time (~300 seconds) and then resumed. + * All sockets, TCP and UDP, seem to get ENOTCONN and must be closed. + * https://github.com/libevent/libevent/issues/265 */ + const struct sockaddr *address = (const struct sockaddr *)&ns->address; + evutil_closesocket(ns->socket); + ns->socket = evutil_socket_(address->sa_family, + SOCK_DGRAM | EVUTIL_SOCK_NONBLOCK | EVUTIL_SOCK_CLOEXEC, 0); + if (base->global_outgoing_addrlen && + !evutil_sockaddr_is_loopback_(address)) { + if (bind(ns->socket, + (struct sockaddr *)&base->global_outgoing_address, + base->global_outgoing_addrlen) < 0) { + log(EVDNS_LOG_WARN, "Couldn't bind to outgoing address"); + } + } + + event_del(&ns->event); + event_assign(&ns->event, ns->base->event_base, ns->socket, + EV_READ | (ns->write_waiting ? EV_WRITE : 0) | EV_PERSIST, + nameserver_ready_callback, ns); + if (!base->disable_when_inactive && event_add(&ns->event, NULL) < 0) { + log(EVDNS_LOG_WARN, "Couldn't add %s event", + ns->write_waiting ? "rw": "read"); + } + } if (evtimer_add(&ns->timeout_event, &base->global_nameserver_probe_initial_timeout) < 0) { log(EVDNS_LOG_WARN, @@ -1101,7 +1131,7 @@ reply_handle(struct request *const req, u16 flags, u32 ttl, struct reply *reply) char msg[64]; evutil_snprintf(msg, sizeof(msg), "Bad response %d (%s)", error, evdns_err_to_string(error)); - nameserver_failed(req->ns, msg); + nameserver_failed(req->ns, msg, 0); if (!request_reissue(req)) return; } break; @@ -1637,7 +1667,7 @@ nameserver_read(struct nameserver *ns) { ASSERT_LOCKED(ns->base); if (!packet) { - nameserver_failed(ns, "not enough memory"); + nameserver_failed(ns, "not enough memory", 0); return; } @@ -1650,7 +1680,7 @@ nameserver_read(struct nameserver *ns) { if (EVUTIL_ERR_RW_RETRIABLE(err)) goto done; nameserver_failed(ns, - evutil_socket_error_to_string(err)); + evutil_socket_error_to_string(err), err); goto done; } if (evutil_sockaddr_cmp((struct sockaddr*)&ss, @@ -2715,7 +2745,7 @@ evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) { reply_schedule_callback(req, 0, DNS_ERR_TIMEOUT, NULL); request_finished(req, &REQ_HEAD(req->base, req->trans_id), 1); - nameserver_failed(ns, "request timed out."); + nameserver_failed(ns, "request timed out.", 0); } else { /* if request is using tcp connection, so tear connection */ if (req->handle->tcp_flags & DNS_QUERY_USEVC) { @@ -2734,7 +2764,7 @@ evdns_request_timeout_callback(evutil_socket_t fd, short events, void *arg) { req->ns->timedout++; if (req->ns->timedout > req->base->global_max_nameserver_timeout) { req->ns->timedout = 0; - nameserver_failed(req->ns, "request timed out."); + nameserver_failed(req->ns, "request timed out.", 0); } } } @@ -2766,7 +2796,7 @@ evdns_request_transmit_to(struct request *req, struct nameserver *server) { int err = evutil_socket_geterror(server->socket); if (EVUTIL_ERR_RW_RETRIABLE(err)) return 1; - nameserver_failed(req->ns, evutil_socket_error_to_string(err)); + nameserver_failed(req->ns, evutil_socket_error_to_string(err), err); return 2; } else if (r != (int)req->request_len) { return 1; /* short write */