import of dnsmasq-2.3.tar.gz

This commit is contained in:
Simon Kelley
2004-03-10 20:04:35 +00:00
parent b49644f39f
commit 44a2a3165c
19 changed files with 1687 additions and 920 deletions

View File

@@ -14,152 +14,89 @@
#include "dnsmasq.h"
static char *add_iface(struct irec **list, unsigned int flags,
char *name, union mysockaddr *addr,
struct iname *names, struct iname *addrs,
struct iname *except)
static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *addr,
struct iname *names, struct iname *addrs,
struct iname *except)
{
struct irec *iface;
int fd, opt;
struct iname *tmp;
/* we may need to check the whitelist */
if (names)
{
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
{
tmp->found = 1;
break;
}
if (!(flags & IFF_LOOPBACK) && !tmp)
/* not on whitelist and not loopback */
return NULL;
}
if (addrs)
{
for (tmp = addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
{
tmp->found = 1;
break;
}
if (!tmp)
/* not on whitelist */
return NULL;
}
/* check blacklist */
if (except)
for (tmp = except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0)
return NULL;
/* we may need to check the whitelist */
if (names || addrs)
{
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
break;
if (!tmp && !addrs)
return NULL;
for (tmp = addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
break;
if (!tmp)
return NULL;
}
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address
and we may be re-scanning. */
for (iface = *list; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
it is possible to have multiple interfaces with the same address */
for (; list; list = list->next)
if (sockaddr_isequal(&list->addr, addr))
break;
if (iface)
{
iface->valid = 1;
return NULL;
}
if ((fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
return "failed to create socket: %s";
/* Set SO_REUSEADDR on the socket, this allows is to bind
specific addresses even if BIND is running and has bound *:53 */
opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(fd, &addr->sa, sa_len(addr)) == -1)
{
int errsave = errno;
close(fd);
errno = errsave;
/* IPv6 interfaces sometimes return ENODEV to bind() for unknown
(to me) reasons. Don't treat that as fatal. */
return errno == ENODEV ? NULL : "failed to bind socket: %s";
}
if (list)
return NULL;
/* If OK, add it to the head of the list */
if (!(iface = malloc(sizeof(struct irec))))
{
close(fd);
return "cannot allocate interface";
}
iface->fd = fd;
iface = safe_malloc(sizeof(struct irec));
iface->addr = *addr;
iface->next = *list;
iface->valid = 1;
*list = iface;
return NULL;
return iface;
}
/* get all interfaces in system and for each one allowed add it to the chain
at interfacep. May be called more that once: interfaces which still exist
are left on the chain, those which have gone have sockets close()ed an are
unlinked. Return value is NULL if OK, an error string and the value of errno
on error. */
char *enumerate_interfaces(struct irec **interfacep,
struct iname *names,
struct iname *addrs,
struct iname *except,
struct dhcp_context *dhcp,
int port)
struct irec *enumerate_interfaces(struct iname *names,
struct iname *addrs,
struct iname *except,
int port)
{
/* this code is adapted from Stevens, page 434. It finally
destroyed my faith in the C/unix API */
int len = 100 * sizeof(struct ifreq);
int errsave, lastlen = 0;
struct irec *iface, *prev;
char *buf, *ptr, *err = NULL;
struct ifconf ifc;
struct irec *iface = NULL, *new;
char *buf, *ptr;
struct ifreq *ifr = NULL;
struct ifconf ifc;
int lastlen = 0;
int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0);
int rawfd = -1;
if (fd == -1)
return "cannot create socket to enumerate interfaces: %s";
/* make all interfaces as old. Any left that way after the scan are reaped. */
for (iface = *interfacep; iface; iface = iface->next)
iface->valid = 0;
die ("cannot create socket to enumerate interfaces: %s", NULL);
while (1)
{
if (!(buf = malloc(len)))
{
err = "cannot allocate buffer";
goto end;
}
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
{
err = "ioctl error while enumerating interfaces: %s";
goto end;
}
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
len += 10*sizeof(struct ifreq);
free(buf);
}
{
buf = safe_malloc(len);
for (ptr = buf; ptr < buf + ifc.ifc_len; )
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
die ("ioctl error while enumerating interfaces: %s", NULL);
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
len += 10*sizeof(struct ifreq);
free(buf);
}
for (ptr = buf; ptr < buf + len; )
{
union mysockaddr addr;
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -168,10 +105,7 @@ char *enumerate_interfaces(struct irec **interfacep,
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
{
err = "cannot allocate buffer";
goto end;
}
die("cannot allocate buffer", NULL);
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
@@ -179,7 +113,7 @@ char *enumerate_interfaces(struct irec **interfacep,
ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
#endif
/* copy address since getting flags overwrites */
if (ifr->ifr_addr.sa_family == AF_INET)
{
@@ -202,14 +136,24 @@ char *enumerate_interfaces(struct irec **interfacep,
continue; /* unknown address family */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
die("ioctl error getting interface flags: %m", NULL);
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (names && (ifr->ifr_flags & IFF_LOOPBACK))
{
err = "ioctl error getting interface flags: %m";
goto end;
struct iname *lo = safe_malloc(sizeof(struct iname));
lo->name = safe_string_alloc(ifr->ifr_name);
lo->next = names->next;
names->next = lo;
}
if ((err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
if ((new = add_iface(iface, ifr->ifr_name,
&addr, names, addrs, except)))
goto end;
{
new->next = iface;
iface = new;
}
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
@@ -252,141 +196,16 @@ char *enumerate_interfaces(struct irec **interfacep,
fclose(f);
}
if (found &&
(err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
&addr6, names, addrs, except)))
goto end;
if (found && (new = add_iface(iface, ifr->ifr_name,
&addr6, names, addrs, except)))
{
new->next = iface;
iface = new;
}
}
#endif /* LINUX */
/* dhcp is non-null only on the first call: set up the relevant
interface-related DHCP stuff here. DHCP is IPv4 only.
Because errors here are ultimately fatal we can return directly and not bother
closing the descriptor.
*/
if (dhcp && addr.sa.sa_family == AF_INET &&
!(ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
{
struct in_addr netmask, broadcast;
struct dhcp_context *context;
int opt = 1;
if (ioctl(fd, SIOCGIFNETMASK, ifr) < 0)
return "ioctl error getting interface netmask: %s";
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) < 0)
return "ioctl error getting interface broadcast address: %s";
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
for (context = dhcp; context; context = context->next)
if (!context->iface && /* may be more than one iface with same addr */
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr)) &&
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr)))
{
struct sockaddr_in saddr;
#ifdef HAVE_BPF
char filename[50];
int b = 0;
while (1)
{
sprintf(filename, "/dev/bpf%d", b);
if ((rawfd = open(filename, O_RDWR, 0)) == -1)
{
if (errno != EBUSY)
return"Cannot create DHCP BPF socket: %s";
b++;
}
else if (ioctl(rawfd, BIOCSETIF, ifr) < 0)
return "Can't attach interface to BPF device: %s";
else
break;
}
if (context->next)
return "no support for DHCP on multiple networks under this OS";
#endif
#ifdef HAVE_PF_PACKET
if (rawfd == -1 && /* same packet socket for all interfaces */
(rawfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
return "Cannot create DHCP packet socket: %s";
/* do this last so that the index is still in ifr for the
call to setsockopt(SO_BINDTODEVICE) */
if (ioctl(fd, SIOCGIFINDEX, ifr) < 0)
return "ioctl error getting interface index: %m";
context->ifindex = ifr->ifr_ifindex;
#endif
context->rawfd = rawfd;
context->serv_addr = addr.in.sin_addr;
context->netmask = netmask;
context->broadcast = broadcast;
if (!(context->iface = malloc(strlen(ifr->ifr_name) + 1)))
return "cannot allocate interface name";
strcpy(context->iface, ifr->ifr_name);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
if ((context->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return "cannot create DHCP server socket: %s";
if (setsockopt(context->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
#ifdef HAVE_PF_PACKET
setsockopt(context->fd, SOL_SOCKET, SO_BINDTODEVICE, ifr, sizeof(*ifr)) == -1 ||
#endif
setsockopt(context->fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
return "failed to set options on DHCP socket: %s";
if (bind(context->fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
return "failed to bind DHCP server socket: %s";
}
}
}
#ifdef HAVE_BPF
/* now go through the interfaces again, looking for AF_LINK records
to get hardware addresses from */
for (ptr = buf; ptr < buf + ifc.ifc_len; )
{
struct dhcp_context *context;
#ifdef HAVE_SOCKADDR_SA_LEN
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
{
err = "cannot allocate buffer";
goto end;
}
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
#else
ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
#endif
if (ifr->ifr_addr.sa_family == AF_LINK)
for (context = dhcp; context; context = context->next)
if (context->iface && strcmp(context->iface, ifr->ifr_name) == 0)
memcpy(context->hwaddr, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
}
#endif
end:
errsave = errno; /* since errno gets overwritten by close */
if (buf)
free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -394,37 +213,91 @@ char *enumerate_interfaces(struct irec **interfacep,
free(ifr);
#endif
close(fd);
if (err)
{
errno = errsave;
return err;
return iface;
}
struct listener *create_wildcard_listeners(int port)
{
union mysockaddr addr;
int opt = 1;
struct listener *listen;
#ifdef HAVE_IPV6
int fd;
#endif
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
listen = safe_malloc(sizeof(struct listener));
if ((listen->fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
die("failed to create socket: %s", NULL);
if (setsockopt(listen->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
#if defined(IP_PKTINFO)
setsockopt(listen->fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(listen->fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(listen->fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(listen->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
die("failed to bind socket: %s", NULL);
listen->next = NULL;
listen->family = AF_INET;
#ifdef HAVE_IPV6
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = htonl(0);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
{
if (errno != EPROTONOSUPPORT &&
errno != EAFNOSUPPORT &&
errno != EINVAL)
die("failed to create IPv6 socket: %s", NULL);
}
else
{
listen->next = safe_malloc(sizeof(struct listener));
listen->next->fd = fd;
listen->next->family = AF_INET6;
listen->next->next = NULL;
if (setsockopt(listen->next->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(listen->next->fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
bind(listen->next->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
die("failed to bind IPv6 socket: %s", NULL);
}
#endif
return listen;
}
struct listener *create_bound_listeners(struct irec *interfaces)
{
struct listener *listeners = NULL;
struct irec *iface;
int opt = 1;
for (iface = interfaces ;iface; iface = iface->next)
{
struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
new->next = listeners;
listeners = new;
if ((new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1)
die("failed to create socket: %s", NULL);
if (setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
die("failed to bind socket: %s", NULL);
}
/* now remove interfaces which were not found on this scan */
for(prev = NULL, iface = *interfacep; iface; )
{
if (iface->valid)
{
prev = iface;
iface = iface->next;
}
else
{
struct irec *tmp = iface;
close(iface->fd);
/* remove pending queries from this interface */
reap_forward(iface->fd);
/* unlink */
if (prev)
prev->next = iface->next;
else
*interfacep = iface->next;
iface = iface->next;
free(tmp);
}
}
return NULL; /* no error */
return listeners;
}
static struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
@@ -607,7 +480,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0);
source_addr.in6.sin6_addr= in6addr_any;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(query_port);
}
#endif /* IPV6 */