Compare commits

..

3 Commits
v2.6 ... v2.9

Author SHA1 Message Date
Simon Kelley
de37951cf4 import of dnsmasq-2.9.tar.gz 2012-01-05 17:31:10 +00:00
Simon Kelley
a222641cb0 import of dnsmasq-2.8.tar.gz 2012-01-05 17:31:10 +00:00
Simon Kelley
a84fa1d085 import of dnsmasq-2.7.tar.gz 2012-01-05 17:31:10 +00:00
19 changed files with 996 additions and 429 deletions

131
CHANGELOG
View File

@@ -916,12 +916,143 @@ release 2.6
comment, a '#' character must now be a the start of a
line or preceded by whitespace. Thanks to Christian
Haggstrom for the bug report.
release 2.7
Allow the dhcp-host specification of id:* which makes
dnsmasq ignore any client-id. This is useful to ensure
that a dual-boot machine sees the same lease when one OS
gives a client-id and the other doesn't. It's also useful
when PXE boot DHCP does not use client IDs but the OS it boots
does. Thanks to Grzegorz Nosek for suggesting this enhancement.
No longer assume that ciaddr is zero in received DHCPDISCOVER
messages, just for security against broken clients.
Set default of siaddr field to the address of the machine running
dnsmasq when not explicitly set using dhcp-boot
option. This is the ISC dhcpd behaviour.
Send T1 and T2 options in DHCPOFFER packets. This is required
by the DHCP client in some JetDirect printers. Thanks
to Paul Mattal for work on this.
Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel.
The code which added loopback interfaces to the list
was confusing the DHCP code, which expected one interface only.
Solved by adding loopback interfaces to address list instead.
Add dhcp-vendorclass option to allow options to be sent only
to certain classes of clients.
Tweaked option search code so that if a netid-qualified
option is used, any unqualified option is ignored.
Changed the method of picking new dynamic IP
addresses. This used to use the next consecutive
address as long it was free, now it uses a hash
from the client hardware address. This reduces the amount
of address movement for clients which let their lease
expire and allows consecutive DHCPOFFERS to the same host
to (almost always) be for the same address, without
storing state before a lease is granted.
Tweaked option handling code to return all possible
options rather than none when DHCP "requested options"
field is missing. This fixes interoperability with
ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for
help with this.
release 2.8
Pad DHCP packets to a minimum size of 300 bytes. This
fixes interoperability problems with the Linux in-kernel
DHCP/BOOTP client. Thanks to Richard Musil for
diagnosing this and supplying a patch.
Fixed option-parsing bug and potential memory leak. Patch
from Richard Musil.
Improved vendor class configuration and added user class
configuration. Specifically: (1) options are matched on
the netids from dhcp-range, dhcp-host, vendor class and
user class(es). Multiple net-ids are allowed and options
are searched on them all. (2) matches agains vendor class
and user class are now on a substring, if the given
string is a substring of the vendor/user class, then a
match occurs. Thanks again to Richard Musil for prompting
this.
Make "#" match any domain on --address and --server
flags. --address=/#/1.2.3.4 will return 1.2.3.4 for _any_
domain not otherwise matched. Of course
--server=/#/1.2.3.4 is exactly equivalent to
--server=1.2.3.4. Special request from Josh Howlett.
Fixed a nasty bug which would cause dnsmasq to lose track
of leases for hosts which had a --dhcp-host flag without
a name specification. The mechanism for this was that
the hostname could get erroneously set as a zero-length
string and then written to the leases file as a
mal-formed line. Restarting dnsmasq would then lose the lease.
Alex Hermann's work helped chase down this problem.
Add checks against DHCP clients which return zero-length
hostnames. This avoids the potential lease-loss problems
reffered to above. Also, if a client sends a hostname when
it creates a lease but subsequently sends no or a
zero-length hostname whilst renewing, continue to use the
existing hostname, don't wipe it out.
Tweaked option parsing to flag some parameter errors.
release 2.9
Fixed interface filter code for two effects: 1) Fixed bug
where queries sent via loopback interface
but to the address of another interface were ignored
unless the loopback interface was explicitly configured.
2) on OpenBSD failure to configure one interface now
causes a fatal error on startup rather than an huge
stream of log messages. Thanks to Erik Jan Tromp for
finding that bug.
Changed server selection strategy to improve performance
when there are many available servers and some are
broken. The new algorithm is to pick as before for the
first try, but if a query is retried, to send to all
available servers in parallel. The first one to reply
then becomes prefered for the next query. This should
improve reliability without generating significant extra
upstream load.
Fixed breakage of special servers/addresses for
unqualified domains introduced in version 2.8
Allow fallback to "bind-interfaces" at runtime: Some
verions of *BSD seem to have enough stuff in the header
files to build but no kernel support. Also now log if
"bind-interfaces" is forced on.
Log replies from upstream servers which refuse to do
recursion - dnsmasq is not a recursive nameserver and
relies on upstream servers to do the recursion, this
flags a configuration error.
Disable client-id matching for hosts whose MAC address is
read from /etc/ethers. Patch from Oleg I. Vdovikin.
Extended --mx-host flag to allow arbitrary targets for MX
records, suggested by Moritz Bunkus.
Fixed build under NetBSD 2.0 - thanks to Felix Deichmann
for the patch.
Deal correctly with repeated addresses in /etc/hosts. The
first name found is now returned for reverse lookups,
rather than all of them.
Add back fatal errors when nonexistant
interfaces or interface addresses are given but only in
"bind-interfaces" mode. Principle of least surprise applies.
Allow # as the argument to --domain, meaning "read the
domain from the first search directive in
/etc.resolv.conf". Feature suggested by Evan Jones.

55
FAQ
View File

@@ -206,19 +206,66 @@ A: What is happening is this: The boot process sends a DHCP
the MAC address has a static allocation, that address is still in
use by the first incarnation of the machine (the one from the boot,
without a client ID.) dnsmasq therefore has to give the machine a
dynamic address from its pool. There are two ways to solve this:
dynamic address from its pool. There are three ways to solve this:
(1) persuade your DHCP client not to send a client ID, or (2) set up
the static assignment to the client ID, not the MAC address. The
default client-id will be 01:<MAC address>, so change the dhcp-host
line from "dhcp-host=11:22:33:44:55:66,1.2.3.4" to
"dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4"
"dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4" or (3) tell dnsmasq to
ignore client IDs for a particular MAC address, like this:
dhcp-host=11:22:33:44:55:66,id:*
Q: What network types are supported by the DHCP server?
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
Linux Token Ring is also supported.
Q: What is this strange "bind-interface" option?
A: The DNS spec says that the reply to a DNS query must come from the
same address it was sent to. The traditional way to write an UDP
server to do this is to find all of the addresses belonging to the
machine (ie all the interfaces on the machine) and then create a
socket for each interface which is bound to the address of the
interface. Then when a packet is sent to address A, it is received
on the socket bound to address A and when the reply is also sent
via that socket, the source address is set to A by the kernel and
everything works. This is the how dnsmasq works when
"bind-interfaces" is set, with the obvious extension that is misses
out creating sockets for some interfaces depending on the
--interface, --address and --except-interface flags. The
disadvantage of this approach is that it breaks if interfaces don't
exist or are not configured when the daemon starts and does the
socket creation step. In a hotplug-aware world this is a real
problem.
The alternative approach is to have only one socket, which is bound
to the correct port and the wildcard IP address (0.0.0.0). That
socket will receive _all_ packets sent to port 53, no matter what
destination address they have. This solves the problem of
interfaces which are created or reconfigured after daemon
start-up. To make this work is more complicated because of the
"reply source address" problem. When a UDP packet is sent by a
socket bound to 0.0.0.0 its source address will be set to the
address of one of the machine's interfaces, but which one is not
determined and can vary depending on the OS being run. To get round
this it is neccessary to use a scary advanced API to determine the
address to which a query was sent, and force that to be the source
address in the reply. For IPv4 this stuff in non-portable and quite
often not even available (It's different between FreeBSD 5.x and
Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
have it at all.) Hence "bind-interfaces" has to always be available
as a fall back. For IPv6 the API is standard and universally
available.
It could be argued that if the --interface or --address flags are
used then binding interfaces is more appropriate, but using
wildcard binding means that dnsmasq will quite happily start up
after being told to use interfaces which don't exist, but which are
created later. Wildcard binding breaks the scenario when dnsmasq is
listening on one interface and another server (most probably BIND)
is listening on another. It's not possible for BIND to bind to an
(address,port) pair when dnsmasq has bound (wildcard,port), hence
the ability to explicitly turn off wildcard binding.

