-/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
+/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "dnsmasq.h"
-static struct irec *add_iface(struct daemon *daemon, struct irec *list,
- char *name, int is_loopback, union mysockaddr *addr)
+static int iface_allowed(struct daemon *daemon, struct irec *iface,
+ char *name, int is_loopback, union mysockaddr *addr)
{
- struct irec *iface;
struct iname *tmp;
/* If we are restricting the set of interfaces to use, make
if (daemon->if_except)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0)
- {
- /* record address of named interfaces, for TCP access control */
- tmp->addr = *addr;
- return list;
- }
-
+ return 0;
+
/* we may need to check the whitelist */
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
- {
- tmp->addr = *addr;
- found = tmp->used = 1;
- }
-
+ found = tmp->used = 1;
+
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
found = tmp->used = 1;
if (!found)
- return list;
+ return 0;
}
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address */
- for (iface = list; iface; iface = iface->next)
+ for (; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
break;
if (iface)
- return list;
+ return 0;
- /* If OK, add it to the head of the list */
- iface = safe_malloc(sizeof(struct irec));
- iface->addr = *addr;
- iface->next = list;
- return iface;
+ return 1;
}
+/* This does two different jobs: if chainp is non-NULL, it puts
+ a list of all the interfaces allowed by config into *chainp.
+ If chainp is NULL, it returns 1 if addr is an address of an interface
+ allowed by config and if that address is IPv4, it fills in the
+ netmask of the interface.
+
+ If chainp is non-NULL, a zero return indicates a fatal error.
-struct irec *enumerate_interfaces(struct daemon *daemon)
+ If chainp is NULL, errors result in a match failure and zero return.
+*/
+int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
+ union mysockaddr *test_addrp, struct in_addr *netmaskp)
{
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
FILE *f;
int lastlen = 0;
int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0);
+ struct in_addr netmask;
+ int ret = 0;
if (fd == -1)
- die ("cannot create socket to enumerate interfaces: %s", NULL);
+ return 0;
+
+#ifdef HAVE_IPV6
+ if (test_addrp && test_addrp->sa.sa_family == AF_INET6)
+ test_addrp->in6.sin6_flowinfo = htonl(0);
+#endif
while (1)
{
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
- die ("ioctl error while enumerating interfaces: %s", NULL);
+ goto exit;
}
else
{
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
- die("cannot allocate buffer", NULL);
+ goto exit;
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
{
addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
addr.in.sin_port = htons(daemon->port);
+ if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
+ goto exit;
+ netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
}
#ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6)
continue; /* unknown address family */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
- die("ioctl error getting interface flags: %m", NULL);
+ goto exit;
- iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr);
+ if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
+ {
+ if (chainp)
+ {
+ struct irec *new = safe_malloc(sizeof(struct irec));
+ new->addr = addr;
+ new->netmask = netmask;
+ new->next = iface;
+ iface = new;
+ }
+ else if (sockaddr_isequal(&addr, test_addrp))
+ {
+ *netmaskp = netmask;
+ ret = 1;
+ goto exit;
+ }
+ }
}
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
- die("ioctl error getting interface flags: %m", NULL);
- iface = add_iface(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr);
+ goto exit;
+
+ if (iface_allowed(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr))
+ {
+ if (chainp)
+ {
+ struct irec *new = safe_malloc(sizeof(struct irec));
+ new->addr = addr;
+ new->next = iface;
+ iface = new;
+ }
+ else if (sockaddr_isequal(&addr, test_addrp))
+ {
+ ret = 1;
+ goto exit;
+ }
+ }
}
fclose(f);
}
#endif /* LINUX */
-
+
+ if (chainp)
+ {
+ *chainp = iface;
+ ret = 1;
+ }
+
+ exit:
if (buf)
free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN
#endif
close(fd);
- return iface;
+ return ret;
}
#ifdef HAVE_IPV6
{
struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
+ new->iface = iface;
new->next = listeners;
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||