diff options
author | okhowang(王沛文) <okhowang@tencent.com> | 2020-05-28 17:32:05 +0800 |
---|---|---|
committer | okhowang(王沛文) <okhowang@tencent.com> | 2020-06-08 20:20:20 +0800 |
commit | efbe563b4e247c6a39c0373f066bad3ec585d936 (patch) | |
tree | 9cf0f6fd37b3a1edd635a453be8f6edbc5bbfa05 /evdns.c | |
parent | 2aaa7358a11319c5f6c3211b7408a66b3faded8e (diff) | |
download | libevent-efbe563b4e247c6a39c0373f066bad3ec585d936.tar.gz |
recreate socket when udp failed
Diffstat (limited to 'evdns.c')
-rw-r--r-- | evdns.c | 48 |
1 files changed, 39 insertions, 9 deletions
@@ -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 */ |