import of dnsmasq-2.17.tar.gz
diff --git a/CHANGELOG b/CHANGELOG
index 26cfcf6..2519ee9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1253,3 +1253,44 @@
Added dynamic-dnsmasq from Peter Willis to the contrib
section.
+version 2.17
+ Correctly deduce the size of numeric dhcp-options, rather
+ than making wild guesses. Also cope with negative values.
+
+ Fixed use of C library reserved symbol "index" which broke
+ under certain combinations of library and compiler.
+
+ Make bind-interfaces work for IPv6 interfaces too.
+
+ Warn if an interface is given for listening which doesn't
+ currently exist when not in bind-interfaces mode. (This is
+ already a fatal error when bind-interfaces is set.)
+
+ Allow the --interface and --except-interface options to
+ take a comma-seperated list of interfaces.
+
+ Tweak --dhcp-userclass matching code to work with the
+ ISC dhclient which violates RFC3004 unless its
+ configuration is very warped. Thanks to Cedric Duval for
+ the bug report.
+
+ Allow more than one network-id tag in a dhcp-option. All
+ the tags must match to enable the option.
+
+ Added dhcp-ignore option to disable classes of hosts based
+ on network-id tags. Also allow BOOTP options to be
+ controlled by network tags.
+
+ Fill in sname, file and siaddr fields in replies to
+ DHCPINFORM messages.
+
+ Don't send NAK replies to DHCPREQUEST packets for disabled
+ clients. Credit to Cedric Duval for spotting this.
+
+ Fix rare crash associated with long DNS names and CNAME
+ records. Thanks to Holger_Hoffstatte and especially Steve
+ Grecni for help chasing that one down.
+
+
+
+
diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec
index 3652a24..0cc9ba8 100644
--- a/dnsmasq-rh.spec
+++ b/dnsmasq-rh.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.16
+Version: 2.17
Release: 1
Copyright: GPL
Group: System Environment/Daemons
diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec
index c1ca8a0..b253a5b 100644
--- a/dnsmasq-suse.spec
+++ b/dnsmasq-suse.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.16
+Version: 2.17
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
diff --git a/dnsmasq.8 b/dnsmasq.8
index b2bec2e..e80fd37 100644
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -366,7 +366,7 @@
.B --dhcp-host
options containing the same information.
.TP
-.B \-O, --dhcp-option=[network-id,]<opt>,[<value>[,<value>]]
+.B \-O, --dhcp-option=[<network-id>,[<network-id>,]]<opt>,[<value>[,<value>]]
Specfify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and
@@ -382,12 +382,10 @@
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma seperated
dotted-quad IP addresses, a decimal number, colon-seperated hex digits
-and a text string. If the optional network-id is given then
-this option is only sent to machines on the network whose dhcp-range
-contains a matching network-id.
+and a text string. If the optional network-ids are given then
+this option is only sent when all the network-ids are matched.
Be careful: no checking is done that the correct type of data for the
-option number is sent, and there are option numbers for which it is not
-possible to generate the correct data type; it is quite possible to
+option number is sent, it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
of this flag.
.TP
@@ -412,10 +410,17 @@
this to set a different printer server for hosts in the class
"accounts" than for hosts in the class "engineering".
.TP
-.B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]]
+.B \ -J, --dhcp-ignore=<network-id>[,<network-id>]
+When all the given network-ids match the set of network-ids derived
+from the net, host, vendor and user classes, ignore the host and do
+not allocate it a DHCP lease.
+.TP
+.B \-M, --dhcp-boot=[net:<network-id>,]<filename>,[<servername>[,<server address>]]
Set BOOTP options to be returned by the DHCP server. These are needed
for machines which network boot, and tell the machine where to collect
-its initial configuration.
+its initial configuration. If the optional network-id(s) are given,
+they must match for this configuration to be sent. Note that
+network-ids are prefixed by "net:" to distinguish them.
.TP
.B \-X, --dhcp-lease-max=<number>
Limits dnsmasq to the specified maximum number of DHCP leases. The
@@ -540,6 +545,21 @@
option. This second technique allows for dynamic update of the server
addresses by PPP or DHCP.
.PP
+The network-id system works as follows: For each DHCP request, dnsmasq
+collects a set of valid network-id tags, one from the
+.B dhcp-range
+used to allocate the address, one from any matching
+.B dhcp-host
+and possibly many from matching vendor classes and user
+classes sent by the DHCP client. Any
+.B dhcp-option
+which has network-id tags will be used in preference to an untagged
+.B dhcp-option,
+provided that _all_ the tags match somewhere in the
+set collected as described above. The prefix '#' on a tag means 'not'
+so --dhcp=option=#purple,3,1.2.3.4 sends the option when the
+network-id tag purple is not in the set of valid tags.
+.PP
The DHCP server in dnsmasq will function as a BOOTP server also,
provided that the MAC address and IP address for clients are given,
either using
diff --git a/doc.html b/doc.html
index 8416b84..c8fbd22 100644
--- a/doc.html
+++ b/doc.html
@@ -23,7 +23,8 @@
Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse,
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, CoyoteLinux and
-Clarkconnect. It is also available as a FreeBSD port and is used in Linksys wireless routers.
+Clarkconnect. It is also available as a FreeBSD port and is used in
+Linksys wireless routers and the m0n0wall project.
<P>
Dnsmasq provides the following features:
<DIR>
@@ -41,22 +42,18 @@
be addressed without having to maintain /etc/hosts on each machine.
</LI>
<LI>
-Dnsmasq will serve names from the DHCP leases file on the firewall machine:
-If machines specify a hostname when they take out a DHCP lease, then they are
-addressable in the local DNS. <B>UPDATE</B> Dnsmasq version 2 now offers an integrated DHCP server
-instead of the lease file reader. This gives better control of the
-interaction with new functions (for example fixed IP leasess and
-attaching names to ethernet addresses centrally) it's also much
-smaller than dnsmasq and ISC dhcpd which is important for router distros.
+The integrated DHCP server supports static and dynamic DHCP leases and
+multiple networks and IP ranges. It works across BOOTP relays and
+supports DHCP options including RFC3397 DNS search lists.
+Machines which are configured by DHCP have their names automatically
+included in the DNS and the names can specified by each machine or
+centrally by associating a name with a MAC address in the dnsmasq
+config file.
</LI>
<LI>
Dnsmasq caches internet addresses (A records and AAAA records) and address-to-name
mappings (PTR records), reducing the load on upstream servers and
-improving performance (especially on modem connections). From version
-0.95 the cache honours time-to-live information and removes old
-records as they expire. From version 0.996 dnsmasq does negative
-caching. From version 1.2 dnsmasq supports IPv6 addresses, both
-in its cache and in /etc/hosts.
+improving performance (especially on modem connections).
</LI>
<LI>
Dnsmasq can be configured to automatically pick up the addresses of
@@ -76,14 +73,8 @@
with private DNS systems easy.
</LI>
<LI>
-Dnsmasq can be configured to return an MX record
-for the firewall host. This makes it easy to configure the mailer on the local
-machines to forward all mail to the central mailer on the firewall host. Never
-lose root messages from your machines again!
-</LI>
-<LI>
-For version 1.15 dnsmasq has a facility to work around Verisign's infamous wildcard A record
-in the .com and .net TLDs
+Dnsmasq supports MX records and can be configured to return MX records
+for any or all local machines.
</LI>
</DIR>
@@ -115,12 +106,19 @@
Ulrich Ivens has a nice HOWTO in German on installing dnsmasq at <A
HREF="http://howto.linux-hardware-shop.de/dnsmasq.html">http://howto.linux-hardware-shop.de/dnsmasq.html</A>
and Damien Raude-Morvan has one in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
+There is a good article about dnsmasq at <A
+HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details.
<H2>Contact.</H2>
-Dnsmasq was written by Simon Kelley. You can contact me at <A HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>. Bugreports, patches, and suggestions for improvements gratefully accepted.
+There is a dnsmasq mailing list at <A
+HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
+http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
+first location for queries, bugreports, suggestions etc.
+Dnsmasq was written by Simon Kelley. You can contact me at <A
+HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
</BODY>
diff --git a/src/cache.c b/src/cache.c
index 64f24af..a49016a 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -17,7 +17,7 @@
static int cache_inserted, cache_live_freed, insert_error;
static union bigname *big_free;
static int bignames_left, log_queries, cache_size, hash_size;
-static int index;
+static int uid;
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
@@ -36,7 +36,7 @@
cache_size = size;
big_free = NULL;
bignames_left = size/10;
- index = 0;
+ uid = 0;
cache_inserted = cache_live_freed = 0;
@@ -48,7 +48,7 @@
{
cache_link(crecp);
crecp->flags = 0;
- crecp->uid = index++;
+ crecp->uid = uid++;
}
}
@@ -85,7 +85,7 @@
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
- crecp->uid = index++; /* invalidate CNAMES pointing to this. */
+ crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
if (cache_tail)
cache_tail->next = crecp;
@@ -673,7 +673,7 @@
if (!host_name)
return;
- if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4)))
+ if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4 | F_CNAME)))
{
if (crec->flags & F_HOSTS)
{
@@ -681,7 +681,7 @@
{
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
syslog(LOG_WARNING,
- "not giving name %s to the DHCP lease of %s because"
+ "not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s",
host_name, inet_ntoa(*host_address),
record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
@@ -689,7 +689,7 @@
return;
}
else if (!(crec->flags & F_DHCP))
- cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
+ cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
}
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
diff --git a/src/config.h b/src/config.h
index 4f12daa..f916551 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
-#define VERSION "2.16"
+#define VERSION "2.17"
#define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */
diff --git a/src/dhcp.c b/src/dhcp.c
index 124ca53..f3a75e9 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -47,6 +47,8 @@
daemon->dhcpfd = fd;
if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
+ (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
die("cannot create ICMP raw socket: %s.", NULL);
@@ -73,8 +75,6 @@
socket receive buffer size to one to avoid that. (zero is
rejected as non-sensical by some BSD kernels) */
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
- (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
- fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
die("cannot create DHCP packet socket: %s. "
"Is CONFIG_PACKET enabled in your kernel?", NULL);
@@ -358,8 +358,7 @@
iov[0].iov_len = sizeof(struct ether_header);
iov[1].iov_base = (char *)rawpacket;
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
- while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 &&
- errno == EINTR);
+ while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
#else
struct sockaddr_ll dest;
@@ -370,7 +369,7 @@
memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
- errno == EINTR);
+ retry_send());
#endif
}
}
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index cec4661..cf28a42 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -30,6 +30,7 @@
struct irec *interfaces;
struct sigaction sigact;
sigset_t sigmask;
+ struct iname *if_tmp;
sighup = 1; /* init cache the first time through */
sigusr1 = 0; /* but don't dump */
@@ -92,7 +93,6 @@
if (daemon->options & OPT_NOWILD)
{
- struct iname *if_tmp;
daemon->listeners = create_bound_listeners(interfaces, daemon->port);
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
@@ -263,6 +263,11 @@
if (bind_fallback)
syslog(LOG_WARNING, "setting --bind-interfaces option because of OS limitations");
+ if (!(daemon->options & OPT_NOWILD))
+ for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+ if (if_tmp->name && !if_tmp->used)
+ syslog(LOG_WARNING, "warning: interface %s does not currently exist", if_tmp->name);
+
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
@@ -288,13 +293,12 @@
"DHCP, IP range %s -- %s, lease time %s",
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
}
+
+#ifdef HAVE_BROKEN_RTC
+ syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
+#endif
}
-#ifdef HAVE_BROKEN_RTC
- if (daemon->dhcp)
- syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
-#endif
-
if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
syslog(LOG_WARNING, "running as root");
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 5464684..e96e139 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -269,6 +269,10 @@
struct dhcp_netid *next;
};
+struct dhcp_netid_list {
+ struct dhcp_netid *list;
+ struct dhcp_netid_list *next;
+};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
@@ -293,12 +297,19 @@
struct dhcp_opt {
int opt, len, is_addr;
unsigned char *val;
- char *netid;
+ struct dhcp_netid *netid;
struct dhcp_opt *next;
};
+struct dhcp_boot {
+ char *file, *sname;
+ struct in_addr next_server;
+ struct dhcp_netid *netid;
+ struct dhcp_boot *next;
+};
+
struct dhcp_vendor {
- int len, is_vendor, used;
+ int len, is_vendor;
char *data;
struct dhcp_netid netid;
struct dhcp_vendor *next;
@@ -361,9 +372,8 @@
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts;
struct dhcp_vendor *dhcp_vendors;
- char *dhcp_file;
- char *dhcp_sname;
- struct in_addr dhcp_next_server;
+ struct dhcp_boot *boot_config;
+ struct dhcp_netid_list *dhcp_ignore;
int dhcp_max;
unsigned int min_leasetime;
struct doctor *doctors;
diff --git a/src/forward.c b/src/forward.c
index 799c01b..9332bb0 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -65,45 +65,43 @@
msg.msg_iov = iov;
msg.msg_iovlen = 1;
- if (!nowild && to->sa.sa_family == AF_INET)
+ if (!nowild)
{
+ struct cmsghdr *cmptr;
msg.msg_control = &control_u;
msg.msg_controllen = sizeof(control_u);
- {
- struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
-#if defined(IP_PKTINFO)
- struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
- pkt->ipi_ifindex = 0;
- pkt->ipi_spec_dst = source->addr.addr4;
- msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
- cmptr->cmsg_level = SOL_IP;
- cmptr->cmsg_type = IP_PKTINFO;
-#elif defined(IP_SENDSRCADDR)
- struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
- *a = source->addr.addr4;
- msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
- cmptr->cmsg_level = IPPROTO_IP;
- cmptr->cmsg_type = IP_SENDSRCADDR;
-#endif
- }
- }
+ cmptr = CMSG_FIRSTHDR(&msg);
-#ifdef HAVE_IPV6
- if (to->sa.sa_family == AF_INET6)
- {
- msg.msg_control = &control_u;
- msg.msg_controllen = sizeof(control_u);
- {
- struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
- struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
- pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
- pkt->ipi6_addr = source->addr.addr6;
- msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
- cmptr->cmsg_type = IPV6_PKTINFO;
- cmptr->cmsg_level = IPV6_LEVEL;
- }
- }
+ if (to->sa.sa_family == AF_INET)
+ {
+#if defined(IP_PKTINFO)
+ struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
+ pkt->ipi_ifindex = 0;
+ pkt->ipi_spec_dst = source->addr.addr4;
+ msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ cmptr->cmsg_level = SOL_IP;
+ cmptr->cmsg_type = IP_PKTINFO;
+#elif defined(IP_SENDSRCADDR)
+ struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
+ *a = source->addr.addr4;
+ msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ cmptr->cmsg_level = IPPROTO_IP;
+ cmptr->cmsg_type = IP_SENDSRCADDR;
#endif
+ }
+
+#ifdef HAVE_IPV6
+ else
+ {
+ struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
+ pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
+ pkt->ipi6_addr = source->addr.addr6;
+ msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmptr->cmsg_type = IPV6_PKTINFO;
+ cmptr->cmsg_level = IPV6_LEVEL;
+ }
+#endif
+ }
retry:
if (sendmsg(fd, &msg, 0) == -1)
@@ -462,7 +460,7 @@
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
-send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
+ send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
&forward->source, &forward->dest, forward->iface);
forward->new_id = 0; /* cancel */
}
@@ -476,7 +474,6 @@
unsigned short type;
struct iname *tmp;
struct all_addr dst_addr;
- int check_dst = !(daemon->options & OPT_NOWILD);
int m, n, if_index = 0;
struct iovec iov[1];
struct msghdr msg;
@@ -508,57 +505,55 @@
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return;
- source_addr.sa.sa_family = listen->family;
-#ifdef HAVE_IPV6
- if (listen->family == AF_INET6)
- {
- check_dst = 1;
- source_addr.in6.sin6_flowinfo = htonl(0);
- }
-#endif
-
- if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
- return;
-
-#if defined(IP_PKTINFO)
- if (check_dst && listen->family == AF_INET)
- for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
- if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
- {
- dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
- if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
- }
-#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
- if (check_dst && listen->family == AF_INET)
- {
- for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
- if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
- dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
- else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
- if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
- }
-#endif
-
-#ifdef HAVE_IPV6
- if (listen->family == AF_INET6)
- {
- for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
- if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
- {
- dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
- if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
- }
- }
-#endif
-
if (n < (int)sizeof(HEADER) || header->qr)
return;
- /* enforce available interface configuration */
- if (check_dst)
+ source_addr.sa.sa_family = listen->family;
+#ifdef HAVE_IPV6
+ if (listen->family == AF_INET6)
+ source_addr.in6.sin6_flowinfo = htonl(0);
+#endif
+
+ if (!(daemon->options & OPT_NOWILD))
{
struct ifreq ifr;
+ if (msg.msg_controllen < sizeof(struct cmsghdr))
+ return;
+
+#if defined(IP_PKTINFO)
+ if (listen->family == AF_INET)
+ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+ if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+ {
+ dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
+ if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
+ }
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+ if (listen->family == AF_INET)
+ {
+ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+ if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
+ dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
+ else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+ if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
+ }
+#endif
+
+#ifdef HAVE_IPV6
+ if (listen->family == AF_INET6)
+ {
+ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+ if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
+ {
+ dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
+ if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
+ }
+ }
+#endif
+
+ /* enforce available interface configuration */
+
if (if_index == 0)
return;
diff --git a/src/network.c b/src/network.c
index 034a5f6..71b7342 100644
--- a/src/network.c
+++ b/src/network.c
@@ -356,34 +356,37 @@
struct irec *iface;
int flags = port, opt = 1;
- /* Create bound listeners only for IPv4, IPv6 always binds the wildcard */
-
-#ifdef HAVE_IPV6
- if (!create_ipv6_listener(&listeners, port))
- die("failed to to create listening socket: %s", NULL);
-#endif
-
for (iface = interfaces ;iface; iface = iface->next)
- if (iface->addr.sa.sa_family == AF_INET)
- {
- struct listener *new = safe_malloc(sizeof(struct listener));
- new->family = iface->addr.sa.sa_family;
- new->next = listeners;
- listeners = new;
- 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 ||
- setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
- setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
- /* See Stevens 16.6 */
- (flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 ||
- fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
- (flags = fcntl(new->fd, F_GETFL, 0)) == -1 ||
- fcntl(new->fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
- bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
- bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
- listen(new->tcpfd, 5) == -1)
- die("failed to to create listening socket: %s", NULL);
- }
+ {
+ struct listener *new = safe_malloc(sizeof(struct listener));
+ new->family = iface->addr.sa.sa_family;
+ new->next = listeners;
+ listeners = new;
+ 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 ||
+ setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+ setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+ /* See Stevens 16.6 */
+ (flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 ||
+ fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
+ (flags = fcntl(new->fd, F_GETFL, 0)) == -1 ||
+ fcntl(new->fd, F_SETFL, flags | O_NONBLOCK) == -1)
+ die("failed to create listening socket: %s", NULL);
+
+#ifdef HAVE_IPV6
+ if (iface->addr.sa.sa_family == AF_INET6)
+ {
+ if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
+ setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
+ die("failed to set IPV6 options on listening socket: %s", NULL);
+ }
+#endif
+
+ if (bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
+ bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
+ listen(new->tcpfd, 5) == -1)
+ die("failed to bind listening socket: %s", NULL);
+ }
return listeners;
}
diff --git a/src/option.c b/src/option.c
index a3b0dc8..5c46f0d 100644
--- a/src/option.c
+++ b/src/option.c
@@ -21,7 +21,7 @@
int val;
};
-#define OPTSTRING "ZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:"
+#define OPTSTRING "ZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:"
static struct myoption opts[] = {
{"version", 0, 0, 'v'},
@@ -72,6 +72,7 @@
{"alias", 1, 0, 'V' },
{"dhcp-vendorclass", 1, 0, 'U'},
{"dhcp-userclass", 1, 0, 'j'},
+ {"dhcp-ignore", 1, 0, 'J'},
{"edns-packet-max", 1, 0, 'P'},
{"keep-in-foreground", 0, 0, 'k'},
{"dhcp-authoritative", 0, 0, 'K'},
@@ -107,8 +108,11 @@
};
static char *usage =
-"Usage: dnsmasq [options]\n"
-"\nValid options are :\n"
+"Usage: dnsmasq [options]\n\n"
+#ifndef HAVE_GETOPT_LONG
+"Use short options only on the command line.\n"
+#endif
+"Valid options are :\n"
"-a, --listen-address=ipaddr Specify local address(es) to listen on.\n"
"-A, --address=/domain/ipaddr Return ipaddr for all hosts in specified domains.\n"
"-b, --bogus-priv Fake reverse lookups for RFC1918 private address ranges.\n"
@@ -128,6 +132,7 @@
"-i, --interface=interface Specify interface(s) to listen on.\n"
"-I, --except-interface=int Specify interface(s) NOT to listen on.\n"
"-j, --dhcp-userclass=<id>,<class> Map DHCP user class to option set.\n"
+"-J, --dhcp-ignore=<id> Don't do DHCP for hosts in option set.\n"
"-k, --keep-in-foreground Do NOT fork into the background, do NOT run in debug mode.\n"
"-K, --dhcp-authoritative Assume we are the only DHCP server on the local network.\n"
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
@@ -167,7 +172,7 @@
char *problem = NULL, *buff = safe_malloc(MAXDNAME);
int option = 0, i;
FILE *file_save = NULL, *f = NULL;
- char *file_name_save = NULL, *conffile = CONFFILE;
+ char *comma, *file_name_save = NULL, *conffile = CONFFILE;
int hosts_index = 1, conffile_set = 0;
int line_save = 0, lineno = 0;
opterr = 0;
@@ -367,8 +372,7 @@
case 'm':
{
- char *comma = strchr(optarg, ',');
- if (comma)
+ if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
{
@@ -428,8 +432,10 @@
break;
case 'i':
- {
+ do {
struct iname *new = safe_malloc(sizeof(struct iname));
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
new->next = daemon->if_names;
daemon->if_names = new;
/* new->name may be NULL if someone does
@@ -438,20 +444,24 @@
new->isloop = new->used = 0;
if (strchr(optarg, ':'))
daemon->options |= OPT_NOWILD;
- break;
- }
-
+ optarg = comma;
+ } while (optarg);
+ break;
+
case 'I':
- {
+ do {
struct iname *new = safe_malloc(sizeof(struct iname));
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
new->next = daemon->if_except;
daemon->if_except = new;
new->name = safe_string_alloc(optarg);
if (strchr(optarg, ':'))
- daemon->options |= OPT_NOWILD;
- break;
- }
-
+ daemon->options |= OPT_NOWILD;
+ optarg = comma;
+ } while (optarg);
+ break;
+
case 'B':
{
struct in_addr addr;
@@ -725,7 +735,7 @@
case 'F':
{
int k, leasepos = 2;
- char *cp, *comma, *a[5] = { NULL, NULL, NULL, NULL, NULL };
+ char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
new->next = daemon->dhcp;
@@ -902,10 +912,7 @@
memcpy(new->clid, arg, len);
}
}
- else if ((arg[0] == 'n' || arg[0] == 'N') &&
- (arg[1] == 'e' || arg[1] == 'E') &&
- (arg[2] == 't' || arg[3] == 'T') &&
- arg[3] == ':')
+ else if (strstr(arg, "net:") == arg)
{
new->flags |= CONFIG_NETID;
new->netid.net = safe_string_alloc(arg+4);
@@ -1005,7 +1012,7 @@
case 'O':
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
- char *cp, *comma;
+ char *cp;
int addrs, digs, is_addr, is_hex, is_dec;
new->next = daemon->dhcp_opts;
@@ -1016,25 +1023,30 @@
if ((comma = strchr(optarg, ',')))
{
+ struct dhcp_netid *np = NULL;
*comma++ = 0;
- for (cp = optarg; *cp; cp++)
- if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
+ do {
+ for (cp = optarg; *cp; cp++)
+ if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
+ break;
+ if (!*cp)
break;
-
- if (*cp)
- {
- new->netid = safe_string_alloc(optarg);
- optarg = comma;
- if ((comma = strchr(optarg, ',')))
- *comma++ = 0;
- }
+
+ new->netid = safe_malloc(sizeof (struct dhcp_netid));
+ new->netid->net = safe_string_alloc(optarg);
+ new->netid->next = np;
+ np = new->netid;
+ optarg = comma;
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
+ } while (optarg);
}
- if ((new->opt = atoi(optarg)) == 0)
+ if (!optarg || (new->opt = atoi(optarg)) == 0)
{
option = '?';
- problem = "bad dhcp-opt";
+ problem = "bad dhcp-option";
}
else if (comma && new->opt == 119)
{
@@ -1052,7 +1064,7 @@
if (!canonicalise(optarg))
{
option = '?';
- problem = "bad dhcp-search-opt";
+ problem = "bad domain in dhcp-option";
break;
}
@@ -1115,7 +1127,7 @@
}
else if (*cp == '.')
is_dec = is_hex = 0;
- else if (!(*cp >='0' && *cp <= '9'))
+ else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
{
is_dec = is_addr = 0;
if (!((*cp >='A' && *cp <= 'F') ||
@@ -1150,31 +1162,24 @@
}
else if (is_dec)
{
- /* Given that we don't know the length,
- this appaling hack is the best available */
- unsigned int val = atoi(comma);
- if (val < 256)
+ int i, val = atoi(comma);
+ /* assume numeric arg is 1 byte except for
+ options where it is known otherwise. */
+ switch (new->opt)
{
+ default:
new->len = 1;
- new->val = safe_malloc(1);
- *(new->val) = val;
- }
- else if (val < 65536)
- {
+ break;
+ case 13: case 22: case 25: case 26:
new->len = 2;
- new->val = safe_malloc(2);
- *(new->val) = val>>8;
- *(new->val+1) = val;
- }
- else
- {
+ break;
+ case 2: case 24: case 35: case 38:
new->len = 4;
- new->val = safe_malloc(4);
- *(new->val) = val>>24;
- *(new->val+1) = val>>16;
- *(new->val+2) = val>>8;
- *(new->val+3) = val;
+ break;
}
+ new->val = safe_malloc(new->len);
+ for (i=0; i<new->len; i++)
+ new->val[i] = val>>((new->len - i - 1)*8);
}
else if (is_addr)
{
@@ -1224,19 +1229,57 @@
case 'M':
{
- char *comma;
-
- if ((comma = strchr(optarg, ',')))
- *comma = 0;
- daemon->dhcp_file = safe_string_alloc(optarg);
- if (comma)
+ struct dhcp_netid *id = NULL;
+ while (optarg && strstr(optarg, "net:") == optarg)
{
- optarg = comma+1;
+ struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
+ newid->next = id;
+ id = newid;
if ((comma = strchr(optarg, ',')))
- *comma = 0;
- daemon->dhcp_sname = safe_string_alloc(optarg);
- if (comma && (daemon->dhcp_next_server.s_addr = inet_addr(comma+1)) == (in_addr_t)-1)
- option = '?';
+ *comma++ = 0;
+ newid->net = safe_string_alloc(optarg+4);
+ optarg = comma;
+ };
+
+ if (!optarg)
+ option = '?';
+ else
+ {
+ char *dhcp_file, *dhcp_sname = NULL;
+ struct in_addr dhcp_next_server;
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
+ dhcp_file = safe_string_alloc(optarg);
+ dhcp_next_server.s_addr = 0;
+ if (comma)
+ {
+ optarg = comma;
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
+ dhcp_sname = safe_string_alloc(optarg);
+ if (comma && (dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
+ option = '?';
+ }
+ if (option != '?')
+ {
+ struct dhcp_boot *new = safe_malloc(sizeof(struct dhcp_boot));
+ new->file = dhcp_file;
+ new->sname = dhcp_sname;
+ new->next_server = dhcp_next_server;
+ new->netid = id;
+ new->next = daemon->boot_config;
+ daemon->boot_config = new;
+ }
+ }
+
+ if (option == '?')
+ {
+ struct dhcp_netid *tmp;
+ for (; id; id = tmp)
+ {
+ tmp = id->next;
+ free(id);
+ }
}
break;
}
@@ -1244,8 +1287,6 @@
case 'U':
case 'j':
{
- char *comma;
-
if (!(comma = strchr(optarg, ',')))
option = '?';
else
@@ -1262,7 +1303,27 @@
}
break;
}
-
+
+ case 'J':
+ {
+ struct dhcp_netid_list *new = safe_malloc(sizeof(struct dhcp_netid_list));
+ struct dhcp_netid *list = NULL;
+ new->next = daemon->dhcp_ignore;
+ daemon->dhcp_ignore = new;
+ do {
+ struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
+ if ((comma = strchr(optarg, ',')))
+ *comma++ = 0;
+ member->next = list;
+ list = member;
+ member->net = safe_string_alloc(optarg);
+ optarg = comma;
+ } while (optarg);
+
+ new->list = list;
+ break;
+ }
+
case 'V':
{
char *a[3] = { NULL, NULL, NULL };
@@ -1312,7 +1373,11 @@
complain(buff, NULL);
}
else
+#ifdef HAVE_GETOPT_LONG
die("bad command line options: %s.", problem ? problem : "try --help");
+#else
+ die("bad command line options: %s.", problem ? problem : "try -w");
+#endif
}
}
diff --git a/src/rfc1035.c b/src/rfc1035.c
index aa7fa96..3715ec4 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -653,7 +653,7 @@
if (!cname_count--)
return; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
- if (cpp)
+ if (newc && cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
@@ -673,7 +673,7 @@
if (aqtype == T_A)
dns_doctor(header, daemon->doctors, (struct in_addr *)p1);
newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD);
- if (cpp)
+ if (newc && cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
@@ -700,7 +700,7 @@
if (ttl || cpp)
{
newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
- if (cpp)
+ if (newc && cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
@@ -807,7 +807,7 @@
{
struct crec *crecp;
- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4|F_IPV6)) &&
+ if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
@@ -1038,7 +1038,7 @@
{
unsigned short type = T_A;
int addrsz = INADDRSZ;
-
+
if (flag == F_IPV6)
{
#ifdef HAVE_IPV6
@@ -1049,18 +1049,20 @@
#endif
}
- if (qtype != type && qtype != T_ANY && qtype != T_CNAME)
+ if (qtype != type && qtype != T_ANY)
continue;
cname_restart:
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)))
{
+ /* don't answer wildcard queries with data not from /etc/hosts
+ or DHCP leases */
+ if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+ break;
+
if (crecp->flags & F_CNAME)
{
- if (qtype == T_CNAME)
- ans = 1;
-
if (!dryrun)
{
ansp = add_text_record(header, nameoffset, ansp, crecp->ttd - now, 0, T_CNAME,
@@ -1068,17 +1070,10 @@
anscount++;
log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
}
+
strcpy(name, cache_get_name(crecp->addr.cname.cache));
goto cname_restart;
}
-
- if (qtype == T_CNAME)
- break;
-
- /* don't answer wildcard queries with data not from /etc/hosts
- or DHCP leases */
- if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
- continue;
if (crecp->flags & F_NEG)
{
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 5514ce3..0f9f30a 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -57,12 +57,14 @@
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
-static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
+static void bootp_option_put(struct dhcp_packet *mess,
+ struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static int option_len(unsigned char *opt);
static void *option_ptr(unsigned char *opt);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
+static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
@@ -81,10 +83,11 @@
int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now)
{
struct dhcp_context *context, *context_tmp;
- unsigned char *opt, *clid;
+ unsigned char *opt, *clid = NULL;
struct dhcp_lease *lease, *ltmp;
struct dhcp_vendor *vendor;
- int clid_len;
+ struct dhcp_netid_list *id_list;
+ int clid_len = 0, ignore = 0;
struct dhcp_packet *mess = &daemon->dhcp_packet->data;
unsigned char *p = mess->options + sizeof(u32); /* skip cookie */
unsigned char *end = (unsigned char *)(daemon->dhcp_packet + 1);
@@ -139,6 +142,15 @@
/* Check for RFC3011 subnet selector */
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT)))
subnet_addr = option_addr(opt);
+
+ /* If there is no client identifier option, use the hardware address */
+ if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
+ {
+ clid = option_ptr(opt);
+ clid_len = option_len(opt);
+ }
+ else
+ clid = mess->chaddr;
}
/* Determine network for this packet. If the machine has an address already, and we don't have
@@ -178,60 +190,67 @@
mess->op = BOOTREPLY;
+ config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL);
+
if (mess_type == 0)
{
/* BOOTP request */
- config = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, NULL);
- if (have_config(config, CONFIG_ADDR) &&
- !have_config(config, CONFIG_DISABLE) &&
- !lease_find_by_addr(config->addr))
+ struct dhcp_netid id;
+ char save = mess->file[128];
+ struct in_addr *logaddr = NULL;
+
+ if (have_config(config, CONFIG_ADDR))
{
- struct dhcp_netid id;
- char save = mess->file[128];
- end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
+ logaddr = &config->addr;
mess->yiaddr = config->addr;
- mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
- if (have_config(config, CONFIG_NAME))
- hostname = config->hostname;
- if (have_config(config, CONFIG_NETID))
- {
- config->netid.next = netid;
- netid = &config->netid;
- }
- /* Match incoming filename field as a netid. */
- if (mess->file[0])
- {
- mess->file[128] = 0; /* ensure zero term. */
- id.net = mess->file;
- id.next = netid;
- netid = &id;
- }
- p = do_req_options(context, p, end, NULL, daemon,
- hostname, iface_addr, netid, subnet_addr);
- /* must do this after do_req_options since it overwrites filename field. */
- bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
- p = option_end(p, end, mess);
- log_packet(NULL, &config->addr, mess->chaddr, iface_name, NULL);
- mess->file[128] = save;
- return p - (unsigned char *)mess;
+ if (lease_find_by_addr(config->addr))
+ message = "address in use";
}
- return 0;
+ else
+ message = "no address configured";
+
+ if (have_config(config, CONFIG_DISABLE))
+ message = "disabled";
+
+ end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
+
+ if (have_config(config, CONFIG_NAME))
+ hostname = config->hostname;
+
+ if (have_config(config, CONFIG_NETID))
+ {
+ config->netid.next = netid;
+ netid = &config->netid;
+ }
+
+ /* Match incoming filename field as a netid. */
+ if (mess->file[0])
+ {
+ mess->file[128] = 0; /* ensure zero term. */
+ id.net = mess->file;
+ id.next = netid;
+ netid = &id;
+ }
+
+ for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
+ if (match_netid(id_list->list, netid))
+ message = "disabled";
+
+ p = do_req_options(context, p, end, NULL, daemon,
+ hostname, iface_addr, netid, subnet_addr);
+ /* must do this after do_req_options since it overwrites filename field. */
+ mess->siaddr = iface_addr;
+ bootp_option_put(mess, daemon->boot_config, netid);
+ p = option_end(p, end, mess);
+ log_packet(NULL, logaddr, mess->chaddr, iface_name, message);
+ mess->file[128] = save;
+
+ if (message)
+ return 0;
+ else
+ return p - (unsigned char *)mess;
}
- /* If there is no client identifier option, use the hardware address */
- if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
- {
- clid = option_ptr(opt);
- clid_len = option_len(opt);
- }
- else
- {
- clid = mess->chaddr;
- clid_len = 0;
- }
-
- config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL);
-
if (have_config(config, CONFIG_NAME))
hostname = config->hostname;
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
@@ -280,46 +299,45 @@
netid = &config->netid;
}
- /* Theres a chance that carefully chosen data could match the same
- vendor/user option twice and make a loop in the netid chain. */
- for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
- vendor->used = 0;
+ /* user-class options are, according to RFC3004, supposed to contain
+ a set of counted strings. Here we check that this is so (by seeing
+ if the counts are consistent with the overall option length) and if
+ so zero the counts so that we don't get spurious matches between
+ the vendor string and the counts. If the lengths don't add up, we
+ assume that the option is a single string and non RFC3004 compliant
+ and just do the substring match. dhclient provides these broken options. */
- if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
- for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
- if (vendor->is_vendor && !vendor->used)
- {
- int i;
- for (i = 0; i <= (option_len(opt) - vendor->len); i++)
- if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
- {
- vendor->used = 1;
- vendor->netid.next = netid;
- netid = &vendor->netid;
- break;
- }
- }
-
if ((opt = option_find(mess, sz, OPTION_USER_CLASS)))
{
- unsigned char *ucp = option_ptr(opt);
- int j;
- for (j = 0; j < option_len(opt); j += ucp[j] + 1)
- for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
- if (!vendor->is_vendor && !vendor->used)
- {
- int i;
- for (i = 0; i <= (ucp[j] - vendor->len); i++)
- if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0)
- {
- vendor->used = 1;
- vendor->netid.next = netid;
- netid = &vendor->netid;
- break;
- }
- }
+ unsigned char *ucp = option_ptr(opt);
+ int tmp, j;
+ for (j = 0; j < option_len(opt); j += ucp[j] + 1);
+ if (j == option_len(opt))
+ for (j = 0; j < option_len(opt); j = tmp)
+ {
+ tmp = j + ucp[j] + 1;
+ ucp[j] = 0;
+ }
}
-
+
+ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+ if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS)))
+ {
+ int i;
+ for (i = 0; i <= (option_len(opt) - vendor->len); i++)
+ if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
+ {
+ vendor->netid.next = netid;
+ netid = &vendor->netid;
+ break;
+ }
+ }
+
+ /* if all the netids in the ignore list are present, ignore this client */
+ for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
+ if (match_netid(id_list->list, netid))
+ ignore = 1;
+
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
if (have_config(config, CONFIG_NOCLID))
{
@@ -420,7 +438,7 @@
case DHCPDISCOVER:
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
addr = option_addr(opt);
- if (have_config(config, CONFIG_DISABLE))
+ if (ignore || have_config(config, CONFIG_DISABLE))
message = "ignored";
else if (have_config(config, CONFIG_ADDR) &&
(!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
@@ -437,8 +455,8 @@
if (message)
return 0;
- bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
- mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
+ mess->siaddr = iface_addr;
+ bootp_option_put(mess, daemon->boot_config, netid);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
@@ -456,9 +474,9 @@
return p - (unsigned char *)mess;
case DHCPREQUEST:
- if (have_config(config, CONFIG_DISABLE))
- message = "disabled";
- else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
+ if (ignore || have_config(config, CONFIG_DISABLE))
+ return 0;
+ if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
{
/* SELECTING or INIT_REBOOT */
mess->yiaddr = option_addr(opt);
@@ -555,8 +573,8 @@
lease_set_hostname(lease, hostname, daemon->domain_suffix);
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
- bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
- mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
+ mess->siaddr = iface_addr;
+ bootp_option_put(mess, daemon->boot_config, netid);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
@@ -573,7 +591,7 @@
return p - (unsigned char *)mess;
case DHCPINFORM:
- if (have_config(config, CONFIG_DISABLE))
+ if (ignore || have_config(config, CONFIG_DISABLE))
message = "ignored";
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message);
@@ -581,6 +599,8 @@
if (message || mess->ciaddr.s_addr == 0)
return 0;
+ mess->siaddr = iface_addr;
+ bootp_option_put(mess, daemon->boot_config, netid);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = do_req_options(context, p, end, req_options, daemon,
@@ -641,14 +661,35 @@
return ret;
}
-static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
+static void bootp_option_put(struct dhcp_packet *mess,
+ struct dhcp_boot *boot_opts, struct dhcp_netid *netids)
{
+ struct dhcp_boot *tmp;
+
+ for (tmp = boot_opts; tmp; tmp = tmp->next)
+ if (match_netid(tmp->netid, netids))
+ break;
+ if (!tmp)
+ /* No match, look for one without a netid */
+ for (tmp = boot_opts; tmp; tmp = tmp->next)
+ if (!tmp->netid)
+ break;
+
+ /* Do this _after_ the matching above, since in
+ BOOTP mode, one if the things we match is the filename. */
+
memset(mess->sname, 0, sizeof(mess->sname));
memset(mess->file, 0, sizeof(mess->file));
- if (sname)
- strncpy(mess->sname, sname, sizeof(mess->sname)-1);
- if (filename)
- strncpy(mess->file, filename, sizeof(mess->file)-1);
+
+ if (tmp)
+ {
+ if (tmp->sname)
+ strncpy(mess->sname, tmp->sname, sizeof(mess->sname)-1);
+ if (tmp->file)
+ strncpy(mess->file, tmp->file, sizeof(mess->file)-1);
+ if (tmp->next_server.s_addr)
+ mess->siaddr = tmp->next_server;
+ }
}
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
@@ -759,20 +800,43 @@
return 0;
}
+/* Is every member of check matched by a member of pool? */
+static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
+{
+ struct dhcp_netid *tmp1;
+
+ if (!check)
+ return 0;
+
+ for (; check; check = check->next)
+ {
+ if (check->net[0] != '#')
+ {
+ for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+ if (strcmp(check->net, tmp1->net) == 0)
+ break;
+ if (!tmp1)
+ return 0;
+ }
+ else
+ for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+ if (strcmp((check->net)+1, tmp1->net) == 0)
+ return 0;
+ }
+ return 1;
+}
+
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
{
struct dhcp_opt *tmp;
- struct dhcp_netid *tmp1;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt)
{
if (netid)
{
- if (tmp->netid)
- for (tmp1 = netid; tmp1; tmp1 = tmp1->next)
- if (strcmp(tmp->netid, tmp1->net) == 0)
- return tmp;
+ if (match_netid(tmp->netid, netid))
+ return tmp;
}
else if (!tmp->netid)
return tmp;