View File

@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.6
Version: 2.9
Release: 1
Copyright: GPL
Group: System Environment/Daemons

View File

@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.6
Version: 2.9
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers

View File

@@ -120,7 +120,9 @@ option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when
running another nameserver on the same machine or using IP
alias. Specifying interfaces with IP alias automatically turns this
option on.
option on. Note that this only applies to the DNS part of dnsmasq, the
DHCP server always binds the wildcard address in order to receive
broadcast packets.
.TP
.B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
@@ -223,10 +225,17 @@ with the specified IP address which may be IPv4 or IPv6. To give
both IPv4 and IPv6 addresses for a domain, use repeated -A flags.
Note that /etc/hosts and DHCP leases override this for individual
names. A common use of this is to redirect the entire doubleclick.net
domain to some friendly local web server to avoid banner ads.
domain to some friendly local web server to avoid banner ads. The
domain specification works in the same was as for --server, with the
additional facility that /#/ matches any domain. Thus
--address=/#/1.2.3.4 will always return 1.2.3.4 for any query not
answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive.
.TP
.B \-m, --mx-host=<mx name>
Return an MX record named <mx name> pointing to the host specified in the --mx-target switch
.B \-m, --mx-host=<mx name>[,<hostname>]
Return an MX record named <mx name> pointing to the given hostname (if
given), or
the host specified in the --mx-target switch
or, if that switch is not given, the host on which dnsmasq
is running. This is useful for directing mail from systems on a LAN
to a central server.
@@ -283,7 +292,7 @@ addresses given via
.B dhcp-host
or from /etc/ethers will be served.
.TP
.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
.B \-G, --dhcp-host=[[<hwaddr>]|[id:[<client_id>][*]]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this
@@ -305,7 +314,10 @@ hardware addresses to identify hosts by prefixing with 'id:'. Thus:
.B --dhcp-host=id:01:02:03:04,.....
refers to the host with client identifier 01:02:03:04. It is also
allowed to specify the client ID as text, like this:
.B --dhcp-host=id:clientidastext,.....
.B --dhcp-host=id:clientidastext,.....
The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes
but not others.
If a name appears in /etc/hosts, the associated address can be
allocated to a DHCP lease, but only if a
.B --dhcp-host
@@ -340,7 +352,7 @@ specfied in RFC2132. For example, to set the default route option to
192.168.4.4, do
.B --dhcp-option=3,192.168.4.4
and to set the time-server address to 192.168.0.4, do
.B dhcp-option=42,192.168.0.4
.B --dhcp-option=42,192.168.0.4
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
@@ -353,6 +365,27 @@ possible to generate the correct data type; it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
of this flag.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Map from a vendor-class string to a network id. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option
maps vendor classes to network ids, so that DHCP options may be selectively delivered
to different classes of hosts. For example
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
will allow options to be set only for HP printers like so:
.B --dhcp-option=printers,3,192.168.4.4
The vendor-class string is
substring matched against the vendor-class supplied by the client, to
allow fuzzy matching.
.TP
.B \-j, --dhcp-userclass=<network-id>,<user-class>
Map from a user-class string to a network id (with substring
matching, like vendor classes). Most DHCP clients provide a
"user class" which is configurable. This option
maps user classes to network ids, so that DHCP options may be selectively delivered
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>]]
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
@@ -380,7 +413,9 @@ for DHCP-configured hosts to claim. The intention is to constrain hostnames so t
.B --domain-suffix=thekelleys.org.uk
and have a machine whose DHCP hostname is "laptop". The IP address for that machine is available from
.B dnsmasq
both as "laptop" and "laptop.thekelleys.org.uk".
both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
given as "#" then the domain is read from the first "search" directive
in /etc/resolv.conf (or equivalent).
.TP
.B \-E, --expand-hosts
Add the domain-suffix to simple names (without a period) in /etc/hosts

View File

@@ -161,10 +161,24 @@ filterwin2k
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,net:red
# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=red,Linux
# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"
#dhcp-userclass=red,accounts
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep

View File

@@ -18,8 +18,9 @@ connected to the internet via a modem, cable-modem or ADSL
connection but would be a good choice for any small network where low
resource use and ease of configuration are important.
<P>
Dnsmasq is included in at least the following Linux distributions: Gentoo, Debian,
Smoothwall, IP-Cop, floppyfw, Firebox, Freesco and
Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse,
Smoothwall, IP-Cop, floppyfw, Firebox, Freesco, CoyoteLinux and
Clarkconnect. It is also available as a FreeBSD port and is used in Linksys wireless routers.
<P>
Dnsmasq provides the following features:
@@ -86,7 +87,7 @@ in the .com and .net TLDs
<H2>Download.</H2>
Download dnsmasq <A HREF="http://www.thekelleys.org.uk/dnsmasq/"> here</A>.
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
The tarball includes this documentation, source, manpage and control files for building .rpms.
There are also pre-built i386 .rpms, and a
<A HREF="CHANGELOG"> CHANGELOG</A>.

View File

@@ -472,19 +472,21 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) &&
if (lookup && (lookup->flags & F_HOSTS) &&
memcmp(&lookup->addr, addr, addrlen) == 0)
free(cache);
else
{
/* Ensure there is only one address -> name mapping (first one trumps) */
if (cache_find_by_addr(NULL, addr, 0, flags & (F_IPV4 | F_IPV6)))
flags &= ~F_REVERSE;
cache->flags = flags;
memcpy(&cache->addr, addr, addrlen);
cache_hash(cache);
}
}
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, unsigned short addn_flag)
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int is_addn)
{
FILE *f = fopen(filename, "r");
char *line;
@@ -529,6 +531,9 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
else
continue;
if (is_addn)
flags |= F_ADDN;
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
{
struct crec *cache;
@@ -543,16 +548,12 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
strcpy(cache->name.sname, token);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
/* Only first name is cannonical and used for reverse lookups */
flags &= ~F_REVERSE;
add_hosts_entry(cache, &addr, addrlen, flags);
}
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
/* Clear this here in case not done above. */
flags &= ~F_REVERSE;
add_hosts_entry(cache, &addr, addrlen, flags);
}
}
else
@@ -604,7 +605,7 @@ void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
if (addn_hosts)
{
read_hostsfile(addn_hosts, opts, buff, domain_suffix, F_ADDN);
read_hostsfile(addn_hosts, opts, buff, domain_suffix, 1);
addn_file = addn_hosts;
}
}

View File

@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.6"
#define VERSION "2.9"
#define FTABSIZ 150 /* max number of outstanding requests */
#define TIMEOUT 20 /* drop queries after TIMEOUT seconds */
@@ -59,17 +59,6 @@
#endif
/* determine if we can find the destination address of recieved packets
and set the source address of sent ones. If so, we can use one socket
bound to INADDR_ANY and cope with dynamically created interfaces.
Linux does this differently to FreeBSD. */
#if defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
# define HAVE_UDP_SRC_DST
#else
# undef HAVE_UDP_SRC_DST
#endif
/* Decide if we're going to support IPv6 */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */

View File

