Blob Blame History Raw
commit 08504de71813ddbd447bfbca4a325cbe8ce8bcda
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Mar 12 11:40:47 2019 +0100

    resolv: Enable full ICMP errors for UDP DNS sockets [BZ #24047]
    
    The Linux kernel suppresses some ICMP error messages by default for
    UDP sockets.  This commit enables full ICMP error reporting,
    hopefully resulting in faster failover to working name servers.

diff --git a/resolv/Makefile b/resolv/Makefile
index 033c3c651f0deb1b..133fe5885c5b65b5 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -92,7 +92,7 @@ libresolv-routines := res_comp res_debug \
 		      res_data res_mkquery res_query res_send		\
 		      inet_net_ntop inet_net_pton inet_neta base64	\
 		      ns_parse ns_name ns_netint ns_ttl ns_print	\
-		      ns_samedomain ns_date \
+		      ns_samedomain ns_date res_enable_icmp \
 		      compat-hooks compat-gethnamaddr
 
 libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
diff --git a/resolv/res_enable_icmp.c b/resolv/res_enable_icmp.c
new file mode 100644
index 0000000000000000..bdc9220f08cef71d
--- /dev/null
+++ b/resolv/res_enable_icmp.c
@@ -0,0 +1,37 @@
+/* Enable full ICMP errors on a socket.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+int
+__res_enable_icmp (int family, int fd)
+{
+  int one = 1;
+  switch (family)
+    {
+    case AF_INET:
+      return setsockopt (fd, SOL_IP, IP_RECVERR, &one, sizeof (one));
+    case AF_INET6:
+      return setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &one, sizeof (one));
+    default:
+      __set_errno (EAFNOSUPPORT);
+      return -1;
+    }
+}
diff --git a/resolv/res_send.c b/resolv/res_send.c
index 05c7ba511b0383c1..e57bb12a66b087e4 100644
--- a/resolv/res_send.c
+++ b/resolv/res_send.c
@@ -943,6 +943,18 @@ reopen (res_state statp, int *terrno, int ns)
 			return (-1);
 		}
 
+		/* Enable full ICMP error reporting for this
+		   socket.  */
+		if (__res_enable_icmp (nsap->sa_family,
+				       EXT (statp).nssocks[ns]) < 0)
+		  {
+		    int saved_errno = errno;
+		    __res_iclose (statp, false);
+		    __set_errno (saved_errno);
+		    *terrno = saved_errno;
+		    return -1;
+		  }
+
 		/*
 		 * On a 4.3BSD+ machine (client and server,
 		 * actually), sending to a nameserver datagram
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
index 32dc44777e311849..d73a2c1b944bcbbe 100644
--- a/resolv/resolv-internal.h
+++ b/resolv/resolv-internal.h
@@ -97,4 +97,10 @@ int __res_nopt (struct resolv_context *, int n0,
 int __inet_pton_length (int af, const char *src, size_t srclen, void *);
 libc_hidden_proto (__inet_pton_length)
 
+/* The Linux kernel does not enable all ICMP messages on a UDP socket
+   by default.  A call this function enables full error reporting for
+   the socket FD.  FAMILY must be AF_INET or AF_INET6.  Returns 0 on
+   success, -1 on failure.  */
+int __res_enable_icmp (int family, int fd) attribute_hidden;
+
 #endif  /* _RESOLV_INTERNAL_H */