/*++ /* NAME /* inet_addr_local 3 /* SUMMARY /* determine if IP address is local /* SYNOPSIS /* #include /* /* int inet_addr_local(addr_list, mask_list) /* INET_ADDR_LIST *addr_list; /* INET_ADDR_LIST *mask_list; /* 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. /* 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 /*--*/ /* System library. */ #include #include #include #include #include #include #include #include #ifdef USE_SYS_SOCKIO_H #include #endif #include #include /* Utility library. */ #include #include #include #include #include /* * Support for variable-length addresses. */ #ifdef _SIZEOF_ADDR_IFREQ #define NEXT_INTERFACE(ifr) ((struct ifreq *) \ ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr))) #define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr) #else #ifdef 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 #endif /* inet_addr_local - find all IP addresses for this host */ int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list) { char *myname = "inet_addr_local"; struct ifconf ifc; struct ifreq *ifr; struct ifreq *the_end; int sock; VSTRING *buf = vstring_alloc(1024); int initial_count = addr_list->used; struct in_addr addr; struct ifreq *ifr_mask; if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) msg_fatal("%s: socket: %m", myname); /* * 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. */ 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); } /* * 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. */ 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_INET) { /* IP interface */ addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr; if (addr.s_addr != INADDR_ANY) { /* has IP address */ inet_addr_list_append(addr_list, &addr); if (mask_list) { ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr)); memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr)); if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0) msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname); addr = ((struct sockaddr_in *) & ifr_mask->ifr_addr)->sin_addr; inet_addr_list_append(mask_list, &addr); myfree((char *) ifr_mask); } } } ifr = NEXT_INTERFACE(ifr); } vstring_free(buf); (void) close(sock); return (addr_list->used - initial_count); } #ifdef TEST #include #include int main(int unused_argc, char **argv) { INET_ADDR_LIST addr_list; INET_ADDR_LIST mask_list; int i; msg_vstream_init(argv[0], VSTREAM_ERR); inet_addr_list_init(&addr_list); inet_addr_list_init(&mask_list); inet_addr_local(&addr_list, &mask_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++) { vstream_printf("%s/", inet_ntoa(addr_list.addrs[i])); vstream_printf("%s\n", inet_ntoa(mask_list.addrs[i])); } vstream_fflush(VSTREAM_OUT); inet_addr_list_free(&addr_list); inet_addr_list_free(&mask_list); } #endif