#include "mDNSUNP.h"
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#ifndef SIOCGIFCONF
#include <sys/sockio.h>
#endif
#ifdef IP_RECVIF
#include <net/if_dl.h>
#endif
struct ifi_info *get_ifi_info(int family, int doaliases)
{
int junk;
struct ifi_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags;
char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
#if defined(AF_INET6) && defined(HAVE_IPV6)
struct sockaddr_in6 *sinptr6;
#endif
sockfd = -1;
buf = NULL;
ifihead = NULL;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
goto gotError;
}
lastlen = 0;
len = 100 * sizeof(struct ifreq);
for ( ; ; ) {
buf = malloc(len);
if (buf == NULL) {
goto gotError;
}
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0) {
goto gotError;
}
} else {
if (ifc.ifc_len == lastlen)
break;
lastlen = ifc.ifc_len;
}
len += 10 * sizeof(struct ifreq);
free(buf);
}
ifihead = NULL;
ifipnext = &ifihead;
lastname[0] = 0;
for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
ifr = (struct ifreq *) ptr;
len = GET_SA_LEN(ifr->ifr_addr);
ptr += sizeof(ifr->ifr_name) + len;
if (ifr->ifr_addr.sa_family != family)
continue;
myflags = 0;
if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
*cptr = 0;
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
if (doaliases == 0)
continue;
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) {
goto gotError;
}
flags = ifrcopy.ifr_flags;
if ((flags & IFF_UP) == 0)
continue;
ifi = calloc(1, sizeof(struct ifi_info));
if (ifi == NULL) {
goto gotError;
}
*ifipnext = ifi;
ifipnext = &ifi->ifi_next;
ifi->ifi_flags = flags;
ifi->ifi_myflags = myflags;
ifi->ifi_index = if_nametoindex(ifr->ifr_name);
memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
ifi->ifi_name[IFI_NAME-1] = '\0';
switch (ifr->ifr_addr.sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
if (ifi->ifi_addr == NULL) {
ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
if (ifi->ifi_addr == NULL) {
goto gotError;
}
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST) {
if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
goto gotError;
}
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
if (ifi->ifi_brdaddr == NULL) {
goto gotError;
}
memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
#ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT) {
if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) {
goto gotError;
}
sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
if (ifi->ifi_dstaddr == NULL) {
goto gotError;
}
memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
}
break;
#if defined(AF_INET6) && defined(HAVE_IPV6)
case AF_INET6:
sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr;
if (ifi->ifi_addr == NULL) {
ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
if (ifi->ifi_addr == NULL) {
goto gotError;
}
if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr))
sinptr6->sin6_addr.__u6_addr.__u6_addr16[1] = 0;
memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
}
break;
#endif
default:
break;
}
}
goto done;
gotError:
if (ifihead != NULL) {
free_ifi_info(ifihead);
ifihead = NULL;
}
done:
if (buf != NULL) {
free(buf);
}
if (sockfd != -1) {
junk = close(sockfd);
assert(junk == 0);
}
return(ifihead);
}
void
free_ifi_info(struct ifi_info *ifihead)
{
struct ifi_info *ifi, *ifinext;
for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
if (ifi->ifi_addr != NULL)
free(ifi->ifi_addr);
if (ifi->ifi_brdaddr != NULL)
free(ifi->ifi_brdaddr);
if (ifi->ifi_dstaddr != NULL)
free(ifi->ifi_dstaddr);
ifinext = ifi->ifi_next;
free(ifi);
}
}
ssize_t
recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
#ifdef CMSG_FIRSTHDR
struct cmsghdr *cmptr;
union {
struct cmsghdr cm;
char control[1024];
} control_un;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_flags = 0;
#else
memset(&msg, 0, sizeof(msg));
#endif
msg.msg_name = (void *) sa;
msg.msg_namelen = *salenptr;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
return(n);
*salenptr = msg.msg_namelen;
if (pktp) {
memset(pktp, 0, sizeof(struct my_in_pktinfo));
pktp->ipi_ifindex = -1;
}
#ifndef CMSG_FIRSTHDR
#warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
*flagsp = 0;
return(n);
#else
*flagsp = msg.msg_flags;
if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
(msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
return(n);
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
cmptr = CMSG_NXTHDR(&msg, cmptr)) {
#ifdef IP_PKTINFO
#if in_pktinfo_definition_is_missing
struct in_pktinfo
{
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};
#endif
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *tmp;
struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
sin->sin_family = AF_INET;
sin->sin_addr = tmp->ipi_addr;
sin->sin_port = 0;
pktp->ipi_ifindex = tmp->ipi_ifindex;
continue;
}
#endif
#ifdef IP_RECVDSTADDR
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVDSTADDR) {
struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
sin->sin_family = AF_INET;
sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
sin->sin_port = 0;
continue;
}
#endif
#ifdef IP_RECVIF
if (cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVIF) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
pktp->ipi_ifindex = sdl->sdl_index;
#ifndef HAVE_BROKEN_RECVIF_NAME
strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
#endif
assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
continue;
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IPV6)
if (cmptr->cmsg_level == IPPROTO_IPV6 &&
cmptr->cmsg_type == IPV6_PKTINFO) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
sin6->sin6_family = AF_INET6;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_addr = ip6_info->ipi6_addr;
sin6->sin6_flowinfo = 0;
sin6->sin6_scope_id = 0;
sin6->sin6_port = 0;
pktp->ipi_ifindex = ip6_info->ipi6_ifindex;
continue;
}
#endif
assert(0); }
return(n);
#endif
}