/* Distributed Checksum Clearinghouse * * kludge replacement for getifaddrs(3) for systems that lack it * * --C-LICENSE-- * $Revision: 1.28 $ */ #include "dcc_defs.h" #include "dcc_ifaddrs.h" #undef EXPANDED #if DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_NATIVE || DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_NONE #define EXPANDED /* Compile something to avoid warnings about empty files. * Make it global to suppress "defined but not used" warning. */ char dcc_getifaddrs_unneeded[] = "dcc_getifaddrs() unneeded"; #endif #if DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_SunOS #define EXPANDED /* * Copyright (c) 2006 WIDE Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* this code includes several fixes--vjs */ #include #include static int get_lifreq(int fd, struct lifreq **ifr_ret) { struct lifnum lifn; struct lifconf lifc; struct lifreq *lifrp; lifn.lifn_family = AF_UNSPEC; lifn.lifn_flags = 0; if (ioctl(fd, SIOCGLIFNUM, &lifn) == -1) lifn.lifn_count = 16; else lifn.lifn_count += 16; for (;;) { lifc.lifc_len = lifn.lifn_count * sizeof (*lifrp); lifrp = dcc_malloc(lifc.lifc_len); if (!lifrp) return (-1); lifc.lifc_family = AF_UNSPEC; lifc.lifc_flags = 0; lifc.lifc_buf = (char *)lifrp; if (ioctl(fd, SIOCGLIFCONF, &lifc) == -1) { dcc_free(lifrp); if (errno == EINVAL) { lifn.lifn_count <<= 1; continue; } return (-1); } if (lifc.lifc_len < (lifn.lifn_count - 1) * ISZ(*lifrp)) break; dcc_free(lifrp); lifn.lifn_count <<= 1; } *ifr_ret = lifrp; return (lifc.lifc_len / sizeof (*lifrp)); } static size_t nbytes(const struct lifreq *lifrp, int nlif, size_t socklen) { size_t len = 0; size_t slen; while (nlif > 0) { slen = strlen(lifrp->lifr_name) + 1; len += sizeof (struct ifaddrs) + ((slen + 3) & ~3); len += 3 * socklen; lifrp++; nlif--; } return (len); } static struct sockaddr * addrcpy(struct sockaddr_storage *addr, char **bufp) { char *buf = *bufp; size_t len; len = addr->ss_family == AF_INET ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6); (void) memcpy(buf, addr, len); *bufp = buf + len; return ((struct sockaddr *)buf); } static void populate(struct ifaddrs *ifa, int fd, struct lifreq *lifrp, int nlif, char **bufp) { char *buf = *bufp; size_t slen; while (nlif > 0) { ifa->ifa_next = (nlif > 1) ? ifa + 1 : 0; (void) strcpy(ifa->ifa_name = buf, lifrp->lifr_name); slen = strlen(lifrp->lifr_name) + 1; buf += (slen + 3) & ~3; if (ioctl(fd, SIOCGLIFFLAGS, lifrp) == -1) ifa->ifa_flags = 0; else ifa->ifa_flags = lifrp->lifr_flags; if (ioctl(fd, SIOCGLIFADDR, lifrp) == -1) ifa->ifa_addr = 0; else ifa->ifa_addr = addrcpy(&lifrp->lifr_addr, &buf); if (ioctl(fd, SIOCGLIFNETMASK, lifrp) == -1) ifa->ifa_netmask = 0; else ifa->ifa_netmask = addrcpy(&lifrp->lifr_addr, &buf); if (ifa->ifa_flags & IFF_POINTOPOINT) { if (ioctl(fd, SIOCGLIFDSTADDR, lifrp) == -1) ifa->ifa_dstaddr = 0; else ifa->ifa_dstaddr = addrcpy(&lifrp->lifr_dstaddr, &buf); } else if (ifa->ifa_flags & IFF_BROADCAST) { if (ioctl(fd, SIOCGLIFBRDADDR, lifrp) == -1) ifa->ifa_broadaddr = 0; else ifa->ifa_broadaddr = addrcpy(&lifrp->lifr_broadaddr, &buf); } else { ifa->ifa_dstaddr = 0; } ifa++; nlif--; lifrp++; } *bufp = buf; } int dcc_getifaddrs(struct ifaddrs **ifap) { int fd4, fd6; int nif4, nif6 = 0; struct lifreq *ifr4 = 0; struct lifreq *ifr6 = 0; struct ifaddrs *ifa = 0; char *buf; if ((fd4 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return (-1); if ((fd6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1 && errno != EAFNOSUPPORT) { close(fd4); return (-1); } if ((nif4 = get_lifreq(fd4, &ifr4)) == -1 || (fd6 >= 0 && (nif6 = get_lifreq(fd6, &ifr6)) == -1)) goto failure; if (nif4 == 0 && nif6 == 0) { close(fd4); if (fd6 >= 0) close(fd6); *ifap = 0; return (0); } ifa = dcc_malloc(nbytes(ifr4, nif4, sizeof (struct sockaddr_in)) + nbytes(ifr6, nif6, sizeof (struct sockaddr_in6))); if (!ifa) goto failure; buf = (char *)(ifa + nif4 + nif6); populate(ifa, fd4, ifr4, nif4, &buf); if (nif4 > 0 && nif6 > 0) ifa[nif4 - 1].ifa_next = ifa + nif4; if (fd6 > 0) populate(ifa + nif4, fd6, ifr6, nif6, &buf); close(fd4); if (fd6 >= 0) close(fd6); if (ifr4) dcc_free(ifr4); if (ifr6) dcc_free(ifr6); *ifap = ifa; return (0); failure: dcc_free(ifa); close(fd4); if (fd6 >= 0) close(fd6); if (ifr4) dcc_free(ifr4); if (ifr6) dcc_free(ifr6); *ifap = 0; return (-1); } #endif /* DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_SunOS */ #if DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_OTHER /* Odd systems and strange versions of Solaris */ #define EXPANDED #define BSD_COMP /* for strange SunOS */ #define _IO_TERMIOS_H /* OpenUNIX */ #include struct ifaddrs_all { struct ifaddrs ifa; DCC_SOCKU addr; char name[IFNAMSIZ]; }; int dcc_getifaddrs(struct ifaddrs **pif) { struct ifconf ifc; int buf_size; char *buf; struct ifreq *ifrp, *ifrp_next, ifr_f; struct ifaddrs_all *result0, *result, *rp; int i, serrno, s; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; /* get the list of interface names and addresses */ buf_size = 1024; for (;;) { buf = dcc_malloc(buf_size); if (!buf) { serrno = errno; close(s); errno = serrno; return -1; } ifc.ifc_buf = buf; ifc.ifc_len = buf_size; memset(buf, 0, buf_size); if (0 > ioctl(s, SIOCGIFCONF, (char *)&ifc) && errno != EINVAL) { serrno = errno; close(s); errno = serrno; *pif = 0; return -1; } if (ifc.ifc_len < buf_size && ifc.ifc_len != 0) break; /* double the size of our temporary buffer and try again * if the list of interfaces did not fit */ dcc_free(buf); buf_size *= 2; } /* start to build the list of interfaces */ i = (ifc.ifc_len/sizeof(struct ifreq)) * sizeof(*result); result = (struct ifaddrs_all *)dcc_malloc(i); if (!result) { serrno = errno; dcc_free(buf); close(s); errno = serrno; *pif = 0; return -1; } memset(result, 0, i); result0 = result; *pif = &result->ifa; for (ifrp = ifc.ifc_req; (void *)ifrp < (void *)&ifc.ifc_buf[ifc.ifc_len]; ifrp = ifrp_next) { #ifdef HAVE_SA_LEN i = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); if (i < ISZ(*ifrp)) ifrp_next = ifrp + 1; else ifrp_next = (struct ifreq *)((char *)ifrp + i); #else ifrp_next = ifrp + 1; #endif if (ifrp->ifr_addr.sa_family != AF_INET #ifdef AF_INET6 && ifrp->ifr_addr.sa_family != AF_INET6 #endif ) continue; memset(&ifr_f, 0, sizeof(ifr_f)); STRLCPY(ifr_f.ifr_name, ifrp->ifr_name, sizeof(ifr_f.ifr_name)); if (0 > ioctl(s, SIOCGIFFLAGS, (char *)&ifr_f)) { if (errno == ENXIO) continue; serrno = errno; close(s); dcc_free(buf); dcc_free(*pif); *pif = 0; errno = serrno; return -1; } memset(result, 0, sizeof(*result)); /* save the name only for debugging */ STRLCPY(result->name, ifrp->ifr_name, sizeof(result->name)); result->ifa.ifa_name = result->name; #ifdef HAVE_SA_LEN memcpy(&result->addr.sa, &ifrp->ifr_addr, ifrp->ifr_addr.sa_len); #else result->addr.sa = ifrp->ifr_addr; #endif result->ifa.ifa_addr = &result->addr.sa; result->ifa.ifa_flags = ifr_f.ifr_flags; result->ifa.ifa_next = &(result+1)->ifa; /* skip duplicate addresses */ rp = result0; for (;;) { if (rp >= result) { ++result; break; } if (DCC_SUnP_EQ(&rp->addr, &result->addr)) break; ++rp; } } if (&result->ifa == *pif) { *pif = 0; } else { (result-1)->ifa.ifa_next = 0; } dcc_free(buf); close(s); return 0; } #endif /* DCC_GETIFADDRS_COMPAT == DCC_GETIFADDRS_COMPAT_OTHER */ #ifndef EXPANDED #error "bad configure value for DCC_GETIFADDRS_COMPAT" #endif