diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2007-12-16 02:39:01 +0100 |
---|---|---|
committer | Michael Adam <obnox@samba.org> | 2008-02-19 21:55:09 +0100 |
commit | 2235ffe2f754da543b796446377b1419bf046204 (patch) | |
tree | c7664f78e8244c7b8e6f931a701f80d655f74eba | |
parent | 27b711414f09ccad140e6ef82d8d47c9cb4ba94a (diff) | |
download | samba-2235ffe2f754da543b796446377b1419bf046204.tar.gz samba-2235ffe2f754da543b796446377b1419bf046204.tar.bz2 samba-2235ffe2f754da543b796446377b1419bf046204.zip |
r26467: Use getifaddrs() for interface enumeration and provide replacements for platforms that don't have it in lib/replace.
(lib/replace part of 9b4924fbd8619033c55b4c6e2589da247332e7db - Michael)
(This used to be commit 789bf2d36bd728cc68b58cfb1e9570b90ca09af7)
-rw-r--r-- | source3/lib/replace/getifaddrs.c | 391 | ||||
-rw-r--r-- | source3/lib/replace/getifaddrs.m4 | 94 | ||||
-rw-r--r-- | source3/lib/replace/libreplace.m4 | 1 | ||||
-rw-r--r-- | source3/lib/replace/system/network.h | 25 |
4 files changed, 511 insertions, 0 deletions
diff --git a/source3/lib/replace/getifaddrs.c b/source3/lib/replace/getifaddrs.c new file mode 100644 index 0000000000..3969535a0b --- /dev/null +++ b/source3/lib/replace/getifaddrs.c @@ -0,0 +1,391 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Jeremy Allison 2007 + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/network.h" + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifndef SIOCGIFCONF +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#endif + +#ifdef HAVE_IFACE_GETIFADDRS +#define _FOUND_IFACE_ANY +#else + +void freeifaddrs(struct ifaddrs *ifp) +{ + free(ifp->ifa_name); + free(ifp->ifa_addr); + free(ifp->ifa_netmask); + free(ifp->ifa_dstaddr); + if (ifp->ifa_next != NULL) + freeifaddrs(ifp->ifa_next); + free(ifp); +} + +struct sockaddr *sockaddr_dup(struct sockaddr *sa) +{ + struct sockaddr *ret = calloc(1, sa->sa_len); + if (ret == NULL) + return NULL; + memcpy(ret, sa, sa->sa_len); + return ret; +} +#endif + +#if HAVE_IFACE_IFCONF + +/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1 + V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2. + + It probably also works on any BSD style system. */ + +int getifaddrs(struct ifaddrs **ifap) +{ + struct ifconf ifc; + char buff[8192]; + int fd, i, n; + struct ifreq *ifr=NULL; + int total = 0; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + struct ifaddrs *curif, *lastif; + + *ifap = NULL; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + + if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) { + close(fd); + return -1; + } + + ifr = ifc.ifc_req; + + n = ifc.ifc_len / sizeof(struct ifreq); + + /* Loop through interfaces, looking for given IP address */ + for (i=n-1;i>=0 && total < max_interfaces;i--) { + if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) { + freeifaddrs(*ifap); + } + + curif = calloc(1, sizeof(struct ifaddrs)); + if (lastif == NULL) { + *ifap = curif; + } else { + lastif->ifa_next = (*ifap); + } + + curif->ifa_name = strdup(ifr[i].ifr_name); + curif->ifa_flags = ifreq.ifr_flags; + curif->ifa_addr = sockaddr_dup(&ifr[i].ifr_addr); + curif->ifa_dstaddr = NULL; + curif->ifa_data = NULL; + curif->ifa_next = NULL; + curif->ifa_netmask = NULL; + + if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) { + freeifaddrs(*ifap); + return -1; + } + + if (!(ifr[i].ifr_flags & IFF_UP)) { + freeifaddrs(curif); + continue; + } + + if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) { + freeifaddrs(*ifap); + return -1; + } + + curif->ifa_netmask = sockaddr_dup(&ifr[i].ifr_addr); + + lastif = curif; + } + + close(fd); + + return 0; +} + +#define _FOUND_IFACE_ANY +#endif /* HAVE_IFACE_IFCONF */ +#ifdef HAVE_IFACE_IFREQ + +#ifndef I_STR +#include <sys/stropts.h> +#endif + +/**************************************************************************** +this should cover most of the streams based systems +Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code +****************************************************************************/ +int getifaddrs(struct ifaddrs **ifap) +{ + struct ifreq ifreq; + struct strioctl strioctl; + char buff[8192]; + int fd, i, n; + struct ifreq *ifr=NULL; + int total = 0; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + struct ifaddrs *curif; + + *ifap = NULL; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + strioctl.ic_cmd = SIOCGIFCONF; + strioctl.ic_dp = buff; + strioctl.ic_len = sizeof(buff); + if (ioctl(fd, I_STR, &strioctl) < 0) { + close(fd); + return -1; + } + + /* we can ignore the possible sizeof(int) here as the resulting + number of interface structures won't change */ + n = strioctl.ic_len / sizeof(struct ifreq); + + /* we will assume that the kernel returns the length as an int + at the start of the buffer if the offered size is a + multiple of the structure size plus an int */ + if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) { + ifr = (struct ifreq *)(buff + sizeof(int)); + } else { + ifr = (struct ifreq *)buff; + } + + /* Loop through interfaces */ + + for (i = 0; i<n && total < max_interfaces; i++) { + ifreq = ifr[i]; + + strioctl.ic_cmd = SIOCGIFFLAGS; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + freeifaddrs(*ifap); + return -1; + } + + if (!(ifreq.ifr_flags & IFF_UP)) { + continue; + } + + strioctl.ic_cmd = SIOCGIFADDR; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + freeifaddrs(*ifap); + return -1; + } + + curif = calloc(1, sizeof(struct ifaddrs)); + if (lastif == NULL) { + *ifap = curif; + } else { + lastif->ifa_next = (*ifap); + } + + curif->ifa_name = strdup(ifreq.ifr_name); + curif->ifa_flags = ifreq.ifr_flags; + curif->ifa_addr = sockaddr_dup(&ifreq.ifr_addr); + curif->ifa_dstaddr = NULL; + curif->ifa_data = NULL; + curif->ifa_next = NULL; + curif->ifa_netmask = NULL; + + strioctl.ic_cmd = SIOCGIFNETMASK; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + freeifaddrs(*ifap); + return -1; + } + + curif->ifa_netmask = sockaddr_dup(&ifreq.ifr_addr); + + lastif = curif; + } + + close(fd); + + return 0; +} + +#define _FOUND_IFACE_ANY +#endif /* HAVE_IFACE_IFREQ */ +#ifdef HAVE_IFACE_AIX + +/**************************************************************************** +this one is for AIX (tested on 4.2) +****************************************************************************/ +int getifaddrs(struct ifaddrs **ifap) +{ + char buff[8192]; + int fd, i; + struct ifconf ifc; + struct ifreq *ifr=NULL; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + int total = 0; + struct ifaddrs *curif, *lastif; + + *ifap = NULL; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + + if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) { + close(fd); + return -1; + } + + ifr = ifc.ifc_req; + + /* Loop through interfaces */ + i = ifc.ifc_len; + + while (i > 0) { + uint_t inc; + + inc = ifr->ifr_addr.sa_len; + + if (ioctl(fd, SIOCGIFADDR, ifr) != 0) { + freeaddrinfo(*ifap); + return -1; + } + + curif = calloc(1, sizeof(struct ifaddrs)); + if (lastif == NULL) { + *ifap = curif; + } else { + lastif->ifa_next = (*ifap); + } + + curif->ifa_name = strdup(ifr->ifr_name); + curif->ifa_flags = ifr->ifr_flags; + curif->ifa_addr = sockaddr_dup(&ifr->ifr_addr); + curif->ifa_dstaddr = NULL; + curif->ifa_data = NULL; + curif->ifa_netmask = NULL; + curif->ifa_next = NULL; + + if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) { + freeaddrinfo(*ifap); + return -1; + } + + if (!(ifr->ifr_flags & IFF_UP)) { + freeaddrinfo(curif); + continue; + } + + if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) { + freeaddrinfo(*ifap); + return -1; + } + + curif->ifa_netmask = sockaddr_dup(&ifr->ifr_addr); + + lastif = curif; + + next: + /* + * Patch from Archie Cobbs (archie@whistle.com). The + * addresses in the SIOCGIFCONF interface list have a + * minimum size. Usually this doesn't matter, but if + * your machine has tunnel interfaces, etc. that have + * a zero length "link address", this does matter. */ + + if (inc < sizeof(ifr->ifr_addr)) + inc = sizeof(ifr->ifr_addr); + inc += IFNAMSIZ; + + ifr = (struct ifreq*) (((char*) ifr) + inc); + i -= inc; + } + + close(fd); + return 0; +} + +#define _FOUND_IFACE_ANY +#endif /* HAVE_IFACE_AIX */ +#ifndef _FOUND_IFACE_ANY +int getifaddrs(struct ifaddrs **ifap) +{ + errno = ENOSYS; + return -1; +} +#endif + +#ifdef AUTOCONF_TEST +/* this is the autoconf driver to test get_interfaces() */ + + int main() +{ + struct ifaddrs *ifs; + int total = get_interfaces(ifaces, MAX_INTERFACES); + int i; + + int ret = getifaddrs(&ifs); + if (ret != 0) { + perror("getifaddrs() failed"); + return 1; + } + + while (ifs) { + printf("%-10s ", ifs->ifr_name); + printf("IP=%s ", inet_ntoa(((struct sockaddr_in *)ifs->ifr_addr)->sin_addr)); + printf("NETMASK=%s\n", inet_ntoa(((struct sockaddr_in *)ifs->ifr_netmask)->sin_addr)); + } + return 0; +} +#endif diff --git a/source3/lib/replace/getifaddrs.m4 b/source3/lib/replace/getifaddrs.m4 new file mode 100644 index 0000000000..f38827406d --- /dev/null +++ b/source3/lib/replace/getifaddrs.m4 @@ -0,0 +1,94 @@ +AC_CHECK_HEADERS([ifaddrs.h]) + +dnl test for getifaddrs and freeifaddrs +AC_CACHE_CHECK([for getifaddrs and freeifaddrs],samba_cv_HAVE_GETIFADDRS,[ +AC_TRY_COMPILE([ +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ifaddrs.h> +#include <netdb.h>], +[ +struct ifaddrs *ifp = NULL; +int ret = getifaddrs (&ifp); +freeifaddrs(ifp); +], +samba_cv_HAVE_GETIFADDRS=yes,samba_cv_HAVE_GETIFADDRS=no)]) +if test x"$samba_cv_HAVE_GETIFADDRS" = x"yes"; then + AC_DEFINE(HAVE_GETIFADDRS,1,[Whether the system has getifaddrs]) + AC_DEFINE(HAVE_FREEIFADDRS,1,[Whether the system has freeifaddrs]) + AC_DEFINE(HAVE_STRUCT_IFADDRS,1,[Whether struct ifaddrs is available]) +fi + +################## +# look for a method of finding the list of network interfaces +# +# This tests need LIBS="$NSL_LIBS $SOCKET_LIBS" +# +old_CFLAGS=$CFLAGS +old_LIBS=$LIBS +LIBS="$NSL_LIBS $SOCKET_LIBS" +CFLAGS="$CFLAGS -Ilib/replace" +iface=no; +################## +# look for a method of finding the list of network interfaces +iface=no; +AC_CACHE_CHECK([for iface getifaddrs],samba_cv_HAVE_IFACE_GETIFADDRS,[ +SAVE_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS ${SAMBA_CONFIGURE_CPPFLAGS}" +AC_TRY_RUN([ +#define NO_CONFIG_H 1 +#define HAVE_IFACE_GETIFADDRS 1 +#define AUTOCONF_TEST 1 +#include "${srcdir-.}/lib/replace/replace.c" +#include "${srcdir-.}/lib/replace/getifaddrs.c"], + samba_cv_HAVE_IFACE_GETIFADDRS=yes,samba_cv_HAVE_IFACE_GETIFADDRS=no,samba_cv_HAVE_IFACE_GETIFADDRS=cross)]) +CPPFLAGS="$SAVE_CPPFLAGS" +if test x"$samba_cv_HAVE_IFACE_GETIFADDRS" = x"yes"; then + iface=yes;AC_DEFINE(HAVE_IFACE_GETIFADDRS,1,[Whether iface getifaddrs is available]) +else + LIBREPLACEOBJ="${LIBREPLACEOBJ} getifaddrs.o" +fi + + +if test $iface = no; then +AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[ +AC_TRY_RUN([ +#define HAVE_IFACE_AIX 1 +#define AUTOCONF_TEST 1 +#undef _XOPEN_SOURCE_EXTENDED +#include "${srcdir-.}/lib/replace/getifaddrs.c"], + samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)]) +if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then + iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available]) +fi +fi + + +if test $iface = no; then +AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[ +AC_TRY_RUN([ +#define HAVE_IFACE_IFCONF 1 +#define AUTOCONF_TEST 1 +#include "${srcdir-.}/lib/replace/getifaddrs.c"], + samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)]) +if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then + iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available]) +fi +fi + +if test $iface = no; then +AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[ +AC_TRY_RUN([ +#define HAVE_IFACE_IFREQ 1 +#define AUTOCONF_TEST 1 +#include "${srcdir-.}/lib/replace/getifaddrs.c"], + samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)]) +if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then + iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available]) +fi +fi + +CFLAGS=$old_CFLAGS +LIBS=$old_LIBS diff --git a/source3/lib/replace/libreplace.m4 b/source3/lib/replace/libreplace.m4 index f866b3648f..6d1d6b8afc 100644 --- a/source3/lib/replace/libreplace.m4 +++ b/source3/lib/replace/libreplace.m4 @@ -348,6 +348,7 @@ m4_include(inet_ntop.m4) m4_include(inet_pton.m4) m4_include(getaddrinfo.m4) m4_include(repdir.m4) +m4_include(getifaddrs.m4) AC_CHECK_FUNCS([syslog printf memset memcpy],,[AC_MSG_ERROR([Required function not found])]) diff --git a/source3/lib/replace/system/network.h b/source3/lib/replace/system/network.h index aff8a841da..61be51744c 100644 --- a/source3/lib/replace/system/network.h +++ b/source3/lib/replace/system/network.h @@ -6,6 +6,7 @@ networking system include wrappers Copyright (C) Andrew Tridgell 2004 + Copyright (C) Jelmer Vernooij 2007 ** NOTE! The following LGPL license applies to the replace ** library. This does NOT imply that all of Samba is released @@ -97,6 +98,30 @@ int rep_inet_pton(int af, const char *src, void *dst); const char *rep_inet_ntop(int af, const void *src, char *dst, socklen_t size); #endif +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#ifndef HAVE_STRUCT_IFADDRS +struct ifaddrs { + struct ifaddrs *ifa_next; /* Pointer to next struct */ + char *ifa_name; /* Interface name */ + u_int ifa_flags; /* Interface flags */ + struct sockaddr *ifa_addr; /* Interface address */ + struct sockaddr *ifa_netmask; /* Interface netmask */ + struct sockaddr *ifa_dstaddr; /* P2P interface destination */ + void *ifa_data; /* Address specific data */ +}; +#endif + +#ifndef HAVE_GETIFADDRS +int rep_getifaddrs(struct ifaddrs **); +#endif + +#ifndef HAVE_FREEIFADDRS +int rep_freeifaddrs(struct ifaddrs **); +#endif + /* * Some systems have getaddrinfo but not the * defines needed to use it. |