]> granicus.if.org Git - libevent/commitdiff
recreate socket when udp failed
authorokhowang(王沛文) <okhowang@tencent.com>
Thu, 28 May 2020 09:32:05 +0000 (17:32 +0800)
committerokhowang(王沛文) <okhowang@tencent.com>
Mon, 8 Jun 2020 12:20:20 +0000 (20:20 +0800)
evdns.c

diff --git a/evdns.c b/evdns.c
index ae4ac3caf8092392a373eb2cd2a0964f85a51ab3..aeee58115bd088164618975e2206660aa2444576 100644 (file)
--- 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 */