@@ -60,7 +60,8 @@ void dhcp_init(int *fdp, int* rfdp)
}
void dhcp_packet(struct dhcp_context *contexts, char *packet,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
struct dhcp_vendor *vendors,
time_t now, char *namebuff, char *domain_suffix,
char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
@@ -128,13 +129,9 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
return;
#else
if (!names || !names->name || names->next)
{
syslog(LOG_ERR, "must set exactly one interface on broken systems without IP_RECVIF");
return;
}
else
strcpy(ifr.ifr_name, names->name);
while (names->isloop)
names = names->next;
strcpy(ifr.ifr_name, names->name);
#endif
#ifdef HAVE_BPF
@@ -181,7 +178,7 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
/* we can use the interface netmask if either the packet came direct,
or it came via a relay listening on the same network. This sounds unlikely,
but it happens with win4lin. */
if ((source.s_addr & iface_netmask.s_addr) != (iface_addr.s_addr & iface_netmask.s_addr))
if (!is_same_net(source, iface_addr, iface_netmask))
iface_netmask.s_addr = 0;
else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
@@ -193,8 +190,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask;
if (netmask.s_addr &&
(source.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr) &&
(source.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr))
is_same_net(source, context->start, netmask) &&
is_same_net(source, context->end, netmask))
break;
}
@@ -225,7 +222,7 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
DHCP broadcast, either this machine or a relay. In the special case that the relay
is on the same network as us, we set the default route to us, not the relay.
This is the win4lin scenario again. */
if ((source.s_addr & context->netmask.s_addr) == (iface_addr.s_addr & context->netmask.s_addr))
if (is_same_net(source, iface_addr, context->netmask))
router = iface_addr;
else
router = source;
@@ -233,8 +230,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
lease_prune(NULL, now); /* lose any expired leases */
newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu,
rawpacket, sz, now, namebuff,
dhcp_opts, dhcp_configs, domain_suffix, dhcp_file,
dhcp_sname, dhcp_next_server, router);
dhcp_opts, dhcp_configs, vendors, domain_suffix,
dhcp_file, dhcp_sname, dhcp_next_server, router);
lease_update_file(0, now);
lease_update_dns();
@@ -350,7 +347,6 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
}
}
int address_available(struct dhcp_context *context, struct in_addr taddr)
{
/* Check is an address is OK for this network, ie
@@ -379,38 +375,47 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
}
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
struct in_addr *addrp)
struct in_addr *addrp, unsigned char *hwaddr)
{
/* Find a free address: exlude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration */
struct dhcp_config *config;
struct in_addr start = context->last;
struct in_addr start, addr ;
int i, j;
/* start == end means no dynamic leases. */
if (context->end.s_addr == context->start.s_addr)
return 0;
/* pick a seed based on hwaddr then iterate until we find a free address. */
for (j = 0, i = 0; i < ETHER_ADDR_LEN; i++)
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
start.s_addr = addr.s_addr =
htonl(ntohl(context->start.s_addr) +
(j % (ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
do {
if (context->last.s_addr == context->end.s_addr)
context->last = context->start;
if (addr.s_addr == context->end.s_addr)
addr = context->start;
else
context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
if (!lease_find_by_addr(context->last))
if (!lease_find_by_addr(addr))
{
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == context->last.s_addr)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
break;
if (!config)
{
*addrp = context->last;
*addrp = addr;
return 1;
}
}
} while (context->last.s_addr != start.s_addr);
} while (addr.s_addr != start.s_addr);
return 0;
}
@@ -421,7 +426,7 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
return 1;
if (!(config->flags & CONFIG_ADDR))
return 1;
if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
if (is_same_net(config->addr, context->start, context->netmask))
return 1;
return 0;
@@ -563,7 +568,7 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
config->addr = addr;
}
config->flags |= CONFIG_HWADDR;
config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
count++;

View File

@@ -37,15 +37,16 @@ int main (int argc, char **argv)
int maxleases = MAXLEASES;
int query_port = 0;
int first_loop = 1;
int bind_fallback = 0;
unsigned long local_ttl = 0;
unsigned int options, min_leasetime;
char *runfile = RUNFILE;
time_t resolv_changed = 0;
time_t now, last = 0;
struct irec *interfaces = NULL;
struct listener *listener, *listeners;
struct listener *listener, *listeners = NULL;
struct doctor *doctors = NULL;
char *mxname = NULL;
struct mx_record *mxnames = NULL;
char *mxtarget = NULL;
char *lease_file = NULL;
char *addn_hosts = NULL;
@@ -66,6 +67,7 @@ int main (int argc, char **argv)
struct dhcp_context *dhcp_tmp, *dhcp = NULL;
struct dhcp_config *dhcp_configs = NULL;
struct dhcp_opt *dhcp_options = NULL;
struct dhcp_vendor *dhcp_vendors = NULL;
char *dhcp_file = NULL, *dhcp_sname = NULL;
struct in_addr dhcp_next_server;
int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
@@ -105,20 +107,14 @@ int main (int argc, char **argv)
packet = safe_malloc(DNSMASQ_PACKETSZ);
dhcp_next_server.s_addr = 0;
options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file,
&username, &groupname, &domain_suffix, &runfile,
&if_names, &if_addrs, &if_except, &bogus_addr,
&serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
&dhcp, &dhcp_configs, &dhcp_options,
&dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors,
&dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
&doctors);
/* if we cannot support binding the wildcard address, set the "bind only
interfaces in use" option */
#ifndef HAVE_UDP_SRC_DST
options |= OPT_NOWILD;
#endif
if (!lease_file)
{
if (dhcp)
@@ -129,12 +125,41 @@ int main (int argc, char **argv)
die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
#endif
interfaces = enumerate_interfaces(if_names, if_addrs, if_except, port);
if (options & OPT_NOWILD)
listeners = create_bound_listeners(interfaces);
else
listeners = create_wildcard_listeners(port);
interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port);
if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port)))
{
bind_fallback = 1;
options |= OPT_NOWILD;
}
if (options & OPT_NOWILD)
{
struct iname *if_tmp;
listeners = create_bound_listeners(interfaces);
for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
die("unknown interface %s", if_tmp->name);
for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used)
{
char addrbuff[ADDRSTRLEN];
#ifdef HAVE_IPV6
if (if_tmp->addr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
addrbuff, ADDRSTRLEN);
else
inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
#endif
die("no interface with address %s", addrbuff);
}
}
forward_init(1);
cache_init(cachesize, options & OPT_LOG);
@@ -147,6 +172,15 @@ int main (int argc, char **argv)
if (dhcp)
{
#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
int c;
struct iname *tmp;
for (c = 0, tmp = if_names; tmp; tmp = tmp->next)
if (!tmp->isloop)
c++;
if (c != 1)
die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
#endif
dhcp_init(&dhcpfd, &dhcp_raw_fd);
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
}
@@ -227,12 +261,9 @@ int main (int argc, char **argv)
else
syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
if (options & OPT_LOCALMX)
syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
else if (mxname)
syslog(LOG_INFO, "serving MX record for mailhost %s target %s",
mxname, mxtarget);
if (bind_fallback)
syslog(LOG_WARNING, "setting --bind-interfaces option because if OS limitations");
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
{
strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
@@ -255,8 +286,9 @@ int main (int argc, char **argv)
if (getuid() == 0 || geteuid() == 0)
syslog(LOG_WARNING, "failed to drop root privs");
servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
servers = check_servers(serv_addrs, interfaces, &sfds);
last_server = NULL;
while (sigterm == 0)
{
fd_set rset;
@@ -274,9 +306,11 @@ int main (int argc, char **argv)
lease_update_dns();
}
if (resolv && (options & OPT_NO_POLL))
servers = last_server =
check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
interfaces, &sfds);
{
servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
interfaces, &sfds);
last_server = NULL;
}
sighup = 0;
}
@@ -372,7 +406,7 @@ int main (int argc, char **argv)
else
{
res->logged = 0;
if (statbuf.st_mtime > last_change)
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
@@ -381,23 +415,23 @@ int main (int argc, char **argv)
res = res->next;
}
if (latest && last_change > resolv_changed)
if (latest && difftime(last_change, resolv_changed) > 0.0)
{
resolv_changed = last_change;
servers = last_server =
check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
interfaces, &sfds);
servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
interfaces, &sfds);
last_server = NULL;
}
}
}
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, &rset))
last_server = reply_query(serverfdp->fd, options, packet, now,
dnamebuff, last_server, bogus_addr, doctors);
last_server = reply_query(serverfdp, options, packet, now,
dnamebuff, servers, last_server, bogus_addr, doctors);
if (dhcp && FD_ISSET(dhcpfd, &rset))
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs,
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors,
now, dnamebuff, domain_suffix, dhcp_file,
dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
if_names, if_addrs, if_except);
@@ -405,7 +439,7 @@ int main (int argc, char **argv)
for (listener = listeners; listener; listener = listener->next)
if (FD_ISSET(listener->fd, &rset))
last_server = receive_query(listener, packet,
mxname, mxtarget, options, now, local_ttl, dnamebuff,
mxnames, mxtarget, options, now, local_ttl, dnamebuff,
if_names, if_addrs, if_except, last_server, servers);
}

View File

@@ -56,7 +56,7 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#if defined(__OpenBSD__)
#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -91,6 +91,7 @@
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
#define OPT_RESOLV_DOMAIN 32768
struct all_addr {
union {
@@ -112,6 +113,11 @@ struct doctor {
struct doctor *next;
};
struct mx_record {
char *mxname, *mxtarget;
struct mx_record *next;
};
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -130,7 +136,7 @@ struct crec {
};
#define F_IMMORTAL 1
#define F_CONFIG 2
#define F_CONFIG 2
#define F_REVERSE 4
#define F_FORWARD 8
#define F_DHCP 16
@@ -206,6 +212,7 @@ struct listener {
struct iname {
char *name;
union mysockaddr addr;
int isloop, used;
struct iname *next;
};
@@ -237,12 +244,18 @@ struct dhcp_lease {
struct dhcp_lease *next;
};
struct dhcp_netid {
char *net;
struct dhcp_netid *next;
};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
unsigned char hwaddr[ETHER_ADDR_LEN];
char *hostname, *netid;
char *hostname;
struct dhcp_netid netid;
struct in_addr addr;
unsigned int lease_time;
struct dhcp_config *next;
@@ -255,19 +268,27 @@ struct dhcp_config {
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
struct dhcp_opt {
int opt, len, is_addr;
unsigned char *val;
char *netid;
struct dhcp_opt *next;
};
};
struct dhcp_vendor {
int len, is_vendor, used;
char *data;
struct dhcp_netid netid;
struct dhcp_vendor *next;
};
struct dhcp_context {
unsigned int lease_time;
struct in_addr netmask, broadcast;
struct in_addr start, end, last; /* range of available addresses */
char *netid;
struct in_addr start, end; /* range of available addresses */
struct dhcp_netid netid;
struct dhcp_context *next;
};
@@ -323,7 +344,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
time_t now, struct doctor *doctors);
void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now);
int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now, unsigned long local_ttl,
char *namebuff);
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
@@ -333,6 +354,7 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
unsigned short rand16(void);
int legal_char(char c);
int canonicalise(char *s);
int atoi_check(char *a, int *res);
void die(char *message, char *arg1);
void complain(char *message, char *arg1);
void *safe_malloc(int size);
@@ -341,25 +363,28 @@ int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(unsigned char *a, unsigned char *b);
time_t dnsmasq_time(int fd);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
/* option.c */
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
char **mxname, char **mxtarget, char **lease_file,
struct mx_record **mxnames, char **mxtarget, char **lease_file,
char **username, char **groupname,
char **domain_suffix, char **runfile,
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize,
int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts,
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, struct dhcp_opt **opts,
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf,
struct dhcp_opt **opts, struct dhcp_vendor **dhcp_vendors,
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server,
int *maxleases, unsigned int *min_leasetime, struct doctor **doctors);
/* forward.c */
void forward_init(int first);
struct server *reply_query(int fd, int options, char *packet, time_t now,
char *dnamebuff, struct server *last_server,
struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
char *dnamebuff, struct server *servers, struct server *last_server,
struct bogus_addr *bogus_nxdomain, struct doctor *doctors);
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *namebuff,
struct iname *names, struct iname *addrs, struct iname *except,
@@ -367,8 +392,8 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
/* network.c */
struct server *reload_servers(char *fname, char *buff, struct server *servers, int query_port);
struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds);
struct irec *enumerate_interfaces(struct iname *names,
struct iname *addrs,
struct irec *enumerate_interfaces(struct iname **names,
struct iname **addrs,
struct iname *except,
int port);
struct listener *create_wildcard_listeners(int port);
@@ -377,13 +402,14 @@ struct listener *create_bound_listeners(struct irec *interfaces);
void dhcp_init(int *fdp, int* rfdp);
void dhcp_packet(struct dhcp_context *contexts, char *packet,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
struct dhcp_vendor *vendors,
time_t now, char *namebuff, char *domain_suffix,
char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
struct iname *names, struct iname *addrs, struct iname *except);
int address_available(struct dhcp_context *context, struct in_addr addr);
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
struct in_addr *addrp);
struct in_addr *addrp, unsigned char *hwaddr);
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
@@ -391,6 +417,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_config *read_ethers(struct dhcp_config *configs, char *buff);
void dhcp_update_configs(struct dhcp_config *configs);
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff);
/* lease.c */
void lease_update_file(int force, time_t now);
void lease_update_dns(void);
@@ -404,6 +431,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain);
/* rfc2131.c */
int dhcp_reply(struct dhcp_context *context,
struct in_addr iface_addr,
@@ -412,6 +440,7 @@ int dhcp_reply(struct dhcp_context *context,
struct udp_dhcp_packet *rawpacket,
unsigned int sz, time_t now, char *namebuff,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
struct dhcp_vendor *vendors,
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server, struct in_addr router);

View File

@@ -120,22 +120,27 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
{
struct frec *forward;
char *domain = NULL;
int type = 0;
struct server *serv;
int forwardall = 0, type = 0;
struct all_addr *addrp = NULL;
unsigned short flags = 0;
unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff);
struct server *start = NULL;
/* may be recursion not speced or no servers available. */
if (!header->rd || !servers)
forward = NULL;
else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
{
/* retry on existing query, send to next server */
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
if (!(options & OPT_ORDER))
{
forwardall = 1;
last_server = NULL;
}
type = forward->sentto->flags & SERV_TYPE;
if (!(forward->sentto = forward->sentto->next))
forward->sentto = servers; /* at end of list, recycle */
if (!(start = forward->sentto->next))
start = servers; /* at end of list, recycle */
header->id = htons(forward->new_id);
}
else
@@ -148,60 +153,47 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int namelen = strlen(dnamebuff);
unsigned int matchlen = 0;
struct server *serv;
for (serv=servers; serv; serv=serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.'))
{
if (serv->flags & SERV_LITERAL_ADDRESS)
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
type = SERV_FOR_NODOTS;
flags = 0;
if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & gotname))
{
/* flags gets set if server is in fact an answer */
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
if (sflag & gotname) /* only OK if addrfamily == query */
{
type = SERV_FOR_NODOTS;
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
else
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
}
else
flags = 0;
}
else if (serv->flags & SERV_HAS_DOMAIN)
{
unsigned int domainlen = strlen(serv->domain);
if (namelen >= domainlen &&
hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) &&
domainlen > matchlen)
domainlen >= matchlen)
{
if (serv->flags & SERV_LITERAL_ADDRESS)
{ /* flags gets set if server is in fact an answer */
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */
{
type = SERV_HAS_DOMAIN;
flags = gotname;
domain = serv->domain;
matchlen = domainlen;
if (serv->addr.sa.sa_family == AF_INET)
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
}
else
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
type = SERV_HAS_DOMAIN;
domain = serv->domain;
matchlen = domainlen;
flags = 0;
if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & gotname))
{
flags = 0; /* may be better match from previous literal */
domain = serv->domain;
matchlen = domainlen;
type = SERV_HAS_DOMAIN;
flags = gotname;
if (serv->addr.sa.sa_family == AF_INET)
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
}
}
@@ -231,10 +223,13 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
otherwise, use the one last known to work. */
if (type != 0 || (options & OPT_ORDER))
forward->sentto = servers;
else
forward->sentto = last_server;
start = servers;
else if (!(start = last_server))
{
start = servers;
forwardall = 1;
}
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->new_id = get_id();
@@ -250,52 +245,52 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
if (!flags && forward)
{
struct server *firstsentto = forward->sentto;
struct server *firstsentto = start;
int forwarded = 0;
while (1)
{
int logflags = 0;
if (forward->sentto->addr.sa.sa_family == AF_INET)
{
logflags = F_SERVER | F_IPV4 | F_FORWARD;
addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
}
#ifdef HAVE_IPV6
else
{
logflags = F_SERVER | F_IPV6 | F_FORWARD;
addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
}
#endif
/* only send to servers dealing with our domain.
domain may be NULL, in which case server->domain
must be NULL also. */
if (type == (forward->sentto->flags & SERV_TYPE) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
if (type == (start->flags & SERV_TYPE) &&
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
{
if (forward->sentto->flags & SERV_NO_ADDR)
if (start->flags & SERV_NO_ADDR)
flags = F_NOERR; /* NULL servers are OK. */
else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
&forward->sentto->addr.sa,
sa_len(&forward->sentto->addr)) != -1)
else if (!(start->flags & SERV_LITERAL_ADDRESS) &&
sendto(start->sfd->fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr)) != -1)
{
log_query(logflags, gotname ? dnamebuff : "query", addrp);
/* for no-domain, don't update last_server */
return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
if (!gotname)
strcpy(dnamebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff,
(struct all_addr *)&start->addr.in.sin_addr);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff,
(struct all_addr *)&start->addr.in6.sin6_addr);
#endif
forwarded = 1;
forward->sentto = start;
if (!forwardall)
break;
}
}
if (!(forward->sentto = forward->sentto->next))
forward->sentto = servers;
if (!(start = start->next))
start = servers;
/* check if we tried all without success */
if (forward->sentto == firstsentto)
if (start == firstsentto)
break;
}
if (forwarded)
return last_server;
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->new_id = 0; /* cancel */
@@ -312,52 +307,81 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
}
/* returns new last_server */
struct server *reply_query(int fd, int options, char *packet, time_t now,
char *dnamebuff, struct server *last_server,
struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
char *dnamebuff, struct server *servers, struct server *last_server,
struct bogus_addr *bogus_nxdomain, struct doctor *doctors)
{
/* packet from peer server, extract data for cache, and send to
original requester */
struct frec *forward;
HEADER *header;
int n = recv(fd, packet, PACKETSZ, 0);
union mysockaddr serveraddr;
socklen_t addrlen = sizeof(serveraddr);
int n = recvfrom(sfd->fd, packet, PACKETSZ, 0, &serveraddr.sa, &addrlen);
/* Determine the address of the server replying so that we can mark that as good */
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET6)
serveraddr.in6.sin6_flowinfo = htonl(0);
#endif
header = (HEADER *)packet;
if (n >= (int)sizeof(HEADER) && header->qr)
if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
{
if ((forward = lookup_frec(ntohs(header->id))))
/* find good server by address if possible, otherwise assume the last one we sent to */
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
{
if (!forward->sentto->domain)
last_server = forward->sentto; /* known good */
if (header->opcode == QUERY)
{
if (!(bogus_nxdomain &&
header->rcode == NOERROR &&
check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
{
if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
else if (!(options & OPT_NO_NEG))
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
}
}
}
header->id = htons(forward->orig_id);
/* There's no point returning an upstream reply marked as truncated,
since that will prod the resolver into moving to TCP - which we
don't support. */
header->tc = 0; /* goodbye truncate */
send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
forward->new_id = 0; /* cancel */
for (last_server = servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
break;
if (!last_server)
last_server = forward->sentto;
}
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
{
char addrbuff[ADDRSTRLEN];
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &serveraddr.in.sin_addr, addrbuff, ADDRSTRLEN);
else if (serveraddr.sa.sa_family == AF_INET6)
inet_ntop(AF_INET6, &serveraddr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(serveraddr.in.sin_addr));
#endif
syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
return NULL;
}
if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY)
{
if (!(bogus_nxdomain &&
header->rcode == NOERROR &&
check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
{
if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
else if (!(options & OPT_NO_NEG))
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
}
}
header->id = htons(forward->orig_id);
/* There's no point returning an upstream reply marked as truncated,
since that will prod the resolver into moving to TCP - which we
don't support. */
header->tc = 0; /* goodbye truncate */
send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
forward->new_id = 0; /* cancel */
}
return last_server;
}
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *namebuff,
struct iname *names, struct iname *addrs, struct iname *except,
@@ -395,7 +419,8 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
msg.msg_iov = iov;
msg.msg_iovlen = 1;
n = recvmsg(listen->fd, &msg, 0);
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return last_server;
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
@@ -502,7 +527,7 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
mxname, mxtarget, options, now, local_ttl, namebuff);
mxnames, mxtarget, options, now, local_ttl, namebuff);
if (m >= 1)
send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr);
else

View File

@@ -109,7 +109,7 @@ void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
for (lease = leases; lease; lease = lease->next)
if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) &&
(config->hostname))
(config->flags & CONFIG_NAME))
lease_set_hostname(lease, config->hostname, domain);
}
@@ -145,7 +145,7 @@ void lease_update_file(int force, time_t now)
expires, lease->hwaddr[0], lease->hwaddr[1],
lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
lease->hwaddr[5], inet_ntoa(lease->addr),
lease->hostname ? lease->hostname : "*");
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
if (lease->clid_len)
{
@@ -311,7 +311,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
if (lease->hostname && name && strcmp(lease->hostname, name) == 0)
if (lease->hostname && name && hostname_isequal(lease->hostname, name))
return;
if (!name && !lease->hostname)

View File

@@ -25,44 +25,47 @@ static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *a
if (except)
for (tmp = except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0)
return NULL;
return list;
/* we may need to check the whitelist */
if (names || addrs)
{
int found = 0;
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
break;
if (!tmp)
for (tmp = addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
break;
if (!tmp)
return NULL;
found = tmp->used = 1;
for (tmp = addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
found = tmp->used = 1;
if (!found)
return list;
}
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address */
for (; list; list = list->next)
if (sockaddr_isequal(&list->addr, addr))
for (iface = list; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
break;
if (list)
return NULL;
if (iface)
return list;
/* If OK, add it to the head of the list */
iface = safe_malloc(sizeof(struct irec));
iface->addr = *addr;
iface->next = list;
return iface;
}
struct irec *enumerate_interfaces(struct iname *names,
struct iname *addrs,
struct irec *enumerate_interfaces(struct iname **names,
struct iname **addrs,
struct iname *except,
int port)
{
struct irec *iface = NULL, *new;
struct irec *iface = NULL;
char *buf, *ptr;
struct ifreq *ifr = NULL;
struct ifconf ifc;
@@ -72,7 +75,7 @@ struct irec *enumerate_interfaces(struct iname *names,
if (fd == -1)
die ("cannot create socket to enumerate interfaces: %s", NULL);
while (1)
{
buf = safe_malloc(len);
@@ -138,21 +141,27 @@ struct irec *enumerate_interfaces(struct iname *names,
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (names && (ifr->ifr_flags & IFF_LOOPBACK))
if (*names && (ifr->ifr_flags & IFF_LOOPBACK))
{
struct iname *lo = safe_malloc(sizeof(struct iname));
lo->name = safe_string_alloc(ifr->ifr_name);
lo->next = names->next;
names->next = lo;
}
if ((new = add_iface(iface, ifr->ifr_name,
&addr, names, addrs, except)))
{
new->next = iface;
iface = new;
struct iname *lo;
for (lo = *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 = *names;
*names = lo;
}
}
iface = add_iface(iface, ifr->ifr_name, &addr, *names, *addrs, except);
#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
@@ -194,12 +203,8 @@ struct irec *enumerate_interfaces(struct iname *names,
fclose(f);
}
if (found && (new = add_iface(iface, ifr->ifr_name,
&addr6, names, addrs, except)))
{
new->next = iface;
iface = new;
}
if (found)
iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except);
}
#endif /* LINUX */
}
@@ -217,6 +222,9 @@ struct irec *enumerate_interfaces(struct iname *names,
struct listener *create_wildcard_listeners(int port)
{
#if !(defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
return NULL;
#else
union mysockaddr addr;
int opt = 1;
struct listener *listen;
@@ -232,7 +240,10 @@ struct listener *create_wildcard_listeners(int port)
#endif
listen = safe_malloc(sizeof(struct listener));
if ((listen->fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
die("failed to create socket: %s", NULL);
{
free(listen);
return NULL;
}
if (setsockopt(listen->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
#if defined(IP_PKTINFO)
setsockopt(listen->fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
@@ -241,7 +252,11 @@ struct listener *create_wildcard_listeners(int port)
setsockopt(listen->fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(listen->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
die("failed to bind socket: %s", NULL);
{
close(listen->fd);
free(listen);
return NULL;
}
listen->next = NULL;
listen->family = AF_INET;
#ifdef HAVE_IPV6
@@ -257,7 +272,11 @@ struct listener *create_wildcard_listeners(int port)
if (errno != EPROTONOSUPPORT &&
errno != EAFNOSUPPORT &&
errno != EINVAL)
die("failed to create IPv6 socket: %s", NULL);
{
close(listen->fd);
free(listen);
return NULL;
}
}
else
{
@@ -268,11 +287,19 @@ struct listener *create_wildcard_listeners(int port)
if (setsockopt(listen->next->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(listen->next->fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
bind(listen->next->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
die("failed to bind IPv6 socket: %s", NULL);
{
close(listen->next->fd);
free(listen->next);
close(listen->fd);
free(listen);
return NULL;
}
}
#endif
return listen;
#endif
}
struct listener *create_bound_listeners(struct irec *interfaces)
@@ -452,7 +479,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
if (!token || strcmp(token, "nameserver") != 0)
continue;
if (!(token = strtok(NULL, " \t\n")))
if (!(token = strtok(NULL, " \t\n\r")))
continue;
#ifdef HAVE_IPV6

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2003 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
@@ -21,7 +21,7 @@ struct myoption {
int val;
};
#define OPTSTRING "ZDNLERzowefnbvhdqr: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:"
#define OPTSTRING "ZDNLERzowefnbvhdqr: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:"
static struct myoption opts[] = {
{"version", 0, 0, 'v'},
@@ -70,6 +70,8 @@ static struct myoption opts[] = {
{"bind-interfaces", 0, 0, 'z'},
{"read-ethers", 0, 0, 'Z' },
{"alias", 1, 0, 'V' },
{"dhcp-vendorclass", 1, 0, 'U'},
{"dhcp-userclass", 1, 0, 'j'},
{0, 0, 0, 0}
};
@@ -120,6 +122,7 @@ static char *usage =
"-H, --addn-hosts=path Specify a hosts file to be read in addition to " HOSTSFILE ".\n"
"-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"
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
"-L, --localmx Return MX records for local hosts.\n"
"-m, --mx-host=host_name Specify the MX name to reply to.\n"
@@ -139,6 +142,7 @@ static char *usage =
"-t, --mx-target=host_name Specify the host in an MX reply.\n"
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
"-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n"
"-v, --version Display dnsmasq version.\n"
"-V, --alias=addr,addr,mask Translate IPv4 addresses from upstream servers.\n"
"-w, --help Display this message.\n"
@@ -150,12 +154,12 @@ static char *usage =
unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **resolv_files,
char **mxname, char **mxtarget, char **lease_file,
struct mx_record **mxnames, char **mxtarget, char **lease_file,
char **username, char **groupname, char **domain_suffix, char **runfile,
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port,
int *query_port, unsigned long *local_ttl, char **addn_hosts, struct dhcp_context **dhcp,
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, char **dhcp_file,
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, int *dhcp_max,
unsigned int *min_leasetime, struct doctor **doctors)
{
@@ -342,11 +346,22 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
case 'm':
if (!canonicalise(optarg))
option = '?';
else
*mxname = safe_string_alloc(optarg);
break;
{
char *comma = strchr(optarg, ',');
if (comma)
*(comma++) = 0;
if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
option = '?';
else
{
struct mx_record *new = safe_malloc(sizeof(struct mx_record));
new->next = *mxnames;
*mxnames = new;
new->mxname = safe_string_alloc(optarg);
new->mxtarget = safe_string_alloc(comma); /* may be NULL */
}
break;
}
case 't':
if (!canonicalise(optarg))
@@ -367,7 +382,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
break;
case 's':
if (!canonicalise(optarg))
if (strcmp (optarg, "#") == 0)
flags |= OPT_RESOLV_DOMAIN;
else if (!canonicalise(optarg))
option = '?';
else
*domain_suffix = safe_string_alloc(optarg);
@@ -389,6 +406,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* new->name may be NULL if someone does
"interface=" to disable all interfaces except loop. */
new->name = safe_string_alloc(optarg);
new->isloop = new->used = 0;
if (strchr(optarg, ':'))
flags |= OPT_NOWILD;
break;
@@ -472,11 +490,15 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
optarg++;
while ((end = strchr(optarg, '/')))
{
char *domain;
char *domain = NULL;
*end = 0;
if (!canonicalise(optarg))
/* # matches everything and becomes a zero length domain string */
if (strcmp(optarg, "#") == 0)
domain = "";
else if (!canonicalise(optarg) && strlen(optarg) != 0)
option = '?';
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
else
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
serv = safe_malloc(sizeof(struct server));
serv->next = newlist;
newlist = serv;
@@ -525,14 +547,16 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if ((portno = strchr(source+1, '#')))
{
*portno = 0;
source_port = atoi(portno+1);
if (!atoi_check(portno+1, &source_port))
option = '?';
}
}
if ((portno = strchr(optarg, '#'))) /* is there a port no. */
{
*portno = 0;
serv_port = atoi(portno+1);
if (!atoi_check(portno+1, &serv_port))
option = '?';
}
#ifdef HAVE_IPV6
@@ -612,32 +636,46 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case 'c':
{
int size = atoi(optarg);
/* zero is OK, and means no caching. */
if (size < 0)
size = 0;
else if (size > 10000)
size = 10000;
*cachesize = size;
int size;
if (!atoi_check(optarg, &size))
option = '?';
else
{
/* zero is OK, and means no caching. */
if (size < 0)
size = 0;
else if (size > 10000)
size = 10000;
*cachesize = size;
}
break;
}
case 'p':
*port = atoi(optarg);
if (!atoi_check(optarg, port))
option = '?';
break;
case 'Q':
*query_port = atoi(optarg);
if (!atoi_check(optarg, query_port))
option = '?';
break;
case 'T':
*local_ttl = (unsigned long)atoi(optarg);
break;
{
int ttl;
if (!atoi_check(optarg, &ttl))
option = '?';
else
*local_ttl = (unsigned long)ttl;
break;
}
case 'X':
*dhcp_max = atoi(optarg);
if (!atoi_check(optarg, dhcp_max))
option = '?';
break;
case 'F':
@@ -650,7 +688,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
new->lease_time = DEFLEASE;
new->netmask.s_addr = 0;
new->broadcast.s_addr = 0;
new->netid = NULL;
new->netid.net = NULL;
for (cp = optarg; *cp; cp++)
@@ -660,7 +698,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if (*cp != ',' && (comma = strchr(optarg, ',')))
{
*comma = 0;
new->netid = safe_string_alloc(optarg);
new->netid.net = safe_string_alloc(optarg);
a[0] = comma + 1;
}
else
@@ -726,7 +764,6 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
}
new->last = new->start;
if (new->lease_time < *min_leasetime)
*min_leasetime = new->lease_time;
break;
@@ -760,40 +797,45 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
(arg[1] == 'd' || arg[1] == 'D') &&
arg[2] == ':')
{
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
{
/* decode hex in place */
char *p = arg, *q = arg, *r;
while (*p)
{
for (r = p; *r && *r != ':'; r++);
if (*r)
{
if (r != p)
{
*r = 0;
*(q++) = strtol(p, NULL, 16);
}
p = r+1;
}
else
{
if (*p)
*(q++) = strtol(p, NULL, 16);
break;
}
}
len = q - arg;
}
if (arg[3] == '*')
new->flags |= CONFIG_NOCLID;
else
len = strlen(arg);
new->flags |= CONFIG_CLID;
new->clid_len = len;
new->clid = safe_malloc(len);
memcpy(new->clid, arg, len);
{
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
{
/* decode hex in place */
char *p = arg, *q = arg, *r;
while (*p)
{
for (r = p; *r && *r != ':'; r++);
if (*r)
{
if (r != p)
{
*r = 0;
*(q++) = strtol(p, NULL, 16);
}
p = r+1;
}
else
{
if (*p)
*(q++) = strtol(p, NULL, 16);
break;
}
}
len = q - arg;
}
else
len = strlen(arg);
new->flags |= CONFIG_CLID;
new->clid_len = len;
new->clid = safe_malloc(len);
memcpy(new->clid, arg, len);
}
}
else if ((arg[0] == 'n' || arg[0] == 'N') &&
(arg[1] == 'e' || arg[1] == 'E') &&
@@ -801,7 +843,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
arg[3] == ':')
{
new->flags |= CONFIG_NETID;
new->netid = safe_string_alloc(arg+4);
new->netid.net = safe_string_alloc(arg+4);
}
else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
&e0, &e1, &e2, &e3, &e4, &e5) == 6)
@@ -882,7 +924,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if (new->flags & CONFIG_CLID)
free(new->clid);
if (new->flags & CONFIG_NETID)
free(new->netid);
free(new->netid.net);
free(new);
}
else
@@ -925,6 +967,8 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if ((new->opt = atoi(optarg)) == 0)
{
option = '?';
if (new->netid)
free(new->netid);
free(new);
break;
}
@@ -954,7 +998,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
is_dec = is_addr = 0;
if (!((*cp >='A' && *cp <= 'F') ||
(*cp >='a' && *cp <= 'F')))
(*cp >='a' && *cp <= 'f')))
is_hex = 0;
}
@@ -1057,6 +1101,28 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
break;
}
case 'U':
case 'j':
{
char *comma;
if (!(comma = strchr(optarg, ',')))
option = '?';
else
{
struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor));
*comma = 0;
new->netid.net = safe_string_alloc(optarg);
new->len = strlen(comma+1);
new->data = safe_malloc(new->len);
memcpy(new->data, comma+1, new->len);
new->is_vendor = (option == 'U');
new->next = *dhcp_vendors;
*dhcp_vendors = new;
}
break;
}
case 'V':
{
char *a[3] = { NULL, NULL, NULL };
@@ -1139,13 +1205,18 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* only one of these need be specified: the other defaults to the
host-name */
if ((flags & OPT_LOCALMX) || *mxname || *mxtarget)
if ((flags & OPT_LOCALMX) || *mxnames || *mxtarget)
{
if (gethostname(buff, MAXDNAME) == -1)
die("cannot get host-name: %s", NULL);
if (!*mxname)
*mxname = safe_string_alloc(buff);
if (!*mxnames)
{
*mxnames = safe_malloc(sizeof(struct mx_record));
(*mxnames)->next = NULL;
(*mxnames)->mxtarget = NULL;
(*mxnames)->mxname = safe_string_alloc(buff);
}
if (!*mxtarget)
*mxtarget = safe_string_alloc(buff);
@@ -1155,7 +1226,36 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*resolv_files = 0;
else if (*resolv_files && (*resolv_files)->next && (flags & OPT_NO_POLL))
die("only one resolv.conf file allowed in no-poll mode.", NULL);
if (flags & OPT_RESOLV_DOMAIN)
{
char *line;
if (!*resolv_files || (*resolv_files)->next)
die("must have exactly one resolv.conf to read domain from.", NULL);
if (!(f = fopen((*resolv_files)->name, "r")))
die("failed to read %s: %m", (*resolv_files)->name);
while ((line = fgets(buff, MAXDNAME, f)))
{
char *token = strtok(line, " \t\n\r");
if (!token || strcmp(token, "search") != 0)
continue;
if ((token = strtok(NULL, " \t\n\r")) &&
canonicalise(token) &&
(*domain_suffix = safe_string_alloc(token)))
break;
}
fclose(f);
if (!*domain_suffix)
die("no search directive found in %s", (*resolv_files)->name);
}
return flags;
}

View File

@@ -443,7 +443,7 @@ void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now
static void dns_doctor(struct doctor *doctor, struct in_addr *addr)
{
for (; doctor; doctor = doctor->next)
if ((doctor->in.s_addr & doctor->mask.s_addr) == (addr->s_addr & doctor->mask.s_addr))
if (is_same_net(doctor->in, *addr, doctor->mask))
{
addr->s_addr &= ~doctor->mask.s_addr;
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
@@ -728,7 +728,7 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
}
/* return zero if we can't answer from cache, or packet size if we can */
int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *name)
{
@@ -988,9 +988,14 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
if (qtype == T_MX || qtype == T_ANY)
{
if (mxname && hostname_isequal(name, mxname))
struct mx_record *mx;
for (mx = mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->mxname))
break;
if (mx)
{
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX, mxtarget);
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX,
mx->mxtarget ? mx->mxtarget : mxtarget);
anscount++;
ans = 1;
}

View File

@@ -18,6 +18,10 @@
#define BOOTREPLY 2
#define DHCP_COOKIE 0x63825363
/* The Linux in-kernel DHCP client silently ignores any packet
smaller than this. Sigh........... */
#define MIN_PACKETSZ 300
#define OPTION_PAD 0
#define OPTION_NETMASK 1
#define OPTION_ROUTER 3
@@ -35,7 +39,9 @@
#define OPTION_MAXMESSAGE 57
#define OPTION_T1 58
#define OPTION_T2 59
#define OPTION_VENDOR_ID 60
#define OPTION_CLIENT_ID 61
#define OPTION_USER_CLASS 77
#define OPTION_END 255
#define DHCPDISCOVER 1
@@ -48,6 +54,7 @@
#define DHCPINFORM 8
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);
@@ -63,7 +70,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
int iface_mtu, char *netid);
int iface_mtu, struct dhcp_netid *netid);
static int have_config(struct dhcp_config *config, unsigned int mask)
{
@@ -77,11 +84,13 @@ int dhcp_reply(struct dhcp_context *context,
struct udp_dhcp_packet *rawpacket,
unsigned int sz, time_t now, char *namebuff,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
struct dhcp_vendor *vendors,
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server, struct in_addr router)
{
unsigned char *opt, *clid;
struct dhcp_lease *lease;
struct dhcp_vendor *vendor;
int clid_len;
struct dhcp_packet *mess = &rawpacket->data;
unsigned char *p = mess->options;
@@ -92,8 +101,10 @@ int dhcp_reply(struct dhcp_context *context,
char *message = NULL;
unsigned int renewal_time, expires_time, def_time;
struct dhcp_config *config;
char *netid;
struct dhcp_netid *netid = NULL;
struct in_addr addr;
unsigned short fuzz = 0;
if (mess->op != BOOTREQUEST ||
mess->hlen != ETHER_ADDR_LEN ||
mess->cookie != htonl(DHCP_COOKIE))
@@ -137,18 +148,7 @@ int dhcp_reply(struct dhcp_context *context,
clid = mess->chaddr;
clid_len = 0;
}
/* do we have a lease in store? */
lease = lease_find_by_client(clid, clid_len);
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
{
int len = option_len(opt);
req_options = namebuff;
memcpy(req_options, option_ptr(opt), len);
req_options[len] = OPTION_END;
}
if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
have_config(config, CONFIG_NAME))
hostname = config->hostname;
@@ -175,15 +175,80 @@ int dhcp_reply(struct dhcp_context *context,
hostname = NULL;
}
else
*dot = 0; /* truncate */
{
*dot = 0; /* truncate */
if (strlen(hostname) == 0)
hostname = NULL; /* nothing left */
}
}
/* search again now we have a hostname */
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
}
}
/* search again now we have a hostname */
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
netid = have_config(config, CONFIG_NETID) ? config->netid : context->netid;
if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
if (have_config(config, CONFIG_NETID))
{
config->netid.next = netid;
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 = vendors; vendor; vendor = vendor->next)
vendor->used = 0;
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
for (vendor = 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 = 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;
}
}
}
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
if (have_config(config, CONFIG_NOCLID))
{
clid = mess->chaddr;
clid_len = 0;
}
/* do we have a lease in store? */
lease = lease_find_by_client(clid, clid_len);
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
{
@@ -203,11 +268,19 @@ int dhcp_reply(struct dhcp_context *context,
else
expires_time = def_time;
}
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
{
int len = option_len(opt);
req_options = namebuff;
memcpy(req_options, option_ptr(opt), len);
req_options[len] = OPTION_END;
}
if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
return 0;
switch (opt[2])
switch (option_uint(opt, 1))
{
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
@@ -261,39 +334,48 @@ int dhcp_reply(struct dhcp_context *context,
case DHCPDISCOVER:
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
mess->yiaddr = option_addr(opt);
addr = option_addr(opt);
if (have_config(config, CONFIG_DISABLE))
message = "ignored";
else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
mess->yiaddr = config->addr;
else if (lease &&
((lease->addr.s_addr & context->netmask.s_addr) ==
(context->start.s_addr & context->netmask.s_addr)))
else if (lease && is_same_net(lease->addr, context->start, context->netmask))
mess->yiaddr = lease->addr;
else if ((!opt || !address_available(context, mess->yiaddr)) &&
!address_allocate(context, dhcp_configs, &mess->yiaddr))
else if (opt && address_available(context, addr))
mess->yiaddr = addr;
else if (!address_allocate(context, dhcp_configs, &mess->yiaddr, mess->chaddr))
message = "no address available";
log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, message);
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
if (message)
return 0;
/* ensure that we send the reply by steam even if a buggy client sets this. */
mess->ciaddr.s_addr = 0;
bootp_option_put(mess, dhcp_file, dhcp_sname);
mess->siaddr = dhcp_next_server;
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
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);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (expires_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 = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
NULL, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
p = option_end(p, end, mess);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
return p - (unsigned char *)mess;
case DHCPREQUEST:
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
if (have_config(config, CONFIG_DISABLE))
message = "disabled";
else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
{
/* SELECTING or INIT_REBOOT */
mess->yiaddr = option_addr(opt);
@@ -333,15 +415,17 @@ int dhcp_reply(struct dhcp_context *context,
mess->yiaddr = mess->ciaddr;
if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
message = "lease not found";
/* desynchronise renewals */
fuzz = rand16();
while (fuzz > (renewal_time/16))
fuzz = fuzz/2;
}
/* If a machine moves networks whilst it has a lease, we catch that here. */
if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr))
if (!message && !is_same_net(mess->yiaddr, context->start, context->netmask))
message = "wrong network";
if (have_config(config, CONFIG_DISABLE))
message = "disabled";
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
if (message)
@@ -352,7 +436,7 @@ int dhcp_reply(struct dhcp_context *context,
bootp_option_put(mess, NULL, NULL);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
p = option_put_string(p, end, OPTION_MESSAGE, message);
p = option_put(p, end, OPTION_END, 0, 0);
p = option_end(p, end, mess);
mess->flags |= htons(0x8000); /* broadcast */
return p - (unsigned char *)mess;
}
@@ -360,41 +444,39 @@ int dhcp_reply(struct dhcp_context *context,
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
lease_set_hwaddr(lease, mess->chaddr);
lease_set_hostname(lease, hostname, domain_suffix);
if (hostname)
lease_set_hostname(lease, hostname, domain_suffix);
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
bootp_option_put(mess, dhcp_file, dhcp_sname);
mess->siaddr = dhcp_next_server;
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
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)
{
unsigned short fuzz = rand16();
while (fuzz > (renewal_time/16))
fuzz = fuzz/2;
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);
}
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
p = option_end(p, end, mess);
return p - (unsigned char *)mess;
case DHCPINFORM:
if (have_config(config, CONFIG_DISABLE))
{
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, "ignored");
return 0;
}
message = "ignored";
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message);
if (message || mess->ciaddr.s_addr == 0)
return 0;
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, dhcp_opts, domain_suffix,
hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
p = option_end(p, end, mess);
log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
return p - (unsigned char *)mess;
@@ -464,20 +546,26 @@ static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt,
int i;
/* always keep one octet space for the END option. */
if ((opt == OPTION_END) || (p + len + 3 < end))
if (p + len + 3 < end)
{
*(p++) = opt;
if (opt != OPTION_END)
{
*(p++) = len;
for (i = 0; i < len; i++)
*(p++) = val >> (8 * (len - (i + 1)));
}
*(p++) = len;
for (i = 0; i < len; i++)
*(p++) = val >> (8 * (len - (i + 1)));
}
return p;
}
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start)
{
*(p++) = OPTION_END;
while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
*p++ = 0;
return p;
}
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
{
if (p + strlen(string) + 3 < end)
@@ -546,6 +634,10 @@ static int in_list(unsigned char *list, int opt)
{
int i;
/* If no requested options, send everything, not nothing. */
if (!list)
return 1;
for (i = 0; list[i] != OPTION_END; i++)
if (opt == list[i])
return 1;
@@ -553,13 +645,26 @@ static int in_list(unsigned char *list, int opt)
return 0;
}
static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt)
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
{
for (; opts; opts = opts->next)
if (opts->opt == opt &&
(!opts->netid || (netid && strcmp(opts->netid, netid) == 0)))
return opts;
return NULL;
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;
}
else if (!tmp->netid)
return tmp;
}
return netid ? option_find2(NULL, opts, opt) : NULL;
}
static unsigned char *do_req_options(struct dhcp_context *context,
@@ -569,13 +674,10 @@ static unsigned char *do_req_options(struct dhcp_context *context,
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
int iface_mtu, char *netid)
int iface_mtu, struct dhcp_netid *netid)
{
int i;
if (!req_options)
return p;
struct dhcp_opt *opt;
if (in_list(req_options, OPTION_MAXMESSAGE))
p = option_put(p, end, OPTION_MAXMESSAGE, 2,
DNSMASQ_PACKETSZ > iface_mtu ?
@@ -607,14 +709,13 @@ static unsigned char *do_req_options(struct dhcp_context *context,
if (hostname && in_list(req_options, OPTION_HOSTNAME))
p = option_put_string(p, end, OPTION_HOSTNAME, hostname);
for (i = 0; req_options[i] != OPTION_END; i++)
for (opt=config_opts; opt; opt = opt->next)
{
struct dhcp_opt *opt;
if (req_options[i] == OPTION_HOSTNAME ||
req_options[i] == OPTION_MAXMESSAGE ||
!(opt = option_find2(netid, config_opts, req_options[i])) ||
(p + opt->len + 3 >= end))
if (opt->opt == OPTION_HOSTNAME ||
opt->opt == OPTION_MAXMESSAGE ||
!in_list(req_options, opt->opt) ||
opt != option_find2(netid, config_opts, opt->opt) ||
p + opt->len + 3 >= end)
continue;
/* For the options we have default values on
@@ -631,7 +732,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
*(p++) = opt->len;
if (opt->len == 0)
continue;
if (opt->is_addr)
{
int j;

View File

@@ -11,7 +11,7 @@
*/
/* Code in this file contributed by Rob Funk. */
/* Some code in this file contributed by Rob Funk. */
#include "dnsmasq.h"
@@ -85,6 +85,18 @@ unsigned short rand16(void)
return( (unsigned short) (rand() >> 15) );
}
int atoi_check(char *a, int *res)
{
char *p;
for (p = a; *p; p++)
if (*p < '0' || *p > '9')
return 0;
*res = atoi(a);
return 1;
}
int legal_char(char c)
{
/* check for legal char a-z A-Z 0-9 -
@@ -100,12 +112,18 @@ int legal_char(char c)
int canonicalise(char *s)
{
/* check for legal chars ans remove trailing . */
/* check for legal chars and remove trailing .
also fail empty string. */
int l = strlen(s);
char c;
if (l>0 && s[l-1] == '.')
s[l-1] = 0;
if (l == 0) return 0;
if (s[l-1] == '.')
{
if (l == 1) return 0;
s[l-1] = 0;
}
while ((c = *s++))
if (c != '.' && !legal_char(c))
@@ -228,3 +246,8 @@ time_t dnsmasq_time(int fd)
return time(NULL);
#endif
}
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
{
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}