Compare commits

...
3 Commits
Author SHA1 Message Date
Simon Kelley bb01cb9604 import of dnsmasq-2.19.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley 59353a6b56 import of dnsmasq-2.18.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley 26128d2747 import of dnsmasq-2.17.tar.gz 2012-01-05 17:31:11 +00:00
16 changed files with 869 additions and 536 deletions

No files matched your search

+75
View File
@@ -1253,3 +1253,78 @@ version 2.16
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.
version 2.18
Reworked the Linux interface discovery code (again) to
cope with interfaces which have only IPv6 addresses and
interfaces with more than one IPv6 address. Thanks to
Martin Pels for help with that.
Fix problems which occured when more than one dhcp-range
was specified in the same subnet: sometimes parameters
(lease time, network-id tag) from the wrong one would be
used. Thanks to Rory Campbell-Lange for the bug report.
Reset cache statistics when clearing the cache.
Enable long command line options on FreeBSD when the
C library supports them.
version 2.19
Tweaked the Linux-only interface discovery code to cope
with interface-indexes larger than 8 bits in
/proc/net/if_inet6. This only affects Linux, obviously.
Thanks to Richard Atterer for the bug report.
Check for under-length option fields in DHCP packets, a
zero length client-id, in particluar, could seriously
confuse dnsmasq 'till now. Thanks to Will Murname for help
with that.
If a DHCP-allocated address has an associated name in
/etc/hosts, and the client does not provide a hostname
parameter and there is no hostname in a matching dhcp-host
option, send the /etc/hosts name as the hostname in
the DHCP lease. Thanks to Will Murname for the suggestion.
+16 -1
View File
@@ -115,7 +115,7 @@ A: Resolver code sometime does strange things when given names without
--expand-hosts and --domain-suffix options.
Q: Can I get dnsmasq to save the contents of its cache to disk when
I shut my machine down and re-load when it starts again.
I shut my machine down and re-load when it starts again?
A: No, that facility is not provided. Very few names in the DNS have
their time-to-live set for longer than a few hours so most of the
@@ -299,7 +299,22 @@ A: Because when a Gentoo box shuts down, it releases its lease with
dnsmasq ignores it until is times out and restarts the process.
To fix this, set the dhcp-authoritative flag in dnsmasq.
Q: My laptop has two network interfaces, a wired one and a wireless
one. I never use both interfaces at the same time, and I'd like the
same IP and configuration to be used irrespcetive of which
interface is in use. How can I do that.
A: By default, the identity of a machine is determined by using the
MAC address, which is associated with interface hardware. Once an
IP is bound to the MAC address of one interface, it cannot be
associated with another MAC address until after the DHCP lease
expires. The solution to this is to use a client-id as the machine
identity rather than the MAC address. If you arrange for the same
client-id to sent when either interface is in use, the DHCP server
will recognise the same machine, and use the same address. The
method for setting the client-id varies with DHCP client software,
dhcpcd uses the "-I" flag. Windows uses a registry setting,
see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
+1 -1
View File
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.16
Version: 2.19
Release: 1
Copyright: GPL
Group: System Environment/Daemons
+1 -1
View File
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.16
Version: 2.19
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
+28 -8
View File
@@ -366,7 +366,7 @@ have exactly the same effect as
.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 @@ and to set the time-server address to 192.168.0.4, do
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 @@ to different classes of hosts. It is possible, for instance to use
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 @@ and run dnsmasq with the
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
+20 -22
View File
@@ -23,7 +23,8 @@ Mac OS X.
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 @@ machine: If the names of local machines are there, then they can all
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 @@ upstream servers handling only those domains. This makes integration
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 @@ bzip2 dnsmasq-zzz.tar
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>
+9 -7
View File
@@ -17,7 +17,7 @@ static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
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 @@ void cache_init(int size, int logq)
cache_size = size;
big_free = NULL;
bignames_left = size/10;
index = 0;
uid = 0;
cache_inserted = cache_live_freed = 0;
@@ -48,7 +48,7 @@ void cache_init(int size, int logq)
{
cache_link(crecp);
crecp->flags = 0;
crecp->uid = index++;
crecp->uid = uid++;
}
}
@@ -85,7 +85,7 @@ static void cache_free(struct crec *crecp)
{
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;
@@ -602,6 +602,8 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
struct crec *cache, **up, *tmp;
int i;
cache_inserted = cache_live_freed = 0;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{
@@ -673,7 +675,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
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 +683,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
{
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 +691,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
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)))
+12 -5
View File
@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2004 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
@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.16"
#define VERSION "2.19"
#define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -180,7 +180,7 @@ NOTES:
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
HAVE_DEV_RANDOM - FreeBSD and NetBSD
(OpenBSD with hardware random number generator)
HAVE_GETOPT_LONG - NetBSD
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
*/
@@ -205,8 +205,10 @@ NOTES:
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Don't fork into background on uClinux */
#if defined(__uClinux__)
/* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new
processes for TCP connections. It's intended for use on MMU-less kernels. */
# define NO_FORK
#endif
@@ -254,7 +256,12 @@ typedef unsigned long in_addr_t;
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_GETOPT_LONG
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
#else
# undef HAVE_GETOPT_LONG
#endif
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
+68 -9
View File
@@ -47,6 +47,8 @@ void dhcp_init(struct daemon *daemon)
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 @@ void dhcp_init(struct daemon *daemon)
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 @@ void dhcp_packet(struct daemon *daemon, time_t now)
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,13 +369,13 @@ void dhcp_packet(struct daemon *daemon, time_t now)
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
}
}
}
int address_available(struct dhcp_context *context, struct in_addr taddr)
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
{
/* Check is an address is OK for this network, check all
possible ranges. */
@@ -391,12 +390,34 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
if (!context->static_only &&
addr >= start &&
addr <= end)
return 1;
return context;
}
return 0;
return NULL;
}
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
{
/* We start of with a set of possible contexts, all on the current subnet.
These are chained on ->current.
Here we have an address, and return the actual context correponding to that
address. Note that none may fit, if the address came a dhcp-host and is outside
any dhcp-range. In that case we return a static range is possible, or failing that,
any context on the subnet. (If there's more than one, this is a dodgy configuration:
maybe there should be a warning.) */
struct dhcp_context *tmp = address_available(context, taddr);
if (tmp)
return tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (tmp->static_only)
return tmp;
return context;
}
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
{
struct dhcp_config *config;
@@ -635,3 +656,41 @@ void dhcp_update_configs(struct dhcp_config *configs)
}
}
/* 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. */
char *host_from_dns(struct daemon *daemon, struct in_addr addr)
{
struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
char *hostname = NULL;
if (lookup && (lookup->flags & F_HOSTS))
{
hostname = daemon->dhcp_buff;
hostname[256] = 0;
strncpy(hostname, cache_get_name(lookup), 256);
hostname = strip_hostname(daemon, hostname);
}
return hostname;
}
char *strip_hostname(struct daemon *daemon, char *hostname)
{
char *dot = strchr(hostname, '.');
if (dot)
{
if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
{
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
hostname = NULL;
}
else
{
*dot = 0; /* truncate */
if (strlen(hostname) == 0)
hostname = NULL; /* nothing left */
}
}
return hostname;
}
+12 -6
View File
@@ -30,6 +30,7 @@ int main (int argc, char **argv)
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 @@ int main (int argc, char **argv)
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 @@ int main (int argc, char **argv)
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 @@ int main (int argc, char **argv)
"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");
@@ -545,11 +549,13 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (!match || (num_kids >= MAX_PROCS))
close(confd);
#ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && fork())
{
num_kids++;
close(confd);
}
#endif
else
{
char *buff;
+22 -9
View File
@@ -29,6 +29,9 @@
#include <syslog.h>
#include <arpa/nameser.h>
/* and this. */
#include <getopt.h>
#include "config.h"
#include <arpa/inet.h>
@@ -50,9 +53,6 @@
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#endif
#include <time.h>
#include <errno.h>
#include <pwd.h>
@@ -269,6 +269,10 @@ struct dhcp_netid {
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_config {
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 daemon {
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;
@@ -457,7 +467,8 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port);
void dhcp_init(struct daemon *daemon);
void dhcp_packet(struct daemon *daemon, time_t now);
int address_available(struct dhcp_context *context, struct in_addr addr);
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr);
struct dhcp_config *find_config(struct dhcp_config *configs,
@@ -467,6 +478,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
void dhcp_update_configs(struct dhcp_config *configs);
void dhcp_read_ethers(struct daemon *daemon);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(struct daemon *daemon, char *hostname);
char *host_from_dns(struct daemon *daemon, struct in_addr addr);
/* lease.c */
void lease_update_file(int force, time_t now);
+76 -81
View File
@@ -65,45 +65,43 @@ static void send_from(int fd, int nowild, char *packet, int len,
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 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
{
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 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
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 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
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;
+112 -100
View File
@@ -14,11 +14,33 @@
#include "dnsmasq.h"
static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *name, union mysockaddr *addr)
static struct irec *add_iface(struct daemon *daemon, struct irec *list,
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
sure that loopback interfaces are in that set. */
if (daemon->if_names && is_loopback)
{
struct iname *lo;
for (lo = daemon->if_names; lo; lo = lo->next)
if (lo->name && strcmp(lo->name, name) == 0)
{
lo->isloop = 1;
break;
}
if (!lo)
{
lo = safe_malloc(sizeof(struct iname));
lo->name = safe_string_alloc(name);
lo->isloop = lo->used = 1;
lo->next = daemon->if_names;
daemon->if_names = lo;
}
}
/* check blacklist */
if (daemon->if_except)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
@@ -67,6 +89,10 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *na
struct irec *enumerate_interfaces(struct daemon *daemon)
{
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
FILE *f;
#endif
union mysockaddr addr;
struct irec *iface = NULL;
char *buf, *ptr;
struct ifreq *ifr = NULL;
@@ -74,7 +100,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
int lastlen = 0;
int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd == -1)
die ("cannot create socket to enumerate interfaces: %s", NULL);
@@ -99,9 +125,8 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
free(buf);
}
for (ptr = buf; ptr < buf + len; )
for (ptr = buf; ptr < buf + ifc.ifc_len; )
{
union mysockaddr addr;
#ifdef HAVE_SOCKADDR_SA_LEN
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
@@ -141,76 +166,45 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
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 (daemon->if_names && (ifr->ifr_flags & IFF_LOOPBACK))
{
struct iname *lo;
for (lo = daemon->if_names; lo; lo = lo->next)
if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0)
{
lo->isloop = 1;
break;
}
if (!lo)
{
lo = safe_malloc(sizeof(struct iname));
lo->name = safe_string_alloc(ifr->ifr_name);
lo->isloop = lo->used = 1;
lo->next = daemon->if_names;
daemon->if_names = lo;
}
}
iface = add_iface(daemon, iface, ifr->ifr_name, &addr);
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
{
FILE *f = fopen(IP6INTERFACES, "r");
int found = 0;
union mysockaddr addr6;
if (f)
{
unsigned int plen, scope, flags, if_idx;
char devname[20], addrstring[32];
while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n",
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
{
if (strcmp(devname, ifr->ifr_name) == 0)
{
int i;
unsigned char *addr6p = (unsigned char *) &addr6.in6.sin6_addr;
memset(&addr6, 0, sizeof(addr6));
addr6.sa.sa_family = AF_INET6;
for (i=0; i<16; i++)
{
unsigned int byte;
sscanf(addrstring+i+i, "%02x", &byte);
addr6p[i] = byte;
}
addr6.in6.sin6_port = htons(daemon->port);
addr6.in6.sin6_flowinfo = htonl(0);
addr6.in6.sin6_scope_id = htonl(scope);
found = 1;
break;
}
}
fclose(f);
}
if (found)
iface = add_iface(daemon, iface, ifr->ifr_name, &addr6);
}
#endif /* LINUX */
iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr);
}
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
if ((f = fopen(IP6INTERFACES, "r")))
{
unsigned int plen, scope, flags, if_idx;
char devname[20], addrstring[32];
while (fscanf(f, "%32s %x %x %x %x %20s\n",
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
{
int i;
struct ifreq sifr;
unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr;
memset(&addr, 0, sizeof(addr));
addr.sa.sa_family = AF_INET6;
for (i=0; i<16; i++)
{
unsigned int byte;
sscanf(addrstring+i+i, "%02x", &byte);
addr6p[i] = byte;
}
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_flowinfo = htonl(0);
addr.in6.sin6_scope_id = htonl(scope);
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);
}
fclose(f);
}
#endif /* LINUX */
if (buf)
free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -356,34 +350,52 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
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;
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)
{
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6 && errno == ENODEV)
{
close(new->tcpfd);
close(new->fd);
free(new);
}
else
#endif
die("failed to bind listening socket: %s", NULL);
}
else
{
listeners = new;
if (listen(new->tcpfd, 5) == -1)
die("failed to listen on socket: %s", NULL);
}
}
return listeners;
}
+143 -76
View File
@@ -21,7 +21,7 @@ struct myoption {
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 @@ static struct myoption opts[] = {
{"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 struct optflags optmap[] = {
};
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 @@ static char *usage =
"-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 @@ struct daemon *read_opts (int argc, char **argv)
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 @@ struct daemon *read_opts (int argc, char **argv)
case 'm':
{
char *comma = strchr(optarg, ',');
if (comma)
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
{
@@ -428,8 +432,10 @@ struct daemon *read_opts (int argc, char **argv)
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 @@ struct daemon *read_opts (int argc, char **argv)
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;
@@ -468,8 +478,10 @@ struct daemon *read_opts (int argc, char **argv)
}
case 'a':
{
do {
struct iname *new = safe_malloc(sizeof(struct iname));
if ((comma = strchr(optarg, ',')))
*comma++ = 0;
new->next = daemon->if_addrs;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr))
@@ -500,14 +512,14 @@ struct daemon *read_opts (int argc, char **argv)
{
option = '?'; /* error */
free(new);
new = NULL;
break;
}
if (new)
daemon->if_addrs = new;
break;
}
daemon->if_addrs = new;
optarg = comma;
} while (optarg);
break;
case 'S':
case 'A':
{
@@ -725,7 +737,7 @@ struct daemon *read_opts (int argc, char **argv)
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 +914,7 @@ struct daemon *read_opts (int argc, char **argv)
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 +1014,7 @@ struct daemon *read_opts (int argc, char **argv)
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 +1025,30 @@ struct daemon *read_opts (int argc, char **argv)
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 +1066,7 @@ struct daemon *read_opts (int argc, char **argv)
if (!canonicalise(optarg))
{
option = '?';
problem = "bad dhcp-search-opt";
problem = "bad domain in dhcp-option";
break;
}
@@ -1115,7 +1129,7 @@ struct daemon *read_opts (int argc, char **argv)
}
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 +1164,24 @@ struct daemon *read_opts (int argc, char **argv)
}
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 +1231,57 @@ struct daemon *read_opts (int argc, char **argv)
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 +1289,6 @@ struct daemon *read_opts (int argc, char **argv)
case 'U':
case 'j':
{
char *comma;
if (!(comma = strchr(optarg, ',')))
option = '?';
else
@@ -1262,7 +1305,27 @@ struct daemon *read_opts (int argc, char **argv)
}
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 +1375,11 @@ struct daemon *read_opts (int argc, char **argv)
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
}
}
+12 -17
View File
@@ -653,7 +653,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
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 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
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 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
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 @@ int check_for_local_domain(char *name, time_t now, struct mx_record *mx)
{
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 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
{
unsigned short type = T_A;
int addrsz = INADDRSZ;
if (flag == F_IPV6)
{
#ifdef HAVE_IPV6
@@ -1049,18 +1049,20 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
#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 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
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)
{
+262 -193
View File
@@ -57,13 +57,15 @@
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 int option_len(unsigned char *opt);
static void bootp_option_put(struct dhcp_packet *mess,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static unsigned 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 unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
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, unsigned int minsize);
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
@@ -81,17 +83,18 @@ static int have_config(struct dhcp_config *config, unsigned int mask)
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);
char *hostname = NULL;
char *req_options = NULL;
char *message = NULL;
unsigned int renewal_time, expires_time, def_time;
unsigned int time;
struct dhcp_config *config;
struct dhcp_netid *netid = NULL;
struct in_addr addr, subnet_addr;
@@ -123,7 +126,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
return 0;
/* check for DHCP rather than BOOTP */
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
{
mess_type = option_uint(opt, 1);
@@ -133,12 +136,21 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* Some buggy clients set ciaddr when they shouldn't, so clear that here since
it can affect the context-determination code. */
if ((option_find(mess, sz, OPTION_REQUESTED_IP) || mess_type == DHCPDISCOVER))
if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
mess->ciaddr.s_addr = 0;
/* Check for RFC3011 subnet selector */
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT)))
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
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, 1)))
{
clid_len = option_len(opt);
clid = option_ptr(opt);
}
else
clid = mess->chaddr;
}
/* Determine network for this packet. If the machine has an address already, and we don't have
@@ -159,13 +171,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
{
context_tmp->current = context;
context = context_tmp;
/* start to build netid chain */
if (context_tmp->netid.net)
{
context_tmp->netid.next = netid;
netid = &context_tmp->netid;
}
}
if (!context)
@@ -178,65 +183,79 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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";
context = narrow_context(context, config->addr);
}
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 (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
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)))
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
{
int len = option_len(opt);
unsigned int len = option_len(opt);
hostname = daemon->dhcp_buff;
memcpy(hostname, option_ptr(opt), len);
/* May not be zero terminated */
@@ -244,33 +263,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* ensure there are no strange chars in there */
if (!canonicalise(hostname))
hostname = NULL;
else
else if ((hostname = strip_hostname(daemon, hostname)) && !config)
{
char *dot = strchr(hostname, '.');
if (dot)
{
if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
{
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
hostname = NULL;
}
else
{
*dot = 0; /* truncate */
if (strlen(hostname) == 0)
hostname = NULL; /* nothing left */
}
}
/* Search again now we have a hostname.
Only accept configs without CLID and HWADDR here, (they won't match)
to avoid impersonation by name. */
if (!config)
{
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname);
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
config = new;
}
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname);
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
config = new;
}
}
@@ -280,46 +280,45 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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)))
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
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);
unsigned 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, 1)))
{
unsigned 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))
{
@@ -330,44 +329,22 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* do we have a lease in store? */
lease = lease_find_by_client(clid, clid_len);
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
{
unsigned int req_time = option_uint(opt, 4);
if (def_time == 0xffffffff ||
(req_time != 0xffffffff && req_time < def_time))
expires_time = renewal_time = req_time;
else
expires_time = renewal_time = def_time;
}
else
{
renewal_time = def_time;
if (lease)
expires_time = (unsigned int)difftime(lease->expires, now);
else
expires_time = def_time;
}
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
{
int len = option_len(opt);
req_options = daemon->dhcp_buff2;
memcpy(req_options, option_ptr(opt), len);
req_options[len] = OPTION_END;
memcpy(req_options, option_ptr(opt), option_len(opt));
req_options[option_len(opt)] = OPTION_END;
}
switch (mess_type)
{
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
return 0;
/* sanitise any message. Paranoid? Moi? */
if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1)))
{
char *p = option_ptr(opt), *q = daemon->dhcp_buff;
int i;
@@ -382,7 +359,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
message = daemon->dhcp_buff;
}
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
return 0;
log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
@@ -404,7 +381,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
return 0;
case DHCPRELEASE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
return 0;
@@ -418,9 +395,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
return 0;
case DHCPDISCOVER:
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
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,16 +414,33 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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;
context = narrow_context(context, mess->yiaddr);
if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
{
unsigned int req_time = option_uint(opt, 4);
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
time = req_time;
}
else if (lease && lease->expires != 0)
time = (unsigned int)difftime(lease->expires, now);
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);
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (expires_time != 0xffffffff)
if (time != 0xffffffff)
{
p = option_put(p, end, OPTION_T1, 4, (expires_time/2));
p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8));
p = option_put(p, end, OPTION_T1, 4, (time/2));
p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
}
p = do_req_options(context, p, end, req_options, daemon,
NULL, iface_addr, netid, subnet_addr);
@@ -456,14 +450,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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, INADDRSZ)))
{
/* SELECTING or INIT_REBOOT */
mess->yiaddr = option_addr(opt);
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)))
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
{
/* SELECTING */
if (iface_addr.s_addr != option_addr(opt).s_addr)
@@ -495,9 +489,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* desynchronise renewals */
fuzz = rand16();
while (fuzz > (renewal_time/16))
fuzz = fuzz/2;
mess->yiaddr = mess->ciaddr;
}
@@ -550,20 +541,39 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
{
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
context = narrow_context(context, mess->yiaddr);
if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
{
unsigned int req_time = option_uint(opt, 4);
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
time = req_time;
}
lease_set_hwaddr(lease, mess->chaddr);
if (!hostname)
hostname = host_from_dns(daemon, mess->yiaddr);
if (hostname)
lease_set_hostname(lease, hostname, daemon->domain_suffix);
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)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);
if (renewal_time != 0xffffffff)
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
if (time != 0xffffffff)
{
p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
while (fuzz > (time/16))
fuzz = fuzz/2;
p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
p = option_put(p, end, OPTION_T2, 4, ((time * 7)/8) - fuzz);
}
p = do_req_options(context, p, end, req_options, daemon,
hostname, iface_addr, netid, subnet_addr);
@@ -573,7 +583,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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,8 +591,19 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if (message || mess->ciaddr.s_addr == 0)
return 0;
context = narrow_context(context, mess->ciaddr);
if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
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));
if (!hostname)
hostname = host_from_dns(daemon, mess->yiaddr);
p = do_req_options(context, p, end, req_options, daemon,
hostname, iface_addr, netid, subnet_addr);
p = option_end(p, end, mess);
@@ -607,7 +628,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr,
string ? string : "");
}
static int option_len(unsigned char *opt)
static unsigned int option_len(unsigned char *opt)
{
return opt[1];
}
@@ -641,14 +662,35 @@ static unsigned int option_uint(unsigned char *opt, int size)
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)
@@ -698,13 +740,13 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
while (*p != OPTION_END)
{
if (end && (p >= end))
if (p >= end)
return 0; /* malformed packet */
else if (*p == OPTION_PAD)
p++;
else if (*p == OPTION_OVERLOAD)
{
if (end && (p >= end - 3))
if (p >= end - 3)
return 0; /* malformed packet */
if (overload)
*overload = *(p+2);
@@ -713,10 +755,10 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
else
{
int opt_len;;
if (end && (p >= end - 2))
if (p >= end - 2)
return 0; /* malformed packet */
opt_len = option_len(p);
if (end && (p >= end - (2 + opt_len)))
if (p >= end - (2 + opt_len))
return 0; /* malformed packet */
if (*p == opt)
return p;
@@ -727,7 +769,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
return NULL;
}
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize)
{
int overload = 0;
unsigned char *ret;
@@ -740,7 +782,11 @@ static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_ty
if (!ret && (overload & 2))
ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
/* Check the option field is big enough */
if (ret && (option_len(ret) < minsize))
ret = NULL;
return ret;
}
@@ -759,20 +805,43 @@ static int in_list(unsigned char *list, int opt)
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;