d8307d
commit 08504de71813ddbd447bfbca4a325cbe8ce8bcda
d8307d
Author: Florian Weimer <fweimer@redhat.com>
d8307d
Date:   Tue Mar 12 11:40:47 2019 +0100
d8307d
d8307d
    resolv: Enable full ICMP errors for UDP DNS sockets [BZ #24047]
d8307d
    
d8307d
    The Linux kernel suppresses some ICMP error messages by default for
d8307d
    UDP sockets.  This commit enables full ICMP error reporting,
d8307d
    hopefully resulting in faster failover to working name servers.
d8307d
d8307d
diff --git a/resolv/Makefile b/resolv/Makefile
d8307d
index 8f22e6a154..ebe1b733f2 100644
d8307d
--- a/resolv/Makefile
d8307d
+++ b/resolv/Makefile
d8307d
@@ -105,7 +105,7 @@ libresolv-routines := res_comp res_debug \
d8307d
 		      res_data res_mkquery res_query res_send		\
d8307d
 		      inet_net_ntop inet_net_pton inet_neta base64	\
d8307d
 		      ns_parse ns_name ns_netint ns_ttl ns_print	\
d8307d
-		      ns_samedomain ns_date \
d8307d
+		      ns_samedomain ns_date res_enable_icmp \
d8307d
 		      compat-hooks compat-gethnamaddr
d8307d
 
d8307d
 libanl-routines := gai_cancel gai_error gai_misc gai_notify gai_suspend \
d8307d
diff --git a/resolv/res_enable_icmp.c b/resolv/res_enable_icmp.c
d8307d
new file mode 100644
d8307d
index 0000000000..bdc9220f08
d8307d
--- /dev/null
d8307d
+++ b/resolv/res_enable_icmp.c
d8307d
@@ -0,0 +1,37 @@
d8307d
+/* Enable full ICMP errors on a socket.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <errno.h>
d8307d
+#include <netinet/in.h>
d8307d
+#include <sys/socket.h>
d8307d
+
d8307d
+int
d8307d
+__res_enable_icmp (int family, int fd)
d8307d
+{
d8307d
+  int one = 1;
d8307d
+  switch (family)
d8307d
+    {
d8307d
+    case AF_INET:
d8307d
+      return setsockopt (fd, SOL_IP, IP_RECVERR, &one, sizeof (one));
d8307d
+    case AF_INET6:
d8307d
+      return setsockopt (fd, SOL_IPV6, IPV6_RECVERR, &one, sizeof (one));
d8307d
+    default:
d8307d
+      __set_errno (EAFNOSUPPORT);
d8307d
+      return -1;
d8307d
+    }
d8307d
+}
d8307d
diff --git a/resolv/res_send.c b/resolv/res_send.c
d8307d
index fa040c1198..0f6ec83a7b 100644
d8307d
--- a/resolv/res_send.c
d8307d
+++ b/resolv/res_send.c
d8307d
@@ -943,6 +943,18 @@ reopen (res_state statp, int *terrno, int ns)
d8307d
 			return (-1);
d8307d
 		}
d8307d
 
d8307d
+		/* Enable full ICMP error reporting for this
d8307d
+		   socket.  */
d8307d
+		if (__res_enable_icmp (nsap->sa_family,
d8307d
+				       EXT (statp).nssocks[ns]) < 0)
d8307d
+		  {
d8307d
+		    int saved_errno = errno;
d8307d
+		    __res_iclose (statp, false);
d8307d
+		    __set_errno (saved_errno);
d8307d
+		    *terrno = saved_errno;
d8307d
+		    return -1;
d8307d
+		  }
d8307d
+
d8307d
 		/*
d8307d
 		 * On a 4.3BSD+ machine (client and server,
d8307d
 		 * actually), sending to a nameserver datagram
d8307d
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
d8307d
index 6ab8f2af09..1500adc607 100644
d8307d
--- a/resolv/resolv-internal.h
d8307d
+++ b/resolv/resolv-internal.h
d8307d
@@ -100,4 +100,10 @@ libc_hidden_proto (__inet_pton_length)
d8307d
 /* Called as part of the thread shutdown sequence.  */
d8307d
 void __res_thread_freeres (void) attribute_hidden;
d8307d
 
d8307d
+/* The Linux kernel does not enable all ICMP messages on a UDP socket
d8307d
+   by default.  A call this function enables full error reporting for
d8307d
+   the socket FD.  FAMILY must be AF_INET or AF_INET6.  Returns 0 on
d8307d
+   success, -1 on failure.  */
d8307d
+int __res_enable_icmp (int family, int fd) attribute_hidden;
d8307d
+
d8307d
 #endif  /* _RESOLV_INTERNAL_H */