import of dnsmasq-2.53.tar.gz
diff --git a/src/dhcp.c b/src/dhcp.c
index dcc57b1..8c300cf 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -127,7 +127,10 @@
int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm;
-
+#ifdef HAVE_LINUX_NETWORK
+ struct arpreq arp_req;
+#endif
+
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
@@ -189,8 +192,13 @@
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
- iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
- if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
+ union {
+ unsigned char *c;
+ struct in_pktinfo *p;
+ } p;
+ p.c = CMSG_DATA(cmptr);
+ iface_index = p.p->ipi_ifindex;
+ if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
@@ -198,20 +206,37 @@
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
- iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
-
+ {
+ union {
+ unsigned char *c;
+ struct sockaddr_dl *s;
+ } p;
+ p.c = CMSG_DATA(cmptr);
+ iface_index = p.s->sdl_index;
+ }
#elif defined(HAVE_SOLARIS_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
- iface_index = *((unsigned int *)CMSG_DATA(cmptr));
-
+ {
+ union {
+ unsigned char *c;
+ unsigned int *i;
+ } p;
+ p.c = CMSG_DATA(cmptr);
+ iface_index = *(p.i);
+ }
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
return;
+#ifdef HAVE_LINUX_NETWORK
+ /* ARP fiddling uses original interface even if we pretend to use a different one. */
+ strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
+#endif
+
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST))
@@ -231,18 +256,14 @@
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
-
- /* interface may have been changed by alias in iface_check */
- if (!addrp)
- {
- if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
- {
- my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
- return;
- }
- else
- iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
- }
+
+ /* weird libvirt-inspired access control */
+ for (context = daemon->dhcp; context; context = context->next)
+ if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
+ break;
+
+ if (!context)
+ return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
@@ -253,6 +274,27 @@
parm.current = NULL;
parm.ind = iface_index;
+ /* interface may have been changed by alias in iface_check, make sure it gets priority in case
+ there is more than one address on the interface in the same subnet */
+ if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
+ {
+ my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
+ return;
+ }
+ else
+ {
+ iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+ if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
+ {
+ struct in_addr netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+ if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
+ {
+ struct in_addr broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+ complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
+ }
+ }
+ }
+
if (!iface_enumerate(&parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
@@ -324,15 +366,14 @@
{
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
- struct arpreq req;
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
- *((struct sockaddr_in *)&req.arp_pa) = dest;
- req.arp_ha.sa_family = mess->htype;
- memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
- strncpy(req.arp_dev, ifr.ifr_name, 16);
- req.arp_flags = ATF_COM;
- ioctl(daemon->dhcpfd, SIOCSARP, &req);
+ memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
+ arp_req.arp_ha.sa_family = mess->htype;
+ memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+ /* interface name already copied in */
+ arp_req.arp_flags = ATF_COM;
+ ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
}
#elif defined(HAVE_SOLARIS_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -491,13 +532,15 @@
if (!(tmp = address_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
- if (is_same_net(taddr, tmp->start, tmp->netmask) &&
+ if (match_netid(tmp->filter, netids, 1) &&
+ is_same_net(taddr, tmp->start, tmp->netmask) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
- if (is_same_net(taddr, tmp->start, tmp->netmask))
+ if (match_netid(tmp->filter, netids, 1) &&
+ is_same_net(taddr, tmp->start, tmp->netmask))
break;
}
@@ -530,7 +573,8 @@
for (; check; check = check->next)
{
- if (check->net[0] != '#')
+ /* '#' for not is for backwards compat. */
+ if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
@@ -546,6 +590,22 @@
return 1;
}
+struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
+{
+ struct tag_if *exprs;
+ struct dhcp_netid_list *list;
+
+ for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
+ if (match_netid(exprs->tag, tags, 1))
+ for (list = exprs->set; list; list = list->next)
+ {
+ list->list->next = tags;
+ tags = list->list;
+ }
+
+ return tags;
+}
+
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now)
@@ -559,9 +619,10 @@
int i, pass;
unsigned int j;
- /* hash hwaddr */
+ /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
+ dispersal even with similarly-valued "strings". */
for (j = 0, i = 0; i < hw_len; i++)
- j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
+ j += hwaddr[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
@@ -977,29 +1038,40 @@
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and
it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
- so check here that the domain name is legal as a hostname. */
+ so check here that the domain name is legal as a hostname.
+ NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
char *host_from_dns(struct in_addr addr)
{
struct crec *lookup;
- char *hostname = NULL;
- char *d1, *d2;
if (daemon->port == 0)
return NULL; /* DNS disabled. */
lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
+
if (lookup && (lookup->flags & F_HOSTS))
{
- hostname = daemon->dhcp_buff;
- strncpy(hostname, cache_get_name(lookup), 256);
- hostname[255] = 0;
- d1 = strip_hostname(hostname);
- d2 = get_domain(addr);
- if (!legal_hostname(hostname) || (d1 && (!d2 || !hostname_isequal(d1, d2))))
- hostname = NULL;
+ char *dot, *hostname = cache_get_name(lookup);
+ dot = strchr(hostname, '.');
+
+ if (dot && strlen(dot+1) != 0)
+ {
+ char *d2 = get_domain(addr);
+ if (!d2 || !hostname_isequal(dot+1, d2))
+ return NULL; /* wrong domain */
+ }
+
+ if (!legal_hostname(hostname))
+ return NULL;
+
+ strncpy(daemon->dhcp_buff, hostname, 256);
+ daemon->dhcp_buff[255] = 0;
+ strip_hostname(daemon->dhcp_buff);
+
+ return daemon->dhcp_buff;
}
- return hostname;
+ return NULL;
}
/* return domain or NULL if none. */