diff --git a/ChangeLog b/ChangeLog index afe50078..ff0a62e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ Changes in 1.4.12-stable: o Use __VA_ARGS__ syntax for varargs macros in event_rpcgen when compiler is not GCC. o Activate fd events in a pseudorandom order with O(N) backends, so that we don't systematically favor low fds (select) or earlier-added fds (poll, win32). o Fix another pair of fencepost bugs in epoll.c. [Patch from Adam Langley.] + o Do not break evdns connections to nameservers when our IP changes. Changes in 1.4.11-stable: o Fix a bug when removing a timeout from the heap. [Patch from Marko Kreen] diff --git a/evdns.c b/evdns.c index b9f6004e..1e7ab520 100644 --- a/evdns.c +++ b/evdns.c @@ -209,6 +209,7 @@ struct reply { struct nameserver { int socket; /* a connected UDP socket */ u32 address; + u16 port; int failed_times; /* number of times which we have given this server a chance */ int timedout; /* number of times in a row a request has timed out */ struct event event; @@ -1150,19 +1151,38 @@ nameserver_pick(void) { } } +static int +address_is_correct(struct nameserver *ns, struct sockaddr *sa, socklen_t slen) +{ + struct sockaddr_in *sin = (struct sockaddr_in*) sa; + if (sa->sa_family != AF_INET || slen != sizeof(struct sockaddr_in)) + return 0; + if (sin->sin_addr.s_addr != ns->address) + return 0; + return 1; +} + /* this is called when a namesever socket is ready for reading */ static void nameserver_read(struct nameserver *ns) { u8 packet[1500]; + struct sockaddr_storage ss; + socklen_t addrlen = sizeof(ss); for (;;) { - const int r = recv(ns->socket, packet, sizeof(packet), 0); + const int r = recvfrom(ns->socket, packet, sizeof(packet), 0, + (struct sockaddr*)&ss, &addrlen); if (r < 0) { int err = last_error(ns->socket); if (error_is_eagain(err)) return; nameserver_failed(ns, strerror(err)); return; } + if (!address_is_correct(ns, (struct sockaddr*)&ss, addrlen)) { + log(EVDNS_LOG_WARN, "Address mismatch on received " + "DNS packet."); + return; + } ns->timedout = 0; reply_parse(packet, r); } @@ -1890,7 +1910,15 @@ evdns_request_timeout_callback(int fd, short events, void *arg) { /* 2 other failure */ static int evdns_request_transmit_to(struct request *req, struct nameserver *server) { - const int r = send(server->socket, req->request, req->request_len, 0); + struct sockaddr_in sin; + int r; + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = req->ns->address; + sin.sin_port = req->ns->port; + sin.sin_family = AF_INET; + + r = sendto(server->socket, req->request, req->request_len, 0, + (struct sockaddr*)&sin, sizeof(sin)); if (r < 0) { int err = last_error(server->socket); if (error_is_eagain(err)) return 1; @@ -2085,7 +2113,6 @@ _evdns_nameserver_add_impl(unsigned long int address, int port) { const struct nameserver *server = server_head, *const started_at = server_head; struct nameserver *ns; - struct sockaddr_in sin; int err = 0; if (server) { do { @@ -2104,15 +2131,9 @@ _evdns_nameserver_add_impl(unsigned long int address, int port) { ns->socket = socket(PF_INET, SOCK_DGRAM, 0); if (ns->socket < 0) { err = 1; goto out1; } evutil_make_socket_nonblocking(ns->socket); - sin.sin_addr.s_addr = address; - sin.sin_port = htons(port); - sin.sin_family = AF_INET; - if (connect(ns->socket, (struct sockaddr *) &sin, sizeof(sin)) != 0) { - err = 2; - goto out2; - } ns->address = address; + ns->port = htons(port); ns->state = 1; event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns); if (event_add(&ns->event, NULL) < 0) {