/*++
/* NAME
/* inet_addr_local 3
/* SUMMARY
/* determine if IP address is local
/* SYNOPSIS
/* #include <inet_addr_local.h>
/*
/* int inet_addr_local(addr_list, mask_list, addr_family_list)
/* INET_ADDR_LIST *addr_list;
/* INET_ADDR_LIST *mask_list;
/* unsigned *addr_family;
/* DESCRIPTION
/* inet_addr_local() determines all active IP interface addresses
/* of the local system. Any address found is appended to the
/* specified address list. The result value is the number of
/* active interfaces found.
/*
/* The mask_list is either a null pointer, or it is a list that
/* receives the netmasks of the interface addresses that were found.
/*
/* The addr_family_list specifies one or more of AF_INET or AF_INET6.
/* DIAGNOSTICS
/* Fatal errors: out of memory.
/* SEE ALSO
/* inet_addr_list(3) address list management
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Dean C. Strik
/* Department ICT
/* Eindhoven University of Technology
/* P.O. Box 513
/* 5600 MB Eindhoven, Netherlands
/* E-mail: <dean@ipnet6.org>
/*--*/
/* System library. */
#include <sys_defs.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if .h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#ifdef USE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#include <errno.h>
#include <string.h>
#ifdef HAS_IPV6 /* Linux only? */
#include <netdb.h>
#include <stdio.h>
#endif
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <inet_addr_list.h>
#include <inet_addr_local.h>
#include <myaddrinfo.h>
#include <sock_addr.h>
#include <mask_addr.h>
#include <hex_code.h>
/*
* Postfix needs its own interface address information to determine whether
* or not it is an MX host for some destination ; without this information ,
* mail would loop between MX hosts . Postfix also needs its interface
* addresses to figure out whether or not it is final destination for
* addresses of the form username @ [ ipaddress ] .
*
* Postfix needs its own interface netmask information when no explicit
* mynetworks setting is given in main . cf , and " mynetworks_style = subnet " .
* The mynetworks parameter controls , among others , what mail clients are
* allowed to relay mail through Postfix .
*
* Different systems have different ways to find out this information . We will
* therefore use OS dependent methods . An overview :
*
* - Use getifaddrs ( ) when available . This supports both IPv4 / IPv6 addresses .
* The implementation however is not present in all major operating systems .
*
* - Use SIOCGLIFCONF when available . This supports both IPv4 / IPv6 addresses .
* With SIOCGLIFNETMASK we can obtain the netmask for either address family .
* Again , this is not present in all major operating systems .
*
* - On Linux , glibc ' s getifaddrs ( 3 ) has returned IPv4 information for some
* time , but IPv6 information was not returned until 2 . 3 . 3 . With older Linux
* versions we get IPv4 interface information with SIOCGIFCONF , and read
* IPv6 address / prefix information from a file in the / proc filesystem .
*
* - On other systems we expect SIOCGIFCONF to return IPv6 addresses . Since
* SIOCGIFNETMASK does not work reliably for IPv6 addresses , we always set
* the prefix length to / 128 ( host ) , and expect the user to configure a more
* appropriate mynetworks setting if needed .
*
* XXX : Each lookup method is implemented by its own function , so we duplicate
* some code . In this case , I think this is better than really drowning in
* the # ifdefs . . .
*
* - - Dean Strik ( dcs )
*/
#ifndef HAVE_GETIFADDRS
/* ial_socket - make socket for ioctl() operations */
static int ial_socket(int af)
{
const char *myname = "inet_addr_local[socket]" ;
int sock;
/*
* The host may not be actually configured with IPv6 . When IPv6 support
* is not actually in the kernel , don ' t consider failure to create an
* IPv6 socket as fatal . This could be tuned better though . For other
* families , the error is fatal .
*
* XXX Now that Postfix controls protocol support centrally with the
* inet_proto ( 3 ) module , this workaround should no longer be needed .
*/
if ((sock = socket(af, SOCK_DGRAM, 0 )) < 0 ) {
#ifdef HAS_IPV6
if (af == AF_INET6) {
if (msg_verbose)
msg_warn("%s: socket: %m" , myname);
return (-1 );
}
#endif
msg_fatal("%s: socket: %m" , myname);
}
return (sock);
}
#endif
#ifdef HAVE_GETIFADDRS
/*
* The getifaddrs ( 3 ) function , introduced by BSD / OS , provides a
* platform - independent way of requesting interface addresses ,
* including IPv6 addresses . The implementation however is not
* present in all major operating systems .
*/
/* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[getifaddrs]" ;
struct ifaddrs *ifap, *ifa;
struct sockaddr *sa, *sam;
if (getifaddrs(&ifap) < 0 )
msg_fatal("%s: getifaddrs: %m" , myname);
/*
* Get the address of each IP network interface . According to BIND we
* must include interfaces that are down because the machine may still
* receive packets for that address ( yes , via some other interface ) .
* Having no way to verify this claim on every machine , I will give them
* the benefit of the doubt .
*
* FIX 200501 : The IPv6 patch did not report NetBSD loopback interfaces ;
* fixed by replacing IFF_RUNNING by IFF_UP .
*
* FIX 200501 : The IPV6 patch did not skip wild - card interface addresses
* ( tested on FreeBSD ) .
*/
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0 )
continue ;
sa = ifa->ifa_addr;
if (af != AF_UNSPEC && sa->sa_family != af)
continue ;
sam = ifa->ifa_netmask;
if (sam == 0 ) {
/* XXX In mynetworks, a null netmask would match everyone. */
msg_warn("ignoring interface with null netmask, address family %d" ,
sa->sa_family);
continue ;
}
switch (sa->sa_family) {
case AF_INET:
if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
continue ;
break ;
#ifdef HAS_IPV6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
continue ;
break ;
#endif
default :
continue ;
}
inet_addr_list_append(addr_list, sa);
if (mask_list != 0 ) {
/*
* Unfortunately , sa_len / sa_family may be broken in the netmask
* sockaddr structure . We must fix this manually to have correct
* addresses . - - dcs
*/
#ifdef HAS_SA_LEN
sam->sa_len = sa->sa_family == AF_INET6 ?
sizeof (struct sockaddr_in6) :
sizeof (struct sockaddr_in);
#endif
sam->sa_family = sa->sa_family;
inet_addr_list_append(mask_list, sam);
}
}
freeifaddrs(ifap);
return (0 );
}
#elif defined (HAS_SIOCGLIF) /* HAVE_GETIFADDRS */
/*
* The SIOCLIF * ioctls are the successors of SIOCGIF * on the Solaris
* and HP / UX operating systems . The data is stored in sockaddr_storage
* structure . Both IPv4 and IPv6 addresses are returned though these
* calls .
*/
#define NEXT_INTERFACE(lifr) (lifr + 1 )
#define LIFREQ_SIZE(lifr) sizeof (lifr[0 ])
/* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
static int ial_siocglif(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[siocglif]" ;
struct lifconf lifc;
struct lifreq *lifr;
struct lifreq *lifr_mask;
struct lifreq *the_end;
struct sockaddr *sa;
int sock;
VSTRING *buf;
/*
* See also comments in ial_siocgif ( )
*/
if (af != AF_INET && af != AF_INET6)
msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
"AF_INET6 (%d)" , myname, af, AF_INET, AF_INET6);
sock = ial_socket(af);
if (sock < 0 )
return (0 );
buf = vstring_alloc(1024 );
for (;;) {
memset(&lifc, 0 , sizeof (lifc));
lifc.lifc_family = AF_UNSPEC; /* XXX Why??? */
lifc.lifc_len = vstring_avail(buf);
lifc.lifc_buf = vstring_str(buf);
if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0 ) {
if (errno != EINVAL)
msg_fatal("%s: ioctl SIOCGLIFCONF: %m" , myname);
} else if (lifc.lifc_len < vstring_avail(buf) / 2 )
break ;
VSTRING_SPACE(buf, vstring_avail(buf) * 2 );
}
the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
for (lifr = lifc.lifc_req; lifr < the_end;) {
sa = (struct sockaddr *) &lifr->lifr_addr;
if (sa->sa_family != af) {
lifr = NEXT_INTERFACE(lifr);
continue ;
}
if (af == AF_INET) {
if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
lifr = NEXT_INTERFACE(lifr);
continue ;
}
#ifdef HAS_IPV6
} else if (af == AF_INET6) {
if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
lifr = NEXT_INTERFACE(lifr);
continue ;
}
}
#endif
inet_addr_list_append(addr_list, sa);
if (mask_list) {
lifr_mask = (struct lifreq *) mymalloc(sizeof (struct lifreq));
memcpy((void *) lifr_mask, (void *) lifr, sizeof (struct lifreq));
if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0 )
msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m" , myname);
/* XXX: Check whether sa_len/family are honoured --dcs */
inet_addr_list_append(mask_list,
(struct sockaddr *) &lifr_mask->lifr_addr);
myfree((void *) lifr_mask);
}
lifr = NEXT_INTERFACE(lifr);
}
vstring_free(buf);
(void ) close(sock);
return (0 );
}
#else /* HAVE_SIOCGLIF */
/*
* The classic SIOCGIF * ioctls . Modern BSD operating systems will
* also return IPv6 addresses through these structure . Note however
* that recent versions of these operating systems have getifaddrs .
*/
#if defined (_SIZEOF_ADDR_IFREQ)
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
#elif defined (HAS_SA_LEN)
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + sizeof (ifr->ifr_name) + ifr->ifr_addr.sa_len))
#define IFREQ_SIZE(ifr) (sizeof (ifr->ifr_name) + ifr->ifr_addr.sa_len)
#else
#define NEXT_INTERFACE(ifr) (ifr + 1 )
#define IFREQ_SIZE(ifr) sizeof (ifr[0 ])
#endif
/* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
static int ial_siocgif(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list,
int af)
{
const char *myname = "inet_addr_local[siocgif]" ;
struct in_addr addr;
struct ifconf ifc;
struct ifreq *ifr;
struct ifreq *ifr_mask;
struct ifreq *the_end;
int sock;
VSTRING *buf;
/*
* Get the network interface list . XXX The socket API appears to have no
* function that returns the number of network interfaces , so we have to
* guess how much space is needed to store the result .
*
* On BSD - derived systems , ioctl SIOCGIFCONF returns as much information as
* possible , leaving it up to the application to repeat the request with
* a larger buffer if the result caused a tight fit .
*
* Other systems , such as Solaris 2 . 5 , generate an EINVAL error when the
* buffer is too small for the entire result . Workaround : ignore EINVAL
* errors and repeat the request with a larger buffer . The downside is
* that the program can run out of memory due to a non - memory problem ,
* making it more difficult than necessary to diagnose the real problem .
*/
sock = ial_socket(af);
if (sock < 0 )
return (0 );
buf = vstring_alloc(1024 );
for (;;) {
ifc.ifc_len = vstring_avail(buf);
ifc.ifc_buf = vstring_str(buf);
if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0 ) {
if (errno != EINVAL)
msg_fatal("%s: ioctl SIOCGIFCONF: %m" , myname);
} else if (ifc.ifc_len < vstring_avail(buf) / 2 )
break ;
VSTRING_SPACE(buf, vstring_avail(buf) * 2 );
}
the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < the_end;) {
if (ifr->ifr_addr.sa_family != af) {
ifr = NEXT_INTERFACE(ifr);
continue ;
}
if (af == AF_INET) {
addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
if (addr.s_addr != INADDR_ANY) {
inet_addr_list_append(addr_list, &ifr->ifr_addr);
if (mask_list) {
ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr));
if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0 )
msg_fatal("%s: ioctl SIOCGIFNETMASK: %m" , myname);
/*
* Note that this SIOCGIFNETMASK has truly screwed up the
* contents of sa_len / sa_family . We must fix this
* manually to have correct addresses . - - dcs
*/
ifr_mask->ifr_addr.sa_family = af;
#ifdef HAS_SA_LEN
ifr_mask->ifr_addr.sa_len = sizeof (struct sockaddr_in);
#endif
inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
myfree((void *) ifr_mask);
}
}
}
#ifdef HAS_IPV6
else if (af == AF_INET6) {
struct sockaddr *sa;
sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
inet_addr_list_append(addr_list, sa);
if (mask_list) {
/* XXX Assume /128 for everything */
struct sockaddr_in6 mask6;
mask6 = *SOCK_ADDR_IN6_PTR(sa);
memset((void *) &mask6.sin6_addr, ~0 ,
sizeof (mask6.sin6_addr));
inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
}
}
}
#endif
ifr = NEXT_INTERFACE(ifr);
}
vstring_free(buf);
(void ) close(sock);
return (0 );
}
#endif /* HAVE_SIOCGLIF */
#ifdef HAS_PROCNET_IFINET6
/*
* Older Linux versions lack proper calls to retrieve IPv6 interface
* addresses . Instead , the addresses can be read from a file in the
* / proc tree . The most important issue with this approach however
* is that the / proc tree may not always be available , for example
* in a chrooted environment or in " hardened " ( sic ) installations .
*/
/* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
INET_ADDR_LIST *mask_list)
{
const char *myname = "inet_addr_local[procnet_ifinet6]" ;
FILE *fp;
char buf[BUFSIZ];
unsigned plen;
VSTRING *addrbuf;
struct sockaddr_in6 addr;
struct sockaddr_in6 mask;
/*
* Example : 00000000000000000000000000000001 01 80 10 80 lo
*
* Fields : address , interface index , prefix length , scope value
* ( net / ipv6 . h ) , interface flags ( linux / rtnetlink . h ) , device name .
*
* FIX 200501 The IPv6 patch used fscanf ( ) , which will hang on unexpected
* input . Use fgets ( ) + sscanf ( ) instead .
*/
if ((fp = fopen(_PATH_PROCNET_IFINET6, "r" )) != 0 ) {
addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1 );
memset((void *) &addr, 0 , sizeof (addr));
addr.sin6_family = AF_INET6;
#ifdef HAS_SA_LEN
addr.sin6_len = sizeof (addr);
#endif
mask = addr;
while (fgets(buf, sizeof (buf), fp) != 0 ) {
/* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2 ) == 0
|| sscanf(buf + MAI_V6ADDR_BYTES * 2 , " %*x %x" , &plen) != 1
|| plen > MAI_V6ADDR_BITS) {
msg_warn("unexpected data in %s - skipping IPv6 configuration" ,
_PATH_PROCNET_IFINET6);
break ;
}
/* vstring_str(addrbuf) has worst-case alignment. */
addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
memset((void *) &mask.sin6_addr, ~0 , sizeof (mask.sin6_addr));
mask_addr((unsigned char *) &mask.sin6_addr,
sizeof (mask.sin6_addr), plen);
inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
}
vstring_free(addrbuf);
fclose(fp); /* FIX 200501 */
} else {
msg_warn("can't open %s (%m) - skipping IPv6 configuration" ,
_PATH_PROCNET_IFINET6);
}
return (0 );
}
#endif /* HAS_PROCNET_IFINET6 */
/* inet_addr_local - find all IP addresses for this host */
int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
unsigned *addr_family_list)
{
const char *myname = "inet_addr_local" ;
int initial_count = addr_list->used;
unsigned family;
int count;
while ((family = *addr_family_list++) != 0 ) {
/*
* IP Version 4
*/
if (family == AF_INET) {
count = addr_list->used;
#if defined (HAVE_GETIFADDRS)
ial_getifaddrs(addr_list, mask_list, AF_INET);
#elif defined (HAS_SIOCGLIF)
ial_siocglif(addr_list, mask_list, AF_INET);
#else
ial_siocgif(addr_list, mask_list, AF_INET);
#endif
if (msg_verbose)
msg_info("%s: configured %d IPv4 addresses" ,
myname, addr_list->used - count);
}
/*
* IP Version 6
*/
#ifdef HAS_IPV6
else if (family == AF_INET6) {
count = addr_list->used;
#if defined (HAVE_GETIFADDRS)
ial_getifaddrs(addr_list, mask_list, AF_INET6);
#elif defined (HAS_PROCNET_IFINET6)
ial_procnet_ifinet6(addr_list, mask_list);
#elif defined (HAS_SIOCGLIF)
ial_siocglif(addr_list, mask_list, AF_INET6);
#else
ial_siocgif(addr_list, mask_list, AF_INET6);
#endif
if (msg_verbose)
msg_info("%s: configured %d IPv6 addresses" , myname,
addr_list->used - count);
}
#endif
/*
* Something ' s not right .
*/
else
msg_panic("%s: unknown address family %d" , myname, family);
}
return (addr_list->used - initial_count);
}
#ifdef TEST
#include <string.h>
#include <vstream.h>
#include <msg_vstream.h>
#include <inet_proto.h>
int main(int unused_argc, char **argv)
{
INET_ADDR_LIST addr_list;
INET_ADDR_LIST mask_list;
MAI_HOSTADDR_STR hostaddr;
MAI_HOSTADDR_STR hostmask;
struct sockaddr *sa;
int i;
INET_PROTO_INFO *proto_info;
msg_vstream_init(argv[0 ], VSTREAM_ERR);
msg_verbose = 1 ;
proto_info = inet_proto_init(argv[0 ],
argv[1 ] ? argv[1 ] : INET_PROTO_NAME_ALL);
inet_addr_list_init(&addr_list);
inet_addr_list_init(&mask_list);
inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
if (addr_list.used == 0 )
msg_fatal("cannot find any active network interfaces" );
if (addr_list.used == 1 )
msg_warn("found only one active network interface" );
for (i = 0 ; i < addr_list.used; i++) {
sa = SOCK_ADDR_PTR(addr_list.addrs + i);
SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
&hostaddr, (MAI_SERVPORT_STR *) 0 , 0 );
sa = SOCK_ADDR_PTR(mask_list.addrs + i);
SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
&hostmask, (MAI_SERVPORT_STR *) 0 , 0 );
vstream_printf("%s/%s\n" , hostaddr.buf, hostmask.buf);
vstream_fflush(VSTREAM_OUT);
}
inet_addr_list_free(&addr_list);
inet_addr_list_free(&mask_list);
return (0 );
}
#endif
Messung V0.5 in Prozent C=72 H=94 G=83
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland