/*
* int zgetifaddress(int af, char *ifname, struct sockaddr *)
*/
/* loadifaddresses() -- for ZMailer
A piece of code from sendmail-8.7.1:src/conf.c
with serious mutations... We want a list of ADDRESSES,
not hostnames per se... Also unlike sendmail, we keep
redoing this query every so often -- in fact for EVERY
smtp connection open!
Original copyright SunSoft/Berkeley/Almann, modifications
by Matti Aarnio <mea@nic.funet.fi> 1997,2000
*/
#include "hostenv.h"
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if (defined(__svr4__) || defined(__SVR4)) && defined(__sun)
# define BSD_COMP /* Damn Solaris, and its tricks... */
#endif
#include <sys/ioctl.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifndef EAI_AGAIN
# include "netdb6.h" /* IPv6 API stuff */
#endif
#include <netinet/in.h>
#ifdef HAVE_NETINET_IN6_H
# include <netinet/in6.h>
#endif
#ifdef HAVE_NETINET6_IN6_H
# include <netinet6/in6.h>
#endif
#ifdef HAVE_LINUX_IN6_H
# include <linux/in6.h>
#endif
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
union sockaddr_uni {
struct sockaddr sa;
struct sockaddr_in v4;
#if defined(AF_INET6) && defined(INET6)
struct sockaddr_in6 v6;
#endif
};
#include "libc.h"
#include "zmalloc.h"
#include "libz.h"
/*
** LOADIFADDRESSES -- load interface-specific addresses
*/
/* autoconfig test script!
AC_CACHE_CHECK([for 'sa_len' in 'struct sockaddr'], ac_cv_struct_sa_len,
[AC_TRY_COMPILE([#include <sys/types.h>
#include <sys/socket.h>], [struct sockaddr sa; sa.sa_len = 0; ],
ac_cv_struct_sa_len=yes, ac_cv_struct_sa_len=no)])
if test "$ac_cv_struct_sa_len" = yes; then
AC_DEFINE(HAVE_SA_LEN)
fi
*/
#include "hostenv.h"
struct rtentry; /* Dummies for BSD systems */
struct mbuf;
#include <arpa/inet.h>
#include <net/if.h>
/* #include "l-if.h" --- just some fake test stuff for SIOCGLIF*** */
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
int
zgetifaddress(af, ifname, sap)
int af;
const char *ifname;
Usockaddr * sap;
{
int i;
#ifdef HAVE_GETIFADDRS
/* #warning "GETIFADDRS() code chosen" */
{
struct ifaddrs *ifar = NULL, *ifa;
if (sap == NULL) return -1; /* Bad param! */
i = getifaddrs( &ifar );
if (i < 0) {
free(sap);
return i;
}
i = -1;
for (ifa = ifar; ifa ; ifa = ifa->ifa_next) {
if ((ifa->ifa_flags & IFF_UP) &&
(ifa->ifa_addr != NULL)) {
struct sockaddr *sa = ifa->ifa_addr;
if (strcasecmp(ifname, ifa->ifa_name) != 0)
continue; /* next ... */
/* We have matching name, do we have matching AF ?? */
if (sa->sa_family != af)
continue; /* Nope.. */
if (sa->sa_family == AF_INET) {
/* pick the whole sockaddr package! */
memcpy(sap, sa, sizeof(struct sockaddr_in));
i = 0; /* Found! */
break;
}
#if defined(AF_INET6) && defined(INET6)
if (sa->sa_family == AF_INET6) {
/* pick the whole sockaddr package! */
memcpy(sap, sa, sizeof(struct sockaddr_in6));
i = 0;
break;
}
#endif
}
}
#ifdef HAVE_FREEIFADDRS
freeifaddrs(ifar);
#else
free(ifar);
#endif
return i;
}
#elif defined(SIOCGLIFADDR)
/* #warning "SIOCGLIFADDR code chosen" */
{
/* Named IPv4 interface */
int sk2 = socket(af, SOCK_DGRAM, 0);
struct lifreq lifr;
memset(&lifr, 0, sizeof(lifr));
strncpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));
lifr.lifr_addr.ss_family = af;
i = -1;
if (ioctl(sk2, SIOCGLIFADDR, &lifr) == 0) {
/* Got the IP address of the interface */
i = 0;
if (af == AF_INET)
memcpy(sap, &lifr.lifr_addr, sizeof(struct sockaddr_in));
#if defined(AF_INET6) && defined(INET6)
else if (af == AF_INET6)
memcpy(sap, &lifr.lifr_addr, sizeof(struct sockaddr_in6));
#endif
else
i = -1;
}
close(sk2);
return i;
}
#elif defined(SIOCGIFADDR)
/* #warning "SIOCGIFADDR code chosen" */
{
int sk2 = socket(af, SOCK_DGRAM, 0);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = 0;
ifr.ifr_addr.sa_family = af;
i = -1;
if (ioctl(sk2, SIOCGIFADDR, &ifr) == 0) {
/* Got the IP address of the interface */
i = 0;
if (af == AF_INET)
memcpy(sap, &ifr.ifr_addr, sizeof(struct sockaddr_in));
#if defined(AF_INET6) && defined(INET6)
else if (af == AF_INET6)
memcpy(sap, &ifr.ifr_addr, sizeof(struct sockaddr_in6));
#endif
else
i = -1;
}
close(sk2);
return i;
}
#elif defined(SIOCGLIFCONF)
/* #warning "SIOCGLIFCONF code chosen" */
{
struct lifconf ifc;
int ifbufsize = 4 * sizeof(struct lifreq) + 4;
char *interfacebuf = (void*)malloc(ifbufsize);
int s, n;
if (!interfacebuf) {
return -2;
}
s = socket(af, SOCK_DGRAM, 0);
if (s < 0) {
free(interfacebuf);
return -1;
}
/* Redo the buffer size increase until we get response size to
be something of LESS THAN the buffer size minus two-times
the sizeof(struct ifreq) -- because then we don't have
a potential case of having larger block of addresses in
system, but us being unable to get them all..
Usually system has TWO interfaces -- loopback, and the LAN,
thus the following loop is executed exactly once! */
/* Some utilities seem to do this probing also with sockets of
AF_X25, AF_IPX, AF_AX25, AF_INET6, etc. -address families,
but on Linux (very least) it can be done with any of them,
thus we use the one that is most likely available: AF_INET */
for (;;) {
/* get the list of known IP address from the kernel */
ifbufsize <<= 1;
interfacebuf = (void*)realloc(interfacebuf, ifbufsize);
memset(&lifc, 0, sizeof(lifc));
lifc.lifc_buf = interfacebuf;
lifc.lifc_len = ifbufsize;
lifc.lifc_family = af;
lifc.lifc_flags = 0;
if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
if (errno == EINVAL)
continue;
if (errno == EINTR)
continue;
}
if (lifc.lifc_len < (ifbufsize - 2*sizeof(struct lifreq)))
break;
/* Redo the query, perhaps didn't get them all.. */
}
/* Count how many addresses listed */
n = -1; /* return code ... */
for (i = 0; i < lifc.lifc_len; ) {
struct lifreq *lifr = (struct lifreq *) &lifc.lifc_buf[i];
union sockaddr_uni *sa = (union sockaddr_uni *) &lifr->lifr_addr;
#ifdef SIOCGLIFFLAGS
struct lifreq lifrf;
#endif
#if defined(SA_LEN)
if (SA_LEN(((struct sockaddr *)sa)) > sizeof(lifr->lifr_addr))
i += sizeof lifr->lifr_name + SA_LEN(((struct sockaddr *)sa));
else
#elif defined(HAVE_SA_LEN)
if (sa->sa.sa_len > sizeof lifr->lifr_addr)
i += sizeof lifr->lifr_name + sa->sa.sa_len;
else
#endif
i += sizeof *lifr;
if (strcasecmp(lifr->lifr_name, ifname) != 0)
continue; /* Not this interface.. */
/* Known address families ?
The one we scanned for ??*/
if (lifr->lifr_addr.sa_family != af)
continue; /* Not desired address family! */
/* Now, what do the flags say ? Are they alive ? */
#ifdef SIOCGLIFFLAGS
memset(&lifrf, 0, sizeof(struct lifreq));
strncpy(lifrf.lifr_name, ifname, sizeof(lifrf.lifr_name));
if (ioctl(s, SIOCGLIFFLAGS, (char *) &lifrf) < 0)
continue; /* Failed.. */
if (!(IFF_UP & lifrf.lifr_flags))
continue; /* Not alive */
#else
/* printf("ifr_flags=0x%x\n",lifr->lifr_flags); */
if (!(IFF_UP & lifr->lifr_flags))
continue; /* Not alive */
#endif
if (af == AF_INET) {
/* pick the whole sockaddr package! */
memcpy(sap, sa, sizeof(struct sockaddr_in));
n = 0;
break,
}
#if defined(AF_INET6) && defined(INET6)
if (af == AF_INET6) {
/* pick the whole sockaddr package! */
memcpy(sap, sa, sizeof(struct sockaddr_in6));
n = 0;
break,
}
#endif
break; /* 't was desired address family... */
}
close(s);
free(interfacebuf);
return n;
}
#elif defined(SIOCGIFCONF)
/* #warning "SIOCGIFCONF code chosen" */
{
struct ifconf ifc;
int ifbufsize = 4 * sizeof(struct ifreq) + 4;
char *interfacebuf = (void*)malloc(ifbufsize);
int s, n;
if (!interfacebuf) {
return -2;
}
s = socket(af, SOCK_DGRAM, 0);
if (s < 0) {
free(interfacebuf);
return -1;
}
/* Redo the buffer size increase until we get response size to
be something of LESS THAN the buffer size minus two-times
the sizeof(struct ifreq) -- because then we don't have
a potential case of having larger block of addresses in
system, but us being unable to get them all..
Usually system has TWO interfaces -- loopback, and the LAN,
thus the following loop is executed exactly once! */
/* Some utilities seem to do this probing also with sockets of
AF_X25, AF_IPX, AF_AX25, AF_INET6, etc. -address families,
but on Linux (very least) it can be done with any of them,
thus we use the one that is most likely available: AF_INET */
for (;;) {
/* get the list of known IP address from the kernel */
ifbufsize <<= 1;
interfacebuf = (void*)realloc(interfacebuf, ifbufsize);
memset(&ifc, 0, sizeof(ifc));
ifc.ifc_buf = interfacebuf;
ifc.ifc_len = ifbufsize;
if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
if (errno == EINVAL)
continue;
if (ifc.ifc_len < (ifbufsize - 2*sizeof(struct ifreq)))
break;
/* Redo the query, perhaps didn't get them all.. */
}
/* Count how many addresses listed */
n = -1; /* return code ... */
for (i = 0; i < ifc.ifc_len; ) {
struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
union sockaddr_uni *sa = (union sockaddr_uni *) &ifr->ifr_addr;
#ifdef SIOCGIFFLAGS
struct ifreq ifrf;
#endif
#if defined(SA_LEN)
if (SA_LEN(((struct sockaddr *)sa)) > sizeof(ifr->ifr_addr))
i += sizeof ifr->ifr_name + SA_LEN(((struct sockaddr *)sa));
else
#elif defined(HAVE_SA_LEN)
if (sa->sa.sa_len > sizeof ifr->ifr_addr)
i += sizeof ifr->ifr_name + sa->sa.sa_len;
else
#endif
i += sizeof *ifr;
if (strcasecmp(ifr->ifr_name, ifname) != 0)
continue; /* Not this interface.. */
/* Known address families ?
The one we scanned for ??*/
if (ifr->ifr_addr.sa_family != af)
continue; /* Not desired address family! */
/* Now, what do the flags say ? Are they alive ? */
#ifdef SIOCGIFFLAGS
memset(&ifrf, 0, sizeof(struct ifreq));
strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name));
if (ioctl(s, SIOCGIFFLAGS, (char *) &ifrf) < 0)
continue; /* Failed.. */
if (!(IFF_UP & ifrf.ifr_flags))
continue; /* Not alive */
#else
/* printf("ifr_flags=0x%x\n",ifr->ifr_flags); */
if (!(IFF_UP & ifr->ifr_flags))
continue; /* Not alive */
#endif
if (af == AF_INET) {
struct sockaddr_in *si4 = (void*)sa;
if (si4 == NULL) break;
/* pick the whole sockaddr package! */
memcpy(sap, &ifr->ifr_addr, sizeof(struct sockaddr_in));
n = 0;
break,
}
#if defined(AF_INET6) && defined(INET6)
if (af == AF_INET6) {
struct sockaddr_in6 *si6 = (void*)sa;
if (si6 == NULL) break;
/* pick the whole sockaddr package! */
memcpy(sap, &ifr->ifr_addr, sizeof(struct sockaddr_in6));
n = 0;
break,
}
#endif
break; /* 't was desired address family... */
}
close(s);
free(interfacebuf);
return n;
}
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1