diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | include/ifaddrs.h | 3 | ||||
-rw-r--r-- | inet/Makefile | 4 | ||||
-rw-r--r-- | sysdeps/generic/check_pf.c | 50 | ||||
-rw-r--r-- | sysdeps/generic/ifaddrs.c | 4 | ||||
-rw-r--r-- | sysdeps/posix/getaddrinfo.c | 30 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/check_pf.c | 193 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/ifaddrs.c | 10 |
8 files changed, 272 insertions, 33 deletions
@@ -1,5 +1,16 @@ 2003-06-10 Ulrich Drepper <drepper@redhat.com> + * inet/Makefile (aux): Add check_fd. + * include/ifaddrs.h: Add prototype for __check_fd. + * sysdeps/generic/check_fd.c: New file. + * sysdeps/unix/sysv/linux/check_fd.c: New file. + * sysdeps/unix/sysv/linux/ifaddrs.h (__no_netlink_support): Renamed + from no_netlink_support. Export. + * sysdeps/posix/getaddrinfo.c (getaddrinfo): Don't call getifaddrs, + call __check_pf. + + * sysdeps/generic/ifaddrs.h: Add libc_hidden_def. + * sysdeps/posix/getaddrinfo.c (getaddrinfo): Don't leak memory from getifaddr calls. diff --git a/include/ifaddrs.h b/include/ifaddrs.h index 294cd0d..aa20c35 100644 --- a/include/ifaddrs.h +++ b/include/ifaddrs.h @@ -1,7 +1,10 @@ #ifndef _IFADDRS_H #include <inet/ifaddrs.h> +#include <stdbool.h> libc_hidden_proto (getifaddrs) libc_hidden_proto (freeifaddrs) +extern void __check_pf (bool *seen_ipv4, bool *seen_ipv6) attribute_hidden; + #endif /* ifaddrs.h */ diff --git a/inet/Makefile b/inet/Makefile index 60a1a2e..b276877 100644 --- a/inet/Makefile +++ b/inet/Makefile @@ -1,4 +1,4 @@ -# Copyright (C) 1991-1999,2000,01,02 Free Software Foundation, Inc. +# Copyright (C) 1991-2002, 2003 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 @@ -47,6 +47,8 @@ routines := htonl htons \ getaliasent_r getaliasent getaliasname getaliasname_r \ in6_addr getnameinfo if_index ifaddrs +aux := check_pf + tests := htontest test_ifindex tst-ntoa tst-ether_aton tst-network \ tst-gethnm test-ifaddrs diff --git a/sysdeps/generic/check_pf.c b/sysdeps/generic/check_pf.c new file mode 100644 index 0000000..5d98c98 --- /dev/null +++ b/sysdeps/generic/check_pf.c @@ -0,0 +1,50 @@ +/* Determine protocol families for which interfaces exist. Generic version. + Copyright (C) 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <ifaddrs.h> +#include <netdb.h> + + +void +attribute_hidden +__check_pf (bool *seen_ipv4, bool *seen_ipv6) +{ + /* Get the interface list via getifaddrs. */ + struct ifaddrs *ifa = NULL; + if (getifaddrs (&ifa) != 0) + { + /* We cannot determine what interfaces are available. Be + pessimistic. */ + *seen_ipv4 = true; + *seen_ipv6 = true; + return; + } + + *seen_ipv4 = false; + *seen_ipv6 = false; + + struct ifaddrs *runp; + for (runp = ifa; runp != NULL; runp = runp->ifa_next) + if (runp->ifa_addr->sa_family == PF_INET) + *seen_ipv4 = true; + else if (runp->ifa_addr->sa_family == PF_INET6) + *seen_ipv6 = true; + + (void) freeifaddrs (ifa); +} diff --git a/sysdeps/generic/ifaddrs.c b/sysdeps/generic/ifaddrs.c index 7b29aa1..330aae3 100644 --- a/sysdeps/generic/ifaddrs.c +++ b/sysdeps/generic/ifaddrs.c @@ -1,5 +1,5 @@ /* getifaddrs -- get names and addresses of all network interfaces - Copyright (C) 2002 Free Software Foundation, Inc. + Copyright (C) 2002, 2003 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 @@ -30,6 +30,7 @@ getifaddrs (struct ifaddrs **ifap) __set_errno (ENOSYS); return -1; } +libc_hidden_def (getifaddrs) stub_warning (getifaddrs) void @@ -41,4 +42,5 @@ freeifaddrs (struct ifaddrs *ifa) /* Can't be called properly if getifaddrs never succeeded. */ abort (); } +libc_hidden_def (freeifaddrs) stub_warning (freeifaddrs) diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c index 23f7122..0d6ac84 100644 --- a/sysdeps/posix/getaddrinfo.c +++ b/sysdeps/posix/getaddrinfo.c @@ -925,32 +925,10 @@ getaddrinfo (const char *name, const char *service, { /* Determine whether we have IPv4 or IPv6 interfaces or both. We cannot cache the results since new interfaces could be - added at any time. - - XXX We are using getifaddrs here which is more costly than - it is really necessary. Once things are stable we will have - a special function which performs the task with less overhead. */ - struct ifaddrs *ifa = NULL; - - if (getifaddrs (&ifa) != 0) - /* Cannot get the interface list, very bad. */ - return EAI_SYSTEM; - - bool seen_ipv4 = false; - bool seen_ipv6 = false; - - struct ifaddrs *runp = ifa; - while (runp != NULL) - { - if (runp->ifa_addr->sa_family == PF_INET) - seen_ipv4 = true; - else if (runp->ifa_addr->sa_family == PF_INET6) - seen_ipv6 = true; - - runp = runp->ifa_next; - } - - (void) freeifaddrs (ifa); + added at any time. */ + bool seen_ipv4; + bool seen_ipv6; + __check_pf (&seen_ipv4, &seen_ipv6); /* Now make a decision on what we return, if anything. */ if (hints->ai_family == PF_UNSPEC) diff --git a/sysdeps/unix/sysv/linux/check_pf.c b/sysdeps/unix/sysv/linux/check_pf.c new file mode 100644 index 0000000..0bc57da --- /dev/null +++ b/sysdeps/unix/sysv/linux/check_pf.c @@ -0,0 +1,193 @@ +/* Determine protocol families for which interfaces exist. Linux version. + Copyright (C) 2003 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, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <ifaddrs.h> +#include <netdb.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/socket.h> + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +#include "kernel-features.h" + + +static int +make_request (int fd, pid_t pid, bool *seen_ipv4, bool *seen_ipv6) +{ + struct + { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + req.nlh.nlmsg_len = sizeof (req); + req.nlh.nlmsg_type = RTM_GETADDR; + req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = time (NULL); + req.g.rtgen_family = AF_UNSPEC; + + memset (&nladdr, '\0', sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + + if (TEMP_FAILURE_RETRY (__sendto (fd, (void *) &req, sizeof (req), 0, + (struct sockaddr *) &nladdr, + sizeof (nladdr))) < 0) + return -1; + + *seen_ipv4 = false; + *seen_ipv6 = false; + + bool done = false; + char buf[4096]; + struct iovec iov = { buf, sizeof (buf) }; + + do + { + struct msghdr msg = + { + (void *) &nladdr, sizeof (nladdr), + &iov, 1, + NULL, 0, + 0 + }; + + ssize_t read_len = TEMP_FAILURE_RETRY (__recvmsg (fd, &msg, 0)); + if (read_len < 0) + return -1; + + if (msg.msg_flags & MSG_TRUNC) + return -1; + + struct nlmsghdr *nlmh; + for (nlmh = (struct nlmsghdr *) buf; + NLMSG_OK (nlmh, (size_t) read_len); + nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, read_len)) + { + if ((pid_t) nlmh->nlmsg_pid != pid + || nlmh->nlmsg_seq != req.nlh.nlmsg_seq) + continue; + + if (nlmh->nlmsg_type == RTM_NEWADDR) + { + struct ifaddrmsg *ifam = (struct ifaddrmsg *) NLMSG_DATA (nlmh); + + switch (ifam->ifa_family) + { + case AF_INET: + *seen_ipv4 = true; + break; + case AF_INET6: + *seen_ipv6 = true; + break; + default: + /* Ignore. */ + break; + } + } + else if (nlmh->nlmsg_type == NLMSG_DONE) + /* We found the end, leave the loop. */ + done = true; + else ; + } + } + while (! done); + + close (fd); + + return 0; +} + + +/* We don't know if we have NETLINK support compiled in in our + Kernel. */ +#if __ASSUME_NETLINK_SUPPORT == 0 +/* Define in ifaddrs.h. */ +extern int __no_netlink_support attribute_hidden; +#else +# define __no_netlink_support 0 +#endif + + +void +attribute_hidden +__check_pf (bool *seen_ipv4, bool *seen_ipv6) +{ + if (! __no_netlink_support) + { + int fd = __socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + struct sockaddr_nl nladdr; + memset (&nladdr, '\0', sizeof (nladdr)); + nladdr.nl_family = AF_NETLINK; + + socklen_t addr_len = sizeof (nladdr); + + if (fd >= 0 + && __bind (fd, (struct sockaddr *) &nladdr, sizeof (nladdr)) == 0 + && __getsockname (fd, (struct sockaddr *) &nladdr, &addr_len) == 0 + && make_request (fd, nladdr.nl_pid, seen_ipv4, seen_ipv6) == 0) + /* It worked. */ + return; + + if (fd >= 0) + close (fd); + +#if __ASSUME_NETLINK_SUPPORT == 0 + /* Remember that there is no netlink support. */ + __no_netlink_support = 1; +#else + /* We cannot determine what interfaces are available. Be + pessimistic. */ + *seen_ipv4 = true; + *seen_ipv6 = true; +#endif + } + +#if __ASSUME_NETLINK_SUPPORT == 0 + /* No netlink. Get the interface list via getifaddrs. */ + struct ifaddrs *ifa = NULL; + if (getifaddrs (&ifa) != 0) + { + /* We cannot determine what interfaces are available. Be + pessimistic. */ + *seen_ipv4 = true; + *seen_ipv6 = true; + return; + } + + *seen_ipv4 = false; + *seen_ipv6 = false; + + struct ifaddrs *runp; + for (runp = ifa; runp != NULL; runp = runp->ifa_next) + if (runp->ifa_addr->sa_family == PF_INET) + *seen_ipv4 = true; + else if (runp->ifa_addr->sa_family == PF_INET6) + *seen_ipv6 = true; + + (void) freeifaddrs (ifa); +#endif +} diff --git a/sysdeps/unix/sysv/linux/ifaddrs.c b/sysdeps/unix/sysv/linux/ifaddrs.c index bc1ca85..837bfca 100644 --- a/sysdeps/unix/sysv/linux/ifaddrs.c +++ b/sysdeps/unix/sysv/linux/ifaddrs.c @@ -41,7 +41,7 @@ /* We don't know if we have NETLINK support compiled in in our Kernel, so include the old implementation as fallback. */ #if __ASSUME_NETLINK_SUPPORT == 0 -static int no_netlink_support; +int __no_netlink_support attribute_hidden; # define getifaddrs fallback_getifaddrs # include "sysdeps/gnu/ifaddrs.c" @@ -49,7 +49,7 @@ static int no_netlink_support; #else -# define no_netlink_support 0 +# define __no_netlink_support 0 #endif @@ -297,17 +297,17 @@ getifaddrs (struct ifaddrs **ifap) if (ifap) *ifap = NULL; - if (! no_netlink_support && netlink_open (&nh) < 0) + if (! __no_netlink_support && netlink_open (&nh) < 0) { #if __ASSUME_NETLINK_SUPPORT == 0 - no_netlink_support = 1; + __no_netlink_support = 1; #else return -1; #endif } #if __ASSUME_NETLINK_SUPPORT == 0 - if (no_netlink_support) + if (__no_netlink_support) return fallback_getifaddrs (ifap); #endif |