Compare commits

...

3 Commits
v2.19 ... v2.22

Author SHA1 Message Date
Simon Kelley
91dccd0958 import of dnsmasq-2.22.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
0a852541d3 import of dnsmasq-2.21.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
f6b7dc47c7 import of dnsmasq-2.20.tar.gz 2012-01-05 17:31:11 +00:00
23 changed files with 2318 additions and 1072 deletions

124
CHANGELOG
View File

@@ -1267,7 +1267,7 @@ version 2.17
already a fatal error when bind-interfaces is set.)
Allow the --interface and --except-interface options to
take a comma-seperated list of interfaces.
take a comma-separated list of interfaces.
Tweak --dhcp-userclass matching code to work with the
ISC dhclient which violates RFC3004 unless its
@@ -1324,7 +1324,123 @@ version 2.19
option, send the /etc/hosts name as the hostname in
the DHCP lease. Thanks to Will Murname for the suggestion.
version 2.20
Allow more than one instance of dnsmasq to run on a
machine, each providing DHCP service on a different
interface, provided that --bind-interfaces is set. This
configuration used to work, but regressed in version 2.14
Fix compilation on Mac OS X. Thanks to Kevin Bullock.
Protect against overlong names and overlong
labels in configuration and from DHCP.
Fix interesting corner case in CNAME handling. This occurs
when a CNAME has a target which "shadowed" by a name in
/etc/hosts or from DHCP. Resolving the CNAME would sneak
the upstream value of the CNAME's target into the cache,
alongside the local value. Now that doesn't happen, though
resolving the CNAME still gives the unshadowed value. This
is arguably wrong but rather difficult to fix. The main
thing is to avoid getting strange results for the target
due to the cache pollution when resolving the
CNAME. Thanks to Pierre Habouzit for exploring the corner
and submitting a very clear bug report.
Fix subtle bug in the DNS packet parsing code. It's almost
impossible to describe this succinctly, but the one known
manifestation is the inability to cache the A record for
www.apple.com. Thanks to Bob Alexander for spotting that.
Support SRV records. Thanks to Robert Kean for the patches
for this.
Fixed sign confusion in the vendor-id matching code which
could cause crashes sometimes. (Credit to Mark Wiater for
help finding this.)
Added the ability to match the netid tag in a
dhcp-range. Combined with the ability to have multiple
ranges in a single subnet, this provides a means to
segregate hosts on different address ranges based on
vendorclass or userclass. Thanks to Mark Wiater for
prompting this enhancement.
Added preference values for MX records.
Added the --localise-queries option.
version 2.21
Improve handling of SERVFAIL and REFUSED errors. Receiving
these now initiates search for a new good server, and a
server which returns them is not a candidate as a good
server. Thanks to Istvan Varadi for pointing out the
problem.
Tweak the time code in BROKEN_RTC mode.
Sanity check lease times in dhcp-range and dhcp-host
configurations and force them to be at least two minutes
(120s) leases shorter than a minute confuse some clients,
notably Apple MacOS X. Rory Campbell-Lange found this
problem.
Only warn once about an upstream server which is refusing to do
recursive queries.
Fix DHCP address allocation problem when netid tags are in
use. Thanks to Will Murnane for the bug report and
subsequent testing.
Add an additional data section to the reply for MX and SRV
queries. Add support for DNS TXT records. Thanks to Robert
Kean and John Hampton for prompts and testing of these.
Apply address rewriting to records in the additional data section
of DNS packets. This makes things like MX records work
with the alias function. Thanks to Chad Skeeters for
pointing out the need for this.
Added support for quoted strings in config file.
Detect and defeat cache-poisoning attacks which attempt to
send (malicious) answers to questions we didn't
send. These are ignored now even if the attacker manages
to guess a random query-id.
Provide DHCP support for interfaces with multiple IP
addresses or aliases. This in only enabled under Linux.
See the FAQ entry for details.
Revisit the MAC-address and client-id matching code to
provide saner behaviour with PXE boots, where some
requests have a client-id and some don't.
Fixed off-by-one buffer overflow in lease file reading
code. Thanks to Rob Holland for the bug report.
Added wildcard matching for MAC addresses in dhcp-host
options. A sensible suggestion by Nathaniel McCallum.
version 2.22
Fixed build problems on (many) systems with older libc
headers where <linux/types.h> is required before
<linux/netlink.h>. Enabled HAVE_RTNETLINK under uclibc now
that this fix is in place.
Added support for encapsulated vendor-class-specific DHCP
options. Thanks to Eric Shattow for help with this.
Fix regression in 2.21 which broke commas in filenames and
corrupted argv. Thanks to Eric Scott for the bugreport.
Fixed stupid thinko which caused dnsmasq to wedge during
startup with certain MX-record options. Another 2.21 regression.
Fixed broken-ness when reading /etc/ethers. 2.21 broke
this too.
Fixed wedge with certain DHCP options. Yet another 2.21
regression. Rob Holland and Roy Marples chased this one
down.

30
FAQ
View File

@@ -21,8 +21,7 @@ Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
that?
A: Update: from version 2.10, it does. There are a few limitations:
data obtained via TCP is not cached, and dynamically-created
interfaces may break under certain circumstances. Source-address
data obtained via TCP is not cached, and source-address
or query-port specifications are ignored for TCP.
Q: When I send SIGUSR1 to dump the contents of the cache, some entries have
@@ -112,7 +111,7 @@ A: Resolver code sometime does strange things when given names without
resolver will attempt to look up "myhost.localnet" so you need to
have dnsmasq reply to that name. The way to do that is to include
the domain in each name on /etc/hosts and/or to use the
--expand-hosts and --domain-suffix options.
--expand-hosts and --domain 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?
@@ -316,5 +315,30 @@ A: By default, the identity of a machine is determined by using the
dhcpcd uses the "-I" flag. Windows uses a registry setting,
see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
Q: Can dnsmasq do DHCP on IP-alias interfaces?
A: Yes, from version-2.21. The support is only available running under
Linux, on a kernel which provides the RT-netlink facility. All 2.4
and 2.6 kernels provide RT-netlink and it's an option in 2.2
kernels. If dnsmasq is built under uclibc, even on Linux, then
the support is not included.
If a physical interface has more than one IP address or aliases
with extra IP addresses, then any dhcp-ranges corresponding to
these addresses can be used for address allocation. So is and
interface has addresses 192.168.1.0/24 and 192.68.2.0/24 and there
are DHCP ranges 192.168.1.100-192.168.1.200 and
192.168.2.100-192.168.2.200 then both ranges would be used for host
connected to the physical interface. A more typical use might be to
have one of the address-ranges as static-only, and have known
hosts allocated addresses on that subnet using dhcp-host options,
while anonymous hosts go on the other.

View File

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

View File

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

117
dnsmasq.8
View File

@@ -143,11 +143,21 @@ requests that it shouldn't reply to. This has the advantage of
working even when interfaces come and go and change address. This
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
running another nameserver (or another instance of dnsmasq) on the
same machine or when using IP
alias. Specifying interfaces with IP alias automatically turns this
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.
option on. Setting this option also enables multiple instances of
dnsmasq which provide DHCP service to run in the same machine.
.TP
.B \-y, --localise-queries
Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
recieved. If a name in /etc/hosts has more than one address associated with
it, and at least one of those addresses is on the same subnet as the
interface to which the query was sent, then return only the
address(es) on that subnet. This allows for a server to have multiple
addresses in /etc/hosts corresponding to each of its interfaces, and
hosts will get the correct address based on which network they are
attached to. Currently this facility is limited to IPv4.
.TP
.B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
@@ -214,7 +224,7 @@ and they are queried only using the specified server. This is
intended for private nameservers: if you have a nameserver on your
network which deals with names of the form
xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B -S /internal.thekelleys.org.uk/192.168.1.1
.B -S /.internal.thekelleys.org.uk/192.168.1.1
will send all queries for
internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. An empty domain specification,
@@ -258,18 +268,20 @@ additional facility that /#/ matches any domain. Thus
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>[,<hostname>]
.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
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.
is running. The default is useful for directing mail from systems on a LAN
to a central server. The preference value is optional, and defaults to
1 if not given. More than one MX record may be given for a host.
.TP
.B \-t, --mx-target=<hostname>
Specify target for the MX record returned by dnsmasq. See --mx-host. Note that to turn on the MX function,
at least one of --mx-host and --mx-target must be set. If only one of --mx-host and --mx-target
is set, the other defaults to the hostname of the machine on which dnsmasq is running.
Specify the default target for the MX record returned by dnsmasq. See
--mx-host. If --mx-target is given, but not --mx-host, then dnsmasq
returns a MX record containing the MX target for MX queries on the
hostname of the machine on which dnsmasq is running.
.TP
.B \-e, --selfmx
Return an MX record pointing to itself for each local
@@ -281,6 +293,27 @@ machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP
leases.
.TP
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
Return a SRV DNS record. See RFC2782 for details. If not supplied, the
domain defaults to that given by
.B --domain.
The default for the target domain is empty, and the default for port
is one and the defaults for
weight and priority are zero. Be careful if transposing data from BIND
zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned. Specifying at least one
.B --srv-host
option also turns on replies to SOA queries for the
domain given by the
.B --domain
option. The data in these is stereotyped, but is enough for resolvers
to deduce that the domain is a valid one for resolving SRV records.
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
so any number may be included, split by commas.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
.TP
@@ -290,7 +323,7 @@ Disable negative caching. Negative caching allows dnsmasq to remember
identical queries without forwarding them again. This flag disables
negative caching.
.TP
.B \-F, --dhcp-range=[network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
.B \-F, --dhcp-range=[[net:]network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
in
@@ -307,10 +340,13 @@ always optional. On some broken systems, dnsmasq can listen on only
one interface when using DHCP, and the name of that interface must be
given using the
.B interface
option. This limitation currently affects OpenBSD. The optional
option. This limitation currently affects OpenBSD. It is always
allowed to have more than one dhcp-range in a single subnet. The optional
network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis. The end address
may be replaced by the keyword
dhcp options may be specified on a per-network basis.
When it is prefixed with 'net:' then its meaning changes from setting
a tag to matching it.
The end address may be replaced by the keyword
.B static
which tells dnsmasq to enable DHCP for the network specified, but not
to dynamically allocate IP addresses. Only hosts which have static
@@ -357,6 +393,12 @@ useful when there is another DHCP server on the network which should
be used by some machines. The net:<network-id> parameter enables DHCP options just
for this host in the same way as the the network-id in
.B dhcp-range.
Ethernet addresses (but not client-ids) may have
wildcard bytes, so for example
.B --dhcp-host=00:20:e0:3b:13:*,ignore
will cause dnsmasq to ignore a range of ethernet addresses. Note that
the "*" will need to be escaped or quoted on a command line, but not
in the configuration file.
.TP
.B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The
@@ -366,7 +408,7 @@ have exactly the same effect as
.B --dhcp-host
options containing the same information.
.TP
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]]<opt>,[<value>[,<value>]]
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:<vendor-class>]<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
@@ -380,14 +422,27 @@ specfied in RFC2132. For example, to set the default route option to
and to set the time-server address to 192.168.0.4, do
.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
machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-separated hex digits
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, it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
of this flag.
of this flag. When the value is a decimal number, dnsmasq must determine how
large the data item is. It does this by examining the option number and/or the
value, but can be overriden by appending a single letter flag as follows:
b = one byte, s = two bytes, i = four bytes. This is mainly useful with
encapsulated vendor class options (see below) where dnsmasq cannot determine data size from the option number.
Encapsulated Vendor-class options may also be specified using
--dhcp-option: for instance
.B --dhcp-option=vendor:PXEClient,1,0.0.0.0
sends the vendor class "PXEClient" and the encapsulated vendor class-specific option "mftp-address=0.0.0.0" Only one vendor class is allowed for any
host, but multiple options are allowed, provided they all have
the same vendor class. The address 0.0.0.0 is not treated specially in
encapsulated vendor class options.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Map from a vendor-class string to a network id. Most DHCP clients provide a
@@ -470,7 +525,10 @@ options which may only be specified once, the configuration file overrides
the command line. Use the --conf-file option to specify a different
configuration file. The conf-file option is also allowed in
configuration files, to include multiple configuration files. Only one
level of nesting is allowed.
level of nesting is allowed. Quoting is allowed in a config file:
between " quotes the special meaning of , and # is removed and the
following escapes are allowed: \\\\ \\" \\t and \\n. The later two
corresponding to newline and tab.
.SH NOTES
When it receives a SIGHUP,
.B dnsmasq
@@ -544,6 +602,17 @@ and run dnsmasq with the
.B \-r /etc/resolv.dnsmasq
option. This second technique allows for dynamic update of the server
addresses by PPP or DHCP.
.PP
Addresses in /etc/hosts will "shadow" different addresses for the same
names in the upstream DNS, so "mycompany.com 1.2.3.4" in /etc/hosts will ensure that
queries for "mycompany.com" always return 1.2.3.4 even if queries in
the upstream DNS would otherwise return a different address. There is
one exception to this: if the upstream DNS contains a CNAME which
points to a shadowed name, then looking up the CNAME through dnsmasq
will result in the unshadowed address associated with the target of
the CNAME. To work around this, add the CNAME to /etc/hosts so that
the CNAME is shadowed too.
.PP
The network-id system works as follows: For each DHCP request, dnsmasq
collects a set of valid network-id tags, one from the
@@ -560,6 +629,14 @@ 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
If the network-id in a
.B dhcp-range
is prefixed with 'net:' then its meaning changes from setting a
tag to matching it. Thus if there is more than dhcp-range on a subnet,
and one is tagged with a network-id which is set (for instance
from a vendorclass option) then hosts which set the netid tag will be
allocated addresses in the tagged range.
.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

View File

@@ -4,14 +4,6 @@
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Change these lines if you want dnsmasq to serve MX records.
# Only one of mx-host and mx-target need be set, the other defaults
# to the name of the host running dnsmasq.
#mx-host=
#mx-target=
#selfmx
#localmx
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
@@ -28,6 +20,8 @@ bogus-priv
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k
# Change this line if you want dns to get its upstream servers from
@@ -63,9 +57,8 @@ bogus-priv
# webserver.
#address=/doubleclick.net/127.0.0.1
# You no longer (as of version 1.7) need to set these to enable
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
# "dip" group to achieve this.
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
#user=
#group=
@@ -174,6 +167,10 @@ bogus-priv
# 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 with ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,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
@@ -242,6 +239,13 @@ bogus-priv
# probably doesn't support this......
#dhcp-option=119,eng.apple.com,marketing.apple.com
# Send encapsulated vendor-class specific options. The vendor-class
# is sent as DHCP option 60, and all the options marked with the
# vendor class are send encapsulated in DHCP option 43. The meaning of
# the options is defined by the vendor-class. This example sets the
# mtftp address to 0.0.0.0 for PXEClients
#dhcp-option=vendor:PXEClient,1,0.0.0.0
# Set the boot filename and tftpd server name and address
# for BOOTP. You will only need this is you want to
# boot machines over the network.
@@ -292,6 +296,64 @@ bogus-priv
# and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,v=spf1 a -all
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries

View File

@@ -108,7 +108,7 @@ HREF="http://howto.linux-hardware-shop.de/dnsmasq.html">http://howto.linux-hardw
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>
and Ilya Evseev has an article in Russian about dnsmasq to be found at <A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq"> http://ilya-evseev.narod.ru/articles/dnsmasq</A>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details.

View File

@@ -9,17 +9,6 @@
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
--- dnsmasq.conf.example 2004-08-08 21:18:26.000000000 +0200
+++ dnsmasq.conf.example 2004-08-12 00:40:01.000000000 +0200
@@ -65,7 +65,7 @@
# You no longer (as of version 1.7) need to set these to enable
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
-# "dip" group to achieve this.
+# "dialout" group to achieve this.
#user=
#group=
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@
@@ -31,7 +20,7 @@
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
@@ -187,7 +187,7 @@
@@ -195,7 +195,7 @@
/* platform independent options. */
#undef HAVE_BROKEN_RTC

View File

@@ -4,7 +4,7 @@
CFLAGS?= -O2
OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o \
network.o dnsmasq.o dhcp.o lease.o rfc2131.o
network.o dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o
.c.o: dnsmasq.h config.h
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -156,32 +156,52 @@ static int is_outdated_cname_pointer(struct crec *crecp)
return 1;
}
static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
static int is_expired(time_t now, struct crec *crecp)
{
if (crecp->flags & F_IMMORTAL)
return 0;
if (difftime(now, crecp->ttd) < 0)
return 0;
return 1;
}
static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
{
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
entries but only in the same hash bucket as name.
If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache. */
If (flags == 0) remove any expired entries in the whole cache.
In the flags & F_FORWARD case, the return code is valid, and returns zero if the
name exists in the cache as a HOSTS or DHCP entry (these are never deleted) */
#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6 | F_CNAME)
struct crec *crecp, **up;
flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4 | F_CNAME);
if (flags & F_FORWARD)
{
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
is_outdated_cname_pointer(crecp) ||
((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
{
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
{
{
cache_unlink(crecp);
cache_free(crecp);
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || (crecp->flags & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else
up = &crecp->hash_next;
@@ -196,8 +216,7 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
#endif
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
if (is_expired(now, crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -206,9 +225,20 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
cache_free(crecp);
}
}
else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
{
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else
up = &crecp->hash_next;
}
return 1;
}
/* Note: The normal calling sequence is
@@ -260,8 +290,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
return NULL;
/* First remove any expired entries and entries for the name/address we
are currently inserting. */
cache_scan_free(name, addr, now, flags);
are currently inserting. Fail is we attempt to delete a name from
/etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags))
{
insert_error = 1;
return NULL;
}
/* Now get a cache entry from the end of the LRU list */
while (1) {
@@ -376,8 +411,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
{
next = crecp->hash_next;
if (!is_outdated_cname_pointer(crecp) &&
((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
(crecp->flags & prot) &&
@@ -458,7 +492,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
for(i=0; i<hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
if (!is_expired(now, crecp))
{
if ((crecp->flags & F_REVERSE) &&
(crecp->flags & prot) &&
@@ -812,8 +846,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
if (!log_queries)
return;
strcpy(types, " ");
if (flags & F_NEG)
{
if (flags & F_REVERSE)
@@ -835,7 +867,17 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
strcat(addrbuff, "-IPv6");
}
else if (flags & F_CNAME)
strcpy(addrbuff, "<CNAME>");
{
/* nasty abuse of IPV4 and IPV6 flags */
if (flags & F_IPV4)
strcpy(addrbuff, "<MX>");
else if (flags & F_IPV6)
strcpy(addrbuff, "<SRV>");
else if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<TXT>");
else
strcpy(addrbuff, "<CNAME>");
}
else
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
@@ -895,19 +937,19 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
if (type != 0)
{
sprintf(types, "[type=%d] ", type);
sprintf(types, "query[type=%d]", type);
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(types,"[%s] ", typestr[i].name);
sprintf(types,"query[%s]", typestr[i].name);
}
source = "query";
source = types;
verb = "from";
}
else
source = "cached";
if ((flags & F_FORWARD) | (flags & F_NEG))
syslog(LOG_DEBUG, "%s %s%s%s %s", source, name, types, verb, addrbuff);
syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, addrbuff);
else if (flags & F_REVERSE)
syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2004 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.19"
#define VERSION "2.22"
#define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -158,6 +158,12 @@ HAVE_PSELECT
HAVE_BPF
If your OS implements Berkeley Packet filter, define this.
HAVE_RTNETLINK
If your OS has the Linux Routing netlink socket API and suitable
C library headers, define this. Note that the code will fall
back to the Berkley API at runtime if netlink support is not
configured into the kernel.
NOTES:
For Linux you should define
HAVE_LINUX_IPV6_PROC
@@ -165,6 +171,7 @@ NOTES:
HAVE_RANDOM
HAVE_DEV_RANDOM
HAVE_DEV_URANDOM
HAVE_RTNETLINK
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
@@ -175,6 +182,7 @@ NOTES:
HAVE_BPF
you should NOT define
HAVE_LINUX_IPV6_PROC
HAVE_RTNETLINK
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
@@ -199,6 +207,7 @@ NOTES:
#if defined(__uClinux__) || defined(__UCLIBC__)
#undef HAVE_LINUX_IPV6_PROC
#define HAVE_GETOPT_LONG
#define HAVE_RTNETLINK
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
@@ -223,6 +232,7 @@ NOTES:
(_LINUX_C_LIB_VERSION_MAJOR == 5 )
#undef HAVE_IPV6
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
@@ -237,6 +247,7 @@ typedef size_t socklen_t;
/* This is for glibc 2.x */
#elif defined(__linux__)
#define HAVE_LINUX_IPV6_PROC
#define HAVE_RTNETLINK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
@@ -256,6 +267,7 @@ typedef unsigned long in_addr_t;
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
@@ -271,6 +283,7 @@ typedef unsigned long in_addr_t;
#elif defined(__APPLE__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
@@ -278,7 +291,6 @@ typedef unsigned long in_addr_t;
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#define BIND_8_COMPAT
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* This is not defined in Mac OS X arpa/nameserv.h */
@@ -286,6 +298,7 @@ typedef unsigned long in_addr_t;
#elif defined(__NetBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
@@ -298,6 +311,7 @@ typedef unsigned long in_addr_t;
/* env "LIBS=-lsocket -lnsl" make */
#elif defined(__sun) || defined(__sun__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_RTNETLINK
#undef HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,6 +34,13 @@ void dhcp_init(struct daemon *daemon)
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
die("failed to set options on DHCP socket: %s", NULL);
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's Ok if they serve different networks.
Need to set REUSEADDR to make this posible. */
if ((daemon->options & OPT_NOWILD) &&
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1)
die("failed to set SO_REUSEADDR on DHCP socket: %s", NULL);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
@@ -91,10 +98,10 @@ void dhcp_init(struct daemon *daemon)
die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
/* These two each hold a DHCP option max size 256
/* These two each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(257);
daemon->dhcp_buff2 = safe_malloc(257);
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
}
@@ -109,7 +116,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
struct iovec iov[2];
struct cmsghdr *cmptr;
int sz, newlen, iface_index = 0;
struct in_addr iface_netmask, iface_addr, iface_broadcast;
struct in_addr iface_addr;
#ifdef HAVE_BPF
unsigned char iface_hwaddr[ETHER_ADDR_LEN];
#endif
@@ -199,66 +206,30 @@ void dhcp_packet(struct daemon *daemon, time_t now)
return;
}
iface_netmask.s_addr = 0;
iface_broadcast.s_addr = 0;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
{
/* Fill in missing netmask and broadcast address values for any approriate
dhcp-ranges which match this interface and don't have them. */
if (!context->netmask.s_addr)
{
if (!iface_netmask.s_addr && ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (iface_netmask.s_addr &&
(is_same_net(iface_addr, context->start, iface_netmask) ||
is_same_net(iface_addr, context->end, iface_netmask)))
{
context->netmask = iface_netmask;
if (!(is_same_net(iface_addr, context->start, iface_netmask) &&
is_same_net(iface_addr, context->end, iface_netmask)))
{
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask));
}
}
}
/* Determine "default" default routes. These are to this server or the relay agent.
Also broadcast addresses, if not specified */
if (context->netmask.s_addr)
{
if (is_same_net(iface_addr, context->start, context->netmask))
{
if (!context->router.s_addr)
context->router = iface_addr;
if (!context->broadcast.s_addr)
{
if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (iface_broadcast.s_addr &&
is_same_net(iface_broadcast, context->start, context->netmask))
context->broadcast = iface_broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
else if (mess->giaddr.s_addr && is_same_net(mess->giaddr, context->start, context->netmask))
{
if (!context->router.s_addr)
context->router = mess->giaddr;
/* fill in missing broadcast addresses for relayed ranges */
if (!context->broadcast.s_addr)
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
}
context->current = context;
#ifdef HAVE_RTNETLINK
if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
#endif
{
struct in_addr iface_netmask, iface_broadcast;
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) < 0)
return;
iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) < 0)
return;
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
context = complete_context(daemon, iface_addr, NULL, iface_netmask,
iface_broadcast, mess->giaddr, iface_addr);
}
lease_prune(NULL, now); /* lose any expired leases */
newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now);
newlen = dhcp_reply(daemon, context, ifr.ifr_name, sz, now);
lease_update_file(0, now);
lease_update_dns(daemon);
@@ -375,6 +346,74 @@ void dhcp_packet(struct daemon *daemon, time_t now)
}
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
of the interface on which a DHCP packet arrives (and any relay address) and does the
following things:
1) Fills in any netmask and broadcast addresses which have not been explicitly configured.
2) Fills in local (this host) and router (this host or relay) addresses.
3) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, struct dhcp_context *current,
struct in_addr netmask, struct in_addr broadcast, struct in_addr relay,
struct in_addr primary)
{
struct dhcp_context *context;
for (context = daemon->dhcp; context; context = context->next)
{
if (!(context->flags & CONTEXT_NETMASK) &&
(is_same_net(local, context->start, netmask) ||
is_same_net(local, context->end, netmask)))
{
if (context->netmask.s_addr != netmask.s_addr &&
!(is_same_net(local, context->start, netmask) &&
is_same_net(local, context->end, netmask)))
{
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
}
context->netmask = netmask;
}
if (context->netmask.s_addr)
{
if (is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask))
{
/* link it onto the current chain if we've not seen it before */
if (context->current == context)
{
context->router = local;
context->local = local;
context->current = current;
current = context;
}
if (!(context->flags & CONTEXT_BRDCAST))
{
if (is_same_net(broadcast, context->start, context->netmask))
context->broadcast = broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
else if (relay.s_addr && is_same_net(relay, context->start, context->netmask))
{
context->router = relay;
context->local = primary;
/* fill in missing broadcast addresses for relayed ranges */
if (!(context->flags & CONTEXT_BRDCAST))
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
}
return current;
}
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
{
/* Check is an address is OK for this network, check all
@@ -387,7 +426,7 @@ struct dhcp_context *address_available(struct dhcp_context *context, struct in_a
start = ntohl(context->start.s_addr);
end = ntohl(context->end.s_addr);
if (!context->static_only &&
if (!(context->flags & CONTEXT_STATIC) &&
addr >= start &&
addr <= end)
return context;
@@ -412,7 +451,7 @@ struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr
return tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (tmp->static_only)
if (tmp->flags & CONTEXT_STATIC)
return tmp;
return context;
@@ -429,25 +468,61 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
return NULL;
}
/* Is every member of check matched by a member of pool? */
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;
}
int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr)
struct in_addr *addrp, unsigned char *hwaddr, struct dhcp_netid *netids)
{
/* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration */
a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which mathc netis first. */
struct in_addr start, addr ;
struct dhcp_context *c;
unsigned int i, j;
for (; context; context = context->current)
if (!context->static_only)
for (c = context; c; c = c->current)
if (c->flags & CONTEXT_STATIC)
continue;
else if (netids && !(c->flags & CONTEXT_FILTER))
continue;
else if (!netids && (c->flags & CONTEXT_FILTER))
continue;
else if (netids && (c->flags & CONTEXT_FILTER) && !match_netid(&c->netid, netids))
continue;
else
{
/* pick a seed based on hwaddr then iterate until we find a free address. */
for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
for (j = c->addr_epoch, 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 % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
htonl(ntohl(c->start.s_addr) +
(j % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
do {
if (!lease_find_by_addr(addr) &&
@@ -456,7 +531,7 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
if (icmp_ping(daemon, addr))
/* perturb address selection so that we are
less likely to try this address again. */
context->addr_epoch++;
c->addr_epoch++;
else
{
*addrp = addr;
@@ -466,18 +541,22 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1))
addr = context->start;
if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
addr = c->start;
} while (addr.s_addr != start.s_addr);
}
if (netids)
return address_allocate(context, daemon, addrp, hwaddr, NULL);
return 0;
}
static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context)
return 1;
return 1;
if (!(config->flags & CONFIG_ADDR))
return 1;
if (is_same_net(config->addr, context->start, context->netmask))
@@ -486,6 +565,7 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
return 0;
}
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
@@ -493,7 +573,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
{
struct dhcp_config *config;
if (clid_len)
if (clid)
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_CLID)
{
@@ -512,24 +592,40 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
is_addr_in_context(context, config))
return config;
if (hostname)
if (hostname && context)
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) &&
hostname_isequal(config->hostname, hostname) &&
is_addr_in_context(context, config))
return config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask != 0 &&
is_addr_in_context(context, config))
{
int i;
unsigned int mask = config->wildcard_mask;
for (i = ETHER_ADDR_LEN - 1; i >= 0; i--, mask = mask >> 1)
if (mask & 1)
config->hwaddr[i] = hwaddr[i];
if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
return config;
}
return NULL;
}
void dhcp_read_ethers(struct daemon *daemon)
{
FILE *f = fopen(ETHERSFILE, "r");
unsigned int flags, e0, e1, e2, e3, e4, e5;
unsigned int flags;
char *buff = daemon->namebuff;
char *ip, *cp;
struct in_addr addr;
@@ -557,16 +653,9 @@ void dhcp_read_ethers(struct daemon *daemon)
if (!*ip)
continue;
if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
if (parse_hex(buff, hwaddr, 6, NULL) != 6)
continue;
hwaddr[0] = e0;
hwaddr[1] = e1;
hwaddr[2] = e2;
hwaddr[3] = e3;
hwaddr[4] = e4;
hwaddr[5] = e5;
/* check for name or dotted-quad */
for (cp = ip; *cp; cp++)
if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
@@ -597,6 +686,7 @@ void dhcp_read_ethers(struct daemon *daemon)
{
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
break;
@@ -605,6 +695,7 @@ void dhcp_read_ethers(struct daemon *daemon)
if (!(config = malloc(sizeof(struct dhcp_config))))
continue;
config->flags = 0;
config->wildcard_mask = 0;
config->next = configs;
configs = config;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2004 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
#include "dnsmasq.h"
static int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
static volatile int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
@@ -69,8 +69,9 @@ int main (int argc, char **argv)
if (daemon->edns_pktsz < PACKETSZ)
daemon->edns_pktsz = PACKETSZ;
daemon->packet = safe_malloc(daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ);
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
if (!daemon->lease_file)
{
@@ -82,8 +83,9 @@ 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(daemon);
if (!enumerate_interfaces(daemon, &interfaces, NULL, NULL))
die("failed to find list of interfaces: %s", NULL);
if (!(daemon->options & OPT_NOWILD) &&
!(daemon->listeners = create_wildcard_listeners(daemon->port)))
{
@@ -271,31 +273,31 @@ int main (int argc, char **argv)
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
#ifdef HAVE_RTNETLINK
/* Must do this after daemonizing so that the pid is right */
daemon->netlinkfd = netlink_init();
#endif
for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
{
char *time = daemon->dhcp_buff2;
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
if (dhcp_tmp->lease_time == 0)
sprintf(time, "infinite");
else
{
unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
if ((x = t/3600))
p += sprintf(&time[p], "%dh", x);
if ((x = (t/60)%60))
p += sprintf(&time[p], "%dm", x);
if ((x = t%60))
p += sprintf(&time[p], "%ds", x);
}
syslog(LOG_INFO,
dhcp_tmp->static_only ?
(dhcp_tmp->flags & CONTEXT_STATIC) ?
"DHCP, static leases only on %.0s%s, lease time %s" :
"DHCP, IP range %s -- %s, lease time %s",
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
#ifdef HAVE_BROKEN_RTC
syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
daemon->min_leasetime = daemon->min_leasetime/3;
if (daemon->min_leasetime > (60 * 60 * 24))
daemon->min_leasetime = 60 * 60 * 24;
if (daemon->min_leasetime < 60)
daemon->min_leasetime = 60;
prettyprint_time(daemon->dhcp_buff2, daemon->min_leasetime);
syslog(LOG_INFO, "DHCP, %s will be written every %s", daemon->lease_file, daemon->dhcp_buff2);
#endif
}
@@ -340,7 +342,7 @@ int main (int argc, char **argv)
{
lease_update_file(1, now);
#ifdef HAVE_BROKEN_RTC
alarm(daemon->min_leasetime/3);
alarm(daemon->min_leasetime);
#endif
}
sigalarm = 0;
@@ -509,45 +511,25 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (FD_ISSET(listener->tcpfd, set))
{
int confd;
struct in_addr netmask, dst_addr_4;
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd != -1)
{
int match = 1;
if (!(daemon->options & OPT_NOWILD))
{
/* Check for allowed interfaces when binding the wildcard address */
/* Don't know how to get interface of a connection, so we have to
check by address. This will break when interfaces change address */
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
struct iname *tmp;
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
{
#ifdef HAVE_IPV6
if (tcp_addr.sa.sa_family == AF_INET6)
tcp_addr.in6.sin6_flowinfo = htonl(0);
#endif
for (match = 1, tmp = daemon->if_except; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 0;
if (match && (daemon->if_names || daemon->if_addrs))
{
match = 0;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 1;
}
}
}
if (!match || (num_kids >= MAX_PROCS))
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
/* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */
if ((num_kids >= MAX_PROCS) ||
(!(daemon->options & OPT_NOWILD) &&
(getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1 ||
!enumerate_interfaces(daemon, NULL, &tcp_addr, &netmask))))
close(confd);
#ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && fork())
@@ -584,7 +566,21 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
buff = tcp_request(daemon, confd, now);
if (listener->family == AF_INET)
{
if (daemon->options & OPT_NOWILD)
{
netmask = listener->iface->netmask;
dst_addr_4 = listener->iface->addr.in.sin_addr;
}
else
/* netmask already set by enumerate_interfaces */
dst_addr_4 = tcp_addr.in.sin_addr;
}
else
dst_addr_4.s_addr = 0;
buff = tcp_request(daemon, confd, now, dst_addr_4, netmask);
if (!(daemon->options & OPT_DEBUG))
exit(0);
@@ -686,3 +682,5 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
return gotreply;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,11 +12,11 @@
/* Author's email: simon@thekelleys.org.uk */
#define COPYRIGHT "Copyright (C) 2000-2004 Simon Kelley"
#define COPYRIGHT "Copyright (C) 2000-2005 Simon Kelley"
#ifdef __linux__
/* for pselect.... */
#define _XOPEN_SOURCE 600
# define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */
#define _BSD_SOURCE
#endif
@@ -27,6 +27,10 @@
/* get this before config.h too. */
#include <syslog.h>
#ifdef __APPLE__
/* need this before arpa/nameser.h */
# define BIND_8_COMPAT
#endif
#include <arpa/nameser.h>
/* and this. */
@@ -57,6 +61,7 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <netinet/if_ether.h>
#else
@@ -74,9 +79,17 @@
#endif
#include <sys/uio.h>
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum.
The buffer is also used for NETLINK, which needs to be about 2000
on systems with many interfaces/addresses. */
#ifdef HAVE_RTNETLINK
# define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#else
# define DNSMASQ_PACKETSZ 2000
#endif
#define OPT_BOGUSPRIV 1
#define OPT_FILTER 2
@@ -96,6 +109,7 @@
#define OPT_RESOLV_DOMAIN 32768
#define OPT_NO_FORK 65536
#define OPT_AUTHORITATIVE 131072
#define OPT_LOCALISE 262144
struct all_addr {
union {
@@ -117,9 +131,16 @@ struct doctor {
struct doctor *next;
};
struct mx_record {
char *mxname, *mxtarget;
struct mx_record *next;
struct mx_srv_record {
char *name, *target;
int issrv, srvport, priority, weight, offset;
struct mx_srv_record *next;
};
struct txt_record {
char *name, *txt;
unsigned short class, len;
struct txt_record *next;
};
union bigname {
@@ -186,14 +207,16 @@ union mysockaddr {
#endif
};
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_SOURCE 8 /* source address specified */
#define SERV_HAS_DOMAIN 16 /* server for one domain only */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_SOURCE 8 /* source address specified */
#define SERV_HAS_DOMAIN 16 /* server for one domain only */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
struct serverfd {
int fd;
union mysockaddr source_addr;
@@ -211,11 +234,13 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
struct irec *next;
};
struct listener {
int fd, tcpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct listener *next;
};
@@ -248,7 +273,7 @@ struct frec {
struct server *sentto;
unsigned int iface;
unsigned short orig_id, new_id;
int fd;
int fd, forwardall;
unsigned int crc;
time_t time;
struct frec *next;
@@ -281,22 +306,22 @@ struct dhcp_config {
char *hostname;
struct dhcp_netid netid;
struct in_addr addr;
unsigned int lease_time;
unsigned int lease_time, wildcard_mask;
struct dhcp_config *next;
};
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_HWADDR 4
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_HWADDR 4
#define CONFIG_TIME 8
#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;
unsigned char *val, *vendor_class;
struct dhcp_netid *netid;
struct dhcp_opt *next;
};
@@ -317,13 +342,20 @@ struct dhcp_vendor {
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast, router;
struct in_addr netmask, broadcast;
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
int static_only;
int flags;
struct dhcp_netid netid;
struct dhcp_context *next, *current;
};
#define CONTEXT_STATIC 1
#define CONTEXT_FILTER 2
#define CONTEXT_NETMASK 4
#define CONTEXT_BRDCAST 8
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
@@ -355,7 +387,8 @@ struct daemon {
unsigned int options;
struct resolvc default_resolv, *resolv_files;
struct mx_record *mxnames;
struct mx_srv_record *mxnames;
struct txt_record *txt;
char *mxtarget;
char *lease_file;
char *username, *groupname;
@@ -370,7 +403,7 @@ struct daemon {
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts;
struct dhcp_opt *dhcp_opts, *vendor_opts;
struct dhcp_vendor *dhcp_vendors;
struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore;
@@ -381,6 +414,7 @@ struct daemon {
/* globally used stuff for DNS */
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
struct serverfd *sfds;
struct listener *listeners;
@@ -389,6 +423,9 @@ struct daemon {
/* DHCP state */
int dhcpfd, dhcp_raw_fd, dhcp_icmp_fd, lease_fd;
#ifdef HAVE_RTNETLINK
int netlinkfd;
#endif
struct udp_dhcp_packet *dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
};
@@ -420,13 +457,14 @@ int setup_reply(HEADER *header, unsigned int qlen,
unsigned long local_ttl);
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
time_t now, struct daemon *daemon);
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now);
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, unsigned int plen,
unsigned int *len, unsigned char **p);
int check_for_local_domain(char *name, time_t now, struct mx_record *mx);
unsigned int questions_crc(HEADER *header, unsigned int plen);
int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
unsigned int questions_crc(HEADER *header, unsigned int plen, char *buff);
int resize_packet(HEADER *header, unsigned int plen,
unsigned char *pheader, unsigned int hlen);
@@ -436,8 +474,8 @@ 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);
void complain(char *message, int lineno, char *file);
void *safe_malloc(size_t size);
char *safe_string_alloc(char *cp);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
@@ -445,6 +483,9 @@ 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);
int retry_send(void);
void prettyprint_time(char *buf, unsigned int t);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask);
/* option.c */
struct daemon *read_opts (int argc, char **argv);
@@ -453,13 +494,15 @@ struct daemon *read_opts (int argc, char **argv);
void forward_init(int first);
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now);
void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
char *tcp_request(struct daemon *daemon, int confd, time_t now);
char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
/* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
void reload_servers(char *fname, struct daemon *daemon);
void check_servers(struct daemon *daemon, struct irec *interfaces);
struct irec *enumerate_interfaces(struct daemon *daemon);
int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
union mysockaddr *test_addrp, struct in_addr *netmaskp);
struct listener *create_wildcard_listeners(int port);
struct listener *create_bound_listeners(struct irec *interfaces, int port);
@@ -469,8 +512,10 @@ void dhcp_packet(struct daemon *daemon, time_t now);
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 match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr);
struct in_addr *addrp, unsigned char *hwaddr,
struct dhcp_netid *netids);
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
@@ -480,22 +525,29 @@ 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);
struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local,
struct dhcp_context *current, struct in_addr netmask,
struct in_addr broadcast, struct in_addr relay,
struct in_addr primary);
/* lease.c */
void lease_update_file(int force, time_t now);
void lease_update_dns(struct daemon *daemon);
void lease_init(struct daemon *daemon, time_t now);
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr);
struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
int clid_len, struct in_addr addr);
int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix);
void lease_set_expires(struct dhcp_lease *lease, time_t exp);
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr,
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 daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now);
int dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, unsigned int sz, time_t now);
/* dnsmasq.c */
int icmp_ping(struct daemon *daemon, struct in_addr addr);
@@ -505,3 +557,10 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr);
void load_dhcp(struct daemon *daemon, time_t now);
#endif
/* netlink.c */
#ifdef HAVE_RTNETLINK
int netlink_init(void);
int netlink_process(struct daemon *daemon, int index,
struct in_addr relay, struct in_addr primary,
struct dhcp_context **retp);
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -197,7 +197,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames))
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
flags = F_NOERR;
if (flags == F_NXDOMAIN || flags == F_NOERR)
@@ -213,12 +213,12 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
{
struct frec *forward;
char *domain = NULL;
int forwardall = 0, type = 0;
int type = 0;
struct all_addr *addrp = NULL;
unsigned short flags = 0;
unsigned short gotname = extract_request(header, (unsigned int)plen, daemon->namebuff, NULL);
struct server *start = NULL;
unsigned int crc = questions_crc(header,(unsigned int)plen);
unsigned int crc = questions_crc(header,(unsigned int)plen, daemon->namebuff);
/* may be recursion not speced or no servers available. */
if (!header->rd || !daemon->servers)
@@ -229,7 +229,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
domain = forward->sentto->domain;
if (!(daemon->options & OPT_ORDER))
{
forwardall = 1;
forward->forwardall = 1;
daemon->last_server = NULL;
}
type = forward->sentto->flags & SERV_TYPE;
@@ -248,6 +248,16 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
if (forward)
{
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
/* In strict_order mode, or when using domain specific servers
always try servers in the order specified in resolv.conf,
otherwise, use the one last known to work. */
@@ -257,17 +267,8 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
else if (!(start = daemon->last_server))
{
start = daemon->servers;
forwardall = 1;
forward->forwardall = 1;
}
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
forward->crc = crc;
header->id = htons(forward->new_id);
}
}
@@ -313,8 +314,9 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
#endif
forwarded = 1;
forward->sentto = start;
if (!forwardall)
if (!forward->forwardall)
break;
forward->forwardall++;
}
}
@@ -341,7 +343,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
}
static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
union mysockaddr *serveraddr, unsigned int n)
unsigned int query_crc, struct server *server, unsigned int n)
{
unsigned char *pheader, *sizep;
unsigned int plen, munged = 0;
@@ -360,25 +362,27 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
/* 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 0;
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
char addrbuff[ADDRSTRLEN];
#ifdef HAVE_IPV6
if (server->addr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &server->addr.in.sin_addr, addrbuff, ADDRSTRLEN);
else if (server->addr.sa.sa_family == AF_INET6)
inet_ntop(AF_INET6, &server->addr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(server->addr.in.sin_addr));
#endif
syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
if (!(daemon->options & OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
@@ -390,7 +394,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
{
if (header->rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
check_for_local_domain(daemon->namebuff, now, daemon))
{
/* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
@@ -400,7 +404,11 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
header->rcode = NOERROR;
}
extract_addresses(header, n, daemon->namebuff, now, daemon);
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (query_crc == questions_crc(header, n, daemon->namebuff))
extract_addresses(header, n, daemon->namebuff, now, daemon);
}
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -442,28 +450,42 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
if (n >= (int)sizeof(HEADER) && header->qr && forward)
{
/* find good server by address if possible, otherwise assume the last one we sent to */
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
struct server *last_server;
daemon->last_server = forward->sentto;
for (last_server = daemon->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))
{
daemon->last_server = last_server;
break;
}
}
if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
struct server *server = forward->sentto;
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
/* find good server by address if possible, otherwise assume the last one we sent to */
struct server *last_server;
for (last_server = daemon->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))
{
server = last_server;
break;
}
}
daemon->last_server = server;
}
if ((n = process_reply(daemon, header, now, forward->crc, server, (unsigned int)n)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
&forward->source, &forward->dest, forward->iface);
forward->new_id = 0; /* cancel */
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
forward->new_id = 0; /* cancel */
}
}
@@ -474,6 +496,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
unsigned short type;
struct iname *tmp;
struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
int m, n, if_index = 0;
struct iovec iov[1];
struct msghdr msg;
@@ -491,6 +514,14 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif
} control_u;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
}
else
dst_addr_4.s_addr = 0;
iov[0].iov_base = daemon->packet;
iov[0].iov_len = daemon->edns_pktsz;
@@ -526,7 +557,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
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;
dst_addr_4 = 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)
@@ -534,7 +565,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
{
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));
dst_addr_4 = 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;
}
@@ -557,7 +588,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (if_index == 0)
return;
if (daemon->if_except || daemon->if_names)
if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
{
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
@@ -567,6 +598,13 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
@@ -610,7 +648,8 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, now);
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon,
dst_addr_4, netmask, now);
if (m >= 1)
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
else
@@ -647,7 +686,8 @@ static int read_write(int fd, char *packet, int size, int rw)
blocking as neccessary, and then return. Note, need to be a bit careful
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
char *tcp_request(struct daemon *daemon, int confd, time_t now)
char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
{
int size = 0, m;
unsigned short qtype, gotname;
@@ -689,7 +729,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
}
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, now);
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon,
local_addr, netmask, now);
if (m == 0)
{
@@ -709,7 +750,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
if (!flags && last_server)
{
struct server *firstsendto = NULL;
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
@@ -774,7 +816,7 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
/* There's no point in updating the cache, since this process will exit and
lose the information after one query. We make this call for the alias and
bogus-nxdomain side-effects. */
m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m);
m = process_reply(daemon, header, now, crc, last_server, (unsigned int)m);
break;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 by 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

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,12 +16,13 @@
static struct dhcp_lease *leases;
static FILE *lease_file;
static int dns_dirty, file_dirty, new_lease;
static int dns_dirty;
enum { no, yes, force } file_dirty;
static int leases_left;
void lease_init(struct daemon *daemon, time_t now)
{
unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3;
unsigned int a0, a1, a2, a3;
unsigned long ei;
time_t expires;
unsigned char hwaddr[ETHER_ADDR_LEN];
@@ -29,9 +30,7 @@ void lease_init(struct daemon *daemon, time_t now)
struct dhcp_lease *lease;
int clid_len = 0;
int has_old = 0;
char *buff = daemon->dhcp_buff;
char *buff2 = daemon->dhcp_buff2;
leases = NULL;
leases_left = daemon->dhcp_max;
@@ -42,9 +41,11 @@ void lease_init(struct daemon *daemon, time_t now)
/* a+ mode lease pointer at end. */
rewind(lease_file);
while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %257s %257s",
&ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3,
buff, buff2) == 13)
/* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */
while (fscanf(lease_file, "%lu %40s %d.%d.%d.%d %255s %764s",
&ei, daemon->dhcp_buff2, &a0, &a1, &a2, &a3,
daemon->dhcp_buff, daemon->packet) == 8)
{
#ifdef HAVE_BROKEN_RTC
if (ei)
@@ -63,41 +64,26 @@ void lease_init(struct daemon *daemon, time_t now)
}
#endif
hwaddr[0] = e0;
hwaddr[1] = e1;
hwaddr[2] = e2;
hwaddr[3] = e3;
hwaddr[4] = e4;
hwaddr[5] = e5;
parse_hex(daemon->dhcp_buff2, hwaddr, ETHER_ADDR_LEN, NULL);
addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
/* decode hex in place */
if (strcmp(buff2, "*") == 0)
if (strcmp(daemon->packet, "*") == 0)
clid_len = 0;
else
{
int s = (strlen(buff2)/3) + 1;
for (clid_len = 0; clid_len < s; clid_len++)
{
buff2[(clid_len*3)+2] = 0;
buff2[clid_len] = strtol(&buff2[clid_len*3], NULL, 16);
}
}
clid_len = parse_hex(daemon->packet, daemon->packet, 255, NULL);
if (!(lease = lease_allocate(buff2, clid_len, addr)))
if (!(lease = lease_allocate(hwaddr, daemon->packet, clid_len, addr)))
die ("too many stored leases", NULL);
lease->expires = expires;
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
if (strcmp(buff, "*") != 0)
lease_set_hostname(lease, buff, daemon->domain_suffix);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix);
}
dns_dirty = 1;
file_dirty = has_old;
new_lease = 0;
file_dirty = has_old ? yes: no;
daemon->lease_fd = fileno(lease_file);
}
@@ -115,18 +101,18 @@ void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
lease_set_hostname(lease, config->hostname, domain);
}
void lease_update_file(int force, time_t now)
void lease_update_file(int always, time_t now)
{
struct dhcp_lease *lease;
int i = force; /* avoid warning */
int i = always; /* avoid warning */
unsigned long expires;
#ifdef HAVE_BROKEN_RTC
if (force || new_lease)
if (always || file_dirty == force)
{
lease_prune(NULL, now);
#else
if (file_dirty)
if (file_dirty != no)
{
#endif
rewind(lease_file);
@@ -149,7 +135,7 @@ void lease_update_file(int force, time_t now)
lease->hwaddr[5], inet_ntoa(lease->addr),
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
if (lease->clid_len)
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
fprintf(lease_file, "%.2x:", lease->clid[i]);
@@ -162,8 +148,7 @@ void lease_update_file(int force, time_t now)
fflush(lease_file);
fsync(fileno(lease_file));
file_dirty = 0;
new_lease = 0;
file_dirty = no;
}
}
@@ -194,7 +179,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
tmp = lease->next;
if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
{
file_dirty = 1;
file_dirty = yes;
*up = lease->next; /* unlink */
if (lease->hostname)
@@ -215,23 +200,21 @@ void lease_prune(struct dhcp_lease *target, time_t now)
}
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr,
unsigned char *clid, int clid_len)
{
struct dhcp_lease *lease;
if (clid_len)
{
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
if (clid)
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
}
else
{
for (lease = leases; lease; lease = lease->next)
if (memcmp(clid, lease->hwaddr, ETHER_ADDR_LEN) == 0)
return lease;
}
for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) &&
memcmp(hwaddr, lease->hwaddr, ETHER_ADDR_LEN) == 0)
return lease;
return NULL;
}
@@ -248,35 +231,29 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
}
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr)
struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
int clid_len, struct in_addr addr)
{
struct dhcp_lease *lease;
if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease))))
return NULL;
lease->clid = NULL;
lease->clid_len = clid_len;
if (clid_len)
{
if (!(lease->clid = malloc(clid_len)))
{
free(lease);
return NULL;
}
memcpy(lease->clid, clid, clid_len);
}
lease->hostname = lease->fqdn = NULL;
lease->hostname = lease->fqdn = NULL;
lease->addr = addr;
memset(lease->hwaddr, 0, ETHER_ADDR_LEN);
lease->expires = 1;
if (!lease_set_hwaddr(lease, hwaddr, clid, clid_len))
{
free(lease);
return NULL;
}
lease->next = leases;
leases = lease;
file_dirty = 1;
new_lease = 1;
file_dirty = force;
leases_left--;
return lease;
@@ -285,18 +262,46 @@ struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_a
void lease_set_expires(struct dhcp_lease *lease, time_t exp)
{
if (exp != lease->expires)
file_dirty = dns_dirty = 1;
{
file_dirty = yes;
dns_dirty = 1;
}
lease->expires = exp;
}
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr)
int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int clid_len)
{
if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0)
{
file_dirty = 1;
file_dirty = force;
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
}
/* only update clid when one is available, stops packets
without a clid removing the record. Lease init uses
clid_len == 0 for no clid. */
if (clid_len != 0 && clid)
{
if (!lease->clid)
lease->clid_len = 0;
if (lease->clid_len != clid_len)
{
file_dirty = force;
if (lease->clid)
free(lease->clid);
if (!(lease->clid = malloc(clid_len)))
return 0;
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
file_dirty = force;
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
return 1;
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
@@ -347,7 +352,8 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
lease->hostname = new_name;
lease->fqdn = new_fqdn;
file_dirty = dns_dirty = 1;
file_dirty = force;
dns_dirty = 1;
}

154
src/netlink.c Normal file
View File

@@ -0,0 +1,154 @@
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
#ifdef HAVE_RTNETLINK
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
int netlink_init(void)
{
struct sockaddr_nl addr;
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0)
return -1; /* no kernel support */
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = getpid();
addr.nl_groups = 0;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
die("cannot bind netlink socket: %s", NULL);
return sock;
}
/* We borrow the DNS packet buffer here. (The DHCP one already has a packet in it)
Since it's used only within this routine, that's fine, just remember
that calling icmp_echo() will trash it */
int netlink_process(struct daemon *daemon, int index, struct in_addr relay,
struct in_addr primary, struct dhcp_context **retp)
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
int len, found_primary = 0;
struct dhcp_context *ret = NULL;
static unsigned int seq = 0;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
if (daemon->netlinkfd == -1)
return 0;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq;
req.g.rtgen_family = AF_INET;
/* Don't block in recvfrom if send fails */
while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
if (len == -1)
{
/* if RTnetlink not configured in the kernel, don't keep trying. */
if (errno == ECONNREFUSED)
{
close(daemon->netlinkfd);
daemon->netlinkfd = -1;
}
return 0;
}
get_next:
while((len = recvfrom(daemon->netlinkfd, daemon->packet, daemon->packet_buff_sz,
MSG_WAITALL, NULL, 0)) == -1 && retry_send());
if (len == -1)
return 0;
h = (struct nlmsghdr *)daemon->packet;
while (NLMSG_OK(h, (unsigned int)len))
{
if (h->nlmsg_seq != seq)
goto get_next;
if (h->nlmsg_type == NLMSG_DONE)
break;
if (h->nlmsg_type == NLMSG_ERROR)
return 0;
if (h->nlmsg_type == RTM_NEWADDR)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
{
struct rtattr *rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr && broadcast.s_addr)
{
ret = complete_context(daemon, addr, ret, netmask, broadcast, relay, primary);
if (addr.s_addr == primary.s_addr)
found_primary = 1;
}
}
}
h = NLMSG_NEXT(h, len);
}
*retp = ret;
return found_primary;
}
#endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,6 +12,11 @@
#include "dnsmasq.h"
static int add_resource_record(HEADER *header, char *limit, int *truncp,
unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type,
unsigned short class, char *format, ...);
static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
unsigned char *name, int isExtract)
{
@@ -19,6 +24,9 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
unsigned int j, l, hops = 0;
int retvalue = 1;
if (isExtract)
*cp = 0;
while ((l = *p++))
{
unsigned int label_type = l & 0xc0;
@@ -117,9 +125,8 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
if (isExtract)
*cp++ = '.';
else
if (*cp != 0 && *cp++ != '.')
retvalue = 2;
else if (*cp != 0 && *cp++ != '.')
retvalue = 2;
}
if ((unsigned int)(p - (unsigned char *)header) >= plen)
@@ -128,7 +135,9 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
if (isExtract)
*--cp = 0; /* terminate: lose final period */
else if (*cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
@@ -324,21 +333,49 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
return ansp;
}
/* CRC all the bytes of the question section. This is used to
safely detect query retransmision. */
unsigned int questions_crc(HEADER *header, unsigned int plen)
/* CRC the question section. This is used to safely detect query
retransmision and to detect answers to questions we didn't ask, which
might be poisoning attacks. Note that we decode the name rather
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. */
unsigned int questions_crc(HEADER *header, unsigned int plen, char *name)
{
unsigned char *start, *end = skip_questions(header, plen);
int q;
unsigned int crc = 0xffffffff;
unsigned char *p1, *p = (unsigned char *)(header+1);
for (q = 0; q < ntohs(header->qdcount); q++)
{
if (!extract_name(header, plen, &p, name, 1))
return crc; /* bad packet */
for (p1 = name; *p1; p1++)
{
int i = 8;
char c = *p1;
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
crc ^= c << 24;
while (i--)
crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
}
/* CRC the class and type as well */
for (p1 = p; p1 < p+4; p1++)
{
int i = 8;
crc ^= *p1 << 24;
while (i--)
crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
}
p += 4;
if ((unsigned int)(p - (unsigned char *)header) > plen)
return crc; /* bad packet */
}
if (end)
for (start = (unsigned char *)(header+1); start < end; start++)
{
int i = 8;
crc ^= *start << 24;
while (i--)
crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
}
return crc;
}
@@ -420,43 +457,6 @@ static int private_net(struct all_addr *addrp)
return 0;
}
static unsigned char *add_text_record(HEADER *header, unsigned int nameoffset, unsigned char *p,
unsigned long ttl, unsigned short pref,
unsigned short type, char *name, int *offset)
{
unsigned char *sav, *cp;
int j;
PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(type, p);
PUTSHORT(C_IN, p);
PUTLONG(ttl, p); /* TTL */
sav = p;
PUTSHORT(0, p); /* dummy RDLENGTH */
if (pref)
PUTSHORT(pref, p);
while (*name)
{
cp = p++;
for (j=0; *name && (*name != '.'); name++, j++)
*p++ = *name;
*cp = j;
if (*name)
name++;
}
*p++ = 0;
j = p - sav - 2;
PUTSHORT(j, sav); /* Real RDLENGTH */
if (offset)
*offset = sav - (unsigned char *)header;
return p;
}
static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
{
for (; doctor; doctor = doctor->next)
@@ -465,14 +465,12 @@ static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *ad
addr->s_addr &= ~doctor->mask.s_addr;
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->nscount = htons(0);
header->arcount = htons(0);
header->aa = 0;
break;
}
}
static int find_soa(HEADER *header, unsigned int qlen)
static int find_soa(HEADER *header, struct doctor *doctor, unsigned int qlen)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -519,6 +517,26 @@ static int find_soa(HEADER *header, unsigned int qlen)
return 0; /* bad packet */
}
if (doctor)
for (i=0; i<ntohs(header->arcount); i++)
{
if (!(p = skip_name(p, header, qlen)))
return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A))
dns_doctor(header, doctor, (struct in_addr *)p);
p += rdlen;
if ((unsigned int)(p - (unsigned char *)header) > qlen)
return 0; /* bad packet */
}
return found_soa ? minttl : 0;
}
@@ -529,8 +547,16 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
{
unsigned char *p, *p1, *endrr;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
unsigned long ttl = 0;
cache_start_insert();
/* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
if (daemon->doctors)
{
searched_soa = 1;
ttl = find_soa(header, daemon->doctors, qlen);
}
/* go through the questions. */
p = (unsigned char *)(header+1);
@@ -540,7 +566,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
int found = 0, cname_count = 5;
struct crec *cpp = NULL;
int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
unsigned long cttl = ULONG_MAX, attl, ttl = 0;
unsigned long cttl = ULONG_MAX, attl;
if (!extract_name(header, qlen, &p, name, 1))
return; /* bad packet */
@@ -609,7 +635,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, NULL, qlen);
}
if (ttl)
cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
@@ -693,7 +719,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, NULL, qlen);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit it's TTL */
@@ -733,11 +759,11 @@ unsigned short extract_request(HEADER *header,unsigned int qlen, char *name, uns
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (typep)
*typep = qtype;
if (qclass == C_IN)
{
if (typep)
*typep = qtype;
if (qtype == T_A)
return F_IPV4;
if (qtype == T_AAAA)
@@ -773,13 +799,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
header->rcode = NOERROR;
header->ancount = htons(1);
header->aa = 1;
PUTSHORT (sizeof(HEADER) | 0xc000, p);
PUTSHORT(T_A, p);
PUTSHORT(C_IN, p);
PUTLONG(ttl, p); /* TTL */
PUTSHORT(INADDRSZ, p);
memcpy(p, addrp, INADDRSZ);
p += INADDRSZ;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (p && flags == F_IPV6)
@@ -787,13 +807,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
header->rcode = NOERROR;
header->ancount = htons(1);
header->aa = 1;
PUTSHORT (sizeof(HEADER) | 0xc000, p);
PUTSHORT(T_AAAA, p);
PUTSHORT(C_IN, p);
PUTLONG(ttl, p); /* TTL */
PUTSHORT(IN6ADDRSZ, p);
memcpy(p, addrp, IN6ADDRSZ);
p += IN6ADDRSZ;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
}
#endif
else /* nowhere to forward to */
@@ -803,16 +817,22 @@ int setup_reply(HEADER *header, unsigned int qlen,
}
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
int check_for_local_domain(char *name, time_t now, struct mx_record *mx)
int check_for_local_domain(char *name, time_t now, struct daemon *daemon)
{
struct crec *crecp;
struct mx_srv_record *mx;
struct txt_record *txt;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
for (; mx; mx = mx->next)
if (hostname_isequal(name, mx->mxname))
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name))
return 1;
for (txt = daemon->txt; txt; txt = txt->next)
if (hostname_isequal(name, txt->name))
return 1;
return 0;
@@ -862,8 +882,101 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
return 0;
}
static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
unsigned char *sav, *p = *pp;
int j;
unsigned short usval;
long lval;
char *sval;
if (truncp && *truncp)
return 0;
PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(type, p);
PUTSHORT(class, p);
PUTLONG(ttl, p); /* TTL */
sav = p; /* Save pointer to RDLength field */
PUTSHORT(0, p); /* Placeholder RDLength */
va_start(ap, format); /* make ap point to 1st unamed argument */
for (; *format; format++)
switch (*format)
{
#ifdef HAVE_IPV6
case '6':
sval = va_arg(ap, char *);
memcpy(p, sval, IN6ADDRSZ);
p += IN6ADDRSZ;
break;
#endif
case '4':
sval = va_arg(ap, char *);
memcpy(p, sval, INADDRSZ);
p += INADDRSZ;
break;
case 's':
usval = va_arg(ap, int);
PUTSHORT(usval, p);
break;
case 'l':
lval = va_arg(ap, long);
PUTLONG(lval, p);
break;
case 'd':
/* get domain-name answer arg and store it in RDATA field */
if (offset)
*offset = p - (unsigned char *)header;
sval = va_arg(ap, char *);
while (sval && *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
*p++ = *sval;
*cp = j;
if (*sval)
sval++;
}
*p++ = 0;
break;
case 't':
usval = va_arg(ap, int);
sval = va_arg(ap, char *);
memcpy(p, sval, usval);
p += usval;
break;
}
va_end(ap); /* clean up variable argument pointer */
j = p - sav - 2;
PUTSHORT(j, sav); /* Now, store real RDLength */
/* check for overflow of buffer */
if (limit && ((unsigned char *)limit - p) < 0)
{
if (truncp)
*truncp = 1;
return 0;
}
*pp = p;
return 1;
}
/* 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, struct daemon *daemon, time_t now)
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon,
struct in_addr local_addr, struct in_addr local_netmask, time_t now)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
@@ -872,11 +985,12 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
unsigned int nameoffset;
unsigned short flag;
int qdcount = ntohs(header->qdcount);
int q, ans, anscount;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;
struct crec *crecp;
int nxdomain, auth;
int nxdomain = 0, auth = 1, trunc = 0;
struct mx_srv_record *rec;
if (!qdcount || header->opcode != QUERY )
return 0;
@@ -907,6 +1021,9 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
dryrun = 1;
}
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
rerun:
/* determine end of question section (we put answers there) */
if (!(ansp = skip_questions(header, qlen)))
@@ -914,7 +1031,6 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
/* now process each question, answers go in RRs after the question */
p = (unsigned char *)(header+1);
nxdomain = 0, auth = 1, anscount = 0;
for (q=0; q<qdcount; q++)
{
@@ -934,127 +1050,115 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
ans = 0; /* have we answered this question */
if (qclass == C_CHAOS && qtype == T_TXT)
/* special query to get version. */
if (qtype == T_TXT || qtype == T_ANY)
{
ans = 1;
if (!dryrun)
struct txt_record *t;
for(t = daemon->txt; t ; t = t->next)
{
int len;
if (hostname_isequal(name, "version.bind"))
sprintf(name, "dnsmasq-%s", VERSION);
else if (hostname_isequal(name, "authors.bind"))
sprintf(name, "Simon Kelley");
else if (hostname_isequal(name, "copyright.bind"))
sprintf(name, COPYRIGHT);
else
*name = 0;
len = strlen(name);
PUTSHORT(nameoffset | 0xc000, ansp);
PUTSHORT(T_TXT, ansp);
PUTSHORT(C_CHAOS, ansp);
PUTLONG(0, ansp);
PUTSHORT(len+1, ansp);
*ansp++ = len;
memcpy(ansp, name, len);
ansp += len;
anscount++;
}
}
else if (qclass == C_IN)
{
if ((daemon->options & OPT_FILTER) &&
(qtype == T_SOA || qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
{
ans = 1;
log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
}
else
{
if (qtype == T_PTR || qtype == T_ANY)
if (t->class == qclass && hostname_isequal(name, t->name))
{
if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
if (!dryrun)
{
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
nxdomain = 1;
}
}
}
else do
{
/* 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)
{
ans = 1;
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
}
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
ans = 1;
if (!dryrun)
{
unsigned long ttl;
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
ansp = add_text_record(header, nameoffset, ansp, ttl, 0, T_PTR,
cache_get_name(crecp), NULL);
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
0, daemon->addn_hosts, crecp->uid);
anscount++;
/* if last answer exceeded packet size, give up */
if (((unsigned char *)limit - ansp) < 0)
return 0;
}
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
ans = 1;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
if (!dryrun &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL,
T_TXT, t->class, "t", t->len, t->txt))
anscount++;
}
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
{
unsigned short type = T_A;
int addrsz = INADDRSZ;
if (flag == F_IPV6)
{
#ifdef HAVE_IPV6
type = T_AAAA;
addrsz = IN6ADDRSZ;
#else
break;
#endif
}
if (qtype != type && qtype != T_ANY)
continue;
}
}
cname_restart:
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)))
if (qclass == C_IN)
{
if (qtype == T_PTR || qtype == T_ANY)
{
if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
}
}
else do
{
/* 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)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
if (!dryrun)
{
unsigned long ttl;
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
0, daemon->addn_hosts, crecp->uid);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL,
T_PTR, C_IN, "d", cache_get_name(crecp)))
anscount++;
}
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
}
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
{
unsigned short type = T_A;
if (flag == F_IPV6)
#ifdef HAVE_IPV6
type = T_AAAA;
#else
break;
#endif
if (qtype != type && qtype != T_ANY)
continue;
cname_restart:
if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
{
int localise = 0;
/* See if a putative address is on the network from which we recieved
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
{
struct crec *save = crecp;
do {
if ((crecp->flags & F_HOSTS) &&
is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
{
localise = 1;
break;
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
crecp = save;
}
do
{
/* don't answer wildcard queries with data not from /etc/hosts
or DHCP leases */
@@ -1065,29 +1169,37 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
{
if (!dryrun)
{
ansp = add_text_record(header, nameoffset, ansp, crecp->ttd - now, 0, T_CNAME,
cache_get_name(crecp->addr.cname.cache), &nameoffset);
anscount++;
log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset,
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
anscount++;
}
strcpy(name, cache_get_name(crecp->addr.cname.cache));
goto cname_restart;
}
if (crecp->flags & F_NEG)
{
ans = 1;
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
{
log_query(crecp->flags, name, NULL, 0, NULL, 0);
auth = 0;
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
}
log_query(crecp->flags, name, NULL, 0, NULL, 0);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
/* If we are returning local answers depending on network,
filter here. */
if (localise &&
(crecp->flags & F_HOSTS) &&
!is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
continue;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
ans = 1;
if (!dryrun)
{
@@ -1098,86 +1210,151 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
else
ttl = crecp->ttd - now;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
auth = 0;
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
0, daemon->addn_hosts, crecp->uid);
/* copy question as first part of answer (use compression) */
PUTSHORT(nameoffset | 0xc000, ansp);
PUTSHORT(type, ansp);
PUTSHORT(C_IN, ansp);
PUTLONG(ttl, ansp); /* TTL */
PUTSHORT(addrsz, ansp);
memcpy(ansp, &crecp->addr, addrsz);
ansp += addrsz;
anscount++;
if (((unsigned char *)limit - ansp) < 0)
return 0;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN,
type == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
}
}
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
}
if (qtype == T_MX || qtype == T_ANY)
{
struct mx_record *mx;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->mxname))
break;
if (mx)
}
if (qtype == T_MX || qtype == T_ANY)
{
int found = 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (!rec->issrv && hostname_isequal(name, rec->name))
{
ans = found = 1;
if (!dryrun)
{
ans = 1;
if (!dryrun)
int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
{
ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX,
mx->mxtarget ? mx->mxtarget : daemon->mxtarget, NULL);
anscount++;
if (rec->target)
rec->offset = offset;
}
}
else if ((daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
{
ans = 1;
if (!dryrun)
{
ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget, NULL);
anscount++;
}
}
if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
{
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_MX, C_IN, "sd", 1,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
anscount++;
}
}
}
if (qtype == T_SRV || qtype == T_ANY)
{
int found = 0;
if (qtype == T_MAILB)
ans = 1, nxdomain = 1;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name))
{
found = ans = 1;
if (!dryrun)
{
int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
{
anscount++;
if (rec->target)
rec->offset = offset;
}
}
}
if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
{
ans = 1;
if (!dryrun)
log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0);
}
}
if (qtype == T_MAILB)
ans = 1, nxdomain = 1;
if (qtype == T_SOA && (daemon->options & OPT_FILTER))
{
ans = 1;
if (!dryrun)
log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
}
}
if (!ans || ((unsigned char *)limit - ansp) < 0)
if (!ans)
return 0; /* failed to answer a question */
}
if (dryrun)
{
dryrun = 0;
goto rerun;
}
/* create an additional data section, for stuff in SRV and MX record replies. */
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->offset != 0)
{
/* squash dupes */
struct mx_srv_record *tmp;
for (tmp = rec->next; tmp; tmp = tmp->next)
if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
tmp->offset = 0;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
{
unsigned long ttl;
#ifdef HAVE_IPV6
int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
#else
int type = T_A;
#endif
if (crecp->flags & F_NEG)
continue;
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN,
crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
addncount++;
}
}
/* done all questions, set up header and return length of result */
header->qr = 1; /* response */
header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
header->ra = 1; /* recursion if available */
header->tc = 0; /* truncation */
header->tc = trunc; /* truncation */
if (anscount == 0 && nxdomain)
header->rcode = NXDOMAIN;
else
header->rcode = NOERROR; /* no error */
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(0);
header->arcount = htons(addncount);
return ansp - (unsigned char *)header;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_VENDOR_CLASS_OPT 43
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
@@ -54,37 +55,32 @@
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
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,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static unsigned int option_len(unsigned char *opt);
static int option_len(unsigned char *opt);
static void *option_ptr(unsigned char *opt);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize);
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize);
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
struct daemon *daemon,
char *hostname,
struct in_addr iface_addr,
struct dhcp_netid *netid,
struct in_addr subnet_addr);
static int have_config(struct dhcp_config *config, unsigned int mask)
int dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, unsigned int sz, time_t now)
{
return config && (config->flags & 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;
struct dhcp_context *context_tmp;
unsigned char *opt, *clid = NULL;
struct dhcp_lease *lease, *ltmp;
struct dhcp_lease *ltmp, *lease = NULL;
struct dhcp_vendor *vendor;
struct dhcp_netid_list *id_list;
int clid_len = 0, ignore = 0;
@@ -149,35 +145,46 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
clid_len = option_len(opt);
clid = option_ptr(opt);
}
else
clid = mess->chaddr;
/* do we have a lease in store? */
lease = lease_find_by_client(mess->chaddr, clid, clid_len);
/* If this request is missing a clid, but we've seen one before,
use it again for option matching etc. */
if (lease && !clid && lease->clid)
{
clid_len = lease->clid_len;
clid = lease->clid;
}
}
/* Determine network for this packet. If the machine has an address already, and we don't have
have a giaddr or explicit subnet selector, use the ciaddr. This is necessary because a
machine which got a lease via a relay won't use the relay to renew. */
addr =
subnet_addr.s_addr ? subnet_addr :
(mess->giaddr.s_addr ? mess->giaddr :
(mess->ciaddr.s_addr ? mess->ciaddr : iface_addr));
/* More than one context may match, we build a chain of them all on ->current
Note that if netmasks, netid or lease times don't match, odd things may happen. */
for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context;
context = context_tmp;
}
/* Determine network for this packet. Our caller will have already linked all the
contexts which match the addresses of the receiving interface but if the
machine has an address already, or came via a relay, or we have a subnet selector,
we search again. If we don't have have a giaddr or explicit subnet selector,
use the ciaddr. This is necessary because a machine which got a lease via a
relay won't use the relay to renew. */
if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
{
addr =
subnet_addr.s_addr ? subnet_addr :
(mess->giaddr.s_addr ? mess->giaddr : mess->ciaddr);
for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context;
context = context_tmp;
}
}
if (!context)
{
syslog(LOG_WARNING, "no address range available for DHCP request %s %s",
subnet_addr.s_addr ? "with subnet selector" : "via",
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
subnet_addr.s_addr ? "with subnet selector" : "via",
subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
return 0;
}
@@ -211,7 +218,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if (have_config(config, CONFIG_NAME))
hostname = config->hostname;
if (context->netid.net)
if (context->netid.net && !(context->flags & CONTEXT_FILTER))
{
context->netid.next = netid;
netid = &context->netid;
@@ -237,9 +244,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
message = "disabled";
p = do_req_options(context, p, end, NULL, daemon,
hostname, iface_addr, netid, subnet_addr);
hostname, netid, subnet_addr);
/* must do this after do_req_options since it overwrites filename field. */
mess->siaddr = iface_addr;
mess->siaddr = context->local;
bootp_option_put(mess, daemon->boot_config, netid);
p = option_end(p, end, mess);
log_packet(NULL, logaddr, mess->chaddr, iface_name, message);
@@ -255,7 +262,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
hostname = config->hostname;
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
{
unsigned int len = option_len(opt);
int len = option_len(opt);
hostname = daemon->dhcp_buff;
memcpy(hostname, option_ptr(opt), len);
/* May not be zero terminated */
@@ -291,7 +298,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
unsigned char *ucp = option_ptr(opt);
unsigned int tmp, j;
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)
@@ -304,7 +311,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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;
int i;
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
{
@@ -321,14 +328,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* 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);
clid = NULL;
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
{
req_options = daemon->dhcp_buff2;
@@ -340,7 +341,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
{
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
(context->local.s_addr != option_addr(opt).s_addr))
return 0;
/* sanitise any message. Paranoid? Moi? */
@@ -382,7 +383,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
case DHCPRELEASE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
(context->local.s_addr != option_addr(opt).s_addr))
return 0;
if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
@@ -407,7 +408,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr;
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr))
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, netid))
message = "no address available";
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
@@ -415,7 +416,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
return 0;
context = narrow_context(context, mess->yiaddr);
if (context->netid.net)
if (context->netid.net && !(context->flags & CONTEXT_FILTER))
{
context->netid.next = netid;
netid = &context->netid;
@@ -431,10 +432,10 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
else if (lease && lease->expires != 0)
time = (unsigned int)difftime(lease->expires, now);
mess->siaddr = iface_addr;
mess->siaddr = context->local;
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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
if (time != 0xffffffff)
@@ -443,7 +444,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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);
NULL, netid, subnet_addr);
p = option_end(p, end, mess);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
@@ -460,7 +461,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
{
/* SELECTING */
if (iface_addr.s_addr != option_addr(opt).s_addr)
if (context->local.s_addr != option_addr(opt).s_addr)
return 0;
/* If a lease exists for this host and another address, squash it. */
@@ -518,12 +519,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* Check to see if the address is reserved as a static address for another host */
else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
message ="address reserved";
message = "address reserved";
else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
message = "address in use";
else if (!lease && !(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
else if (!lease && !(lease = lease_allocate(mess->chaddr, clid, clid_len, mess->yiaddr)))
message = "no leases left";
}
@@ -542,7 +543,7 @@ 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)
if (context->netid.net && !(context->flags & CONTEXT_FILTER))
{
context->netid.next = netid;
netid = &context->netid;
@@ -556,17 +557,17 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
time = req_time;
}
lease_set_hwaddr(lease, mess->chaddr);
lease_set_hwaddr(lease, mess->chaddr, clid, clid_len);
if (!hostname)
hostname = host_from_dns(daemon, mess->yiaddr);
if (hostname)
lease_set_hostname(lease, hostname, daemon->domain_suffix);
lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time);
mess->siaddr = iface_addr;
mess->siaddr = context->local;
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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
if (time != 0xffffffff)
{
@@ -576,7 +577,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
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);
hostname, netid, subnet_addr);
}
p = option_end(p, end, mess);
@@ -598,14 +599,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
netid = &context->netid;
}
mess->siaddr = iface_addr;
mess->siaddr = context->local;
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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.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);
hostname, netid, subnet_addr);
p = option_end(p, end, mess);
log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
@@ -628,7 +629,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr,
string ? string : "");
}
static unsigned int option_len(unsigned char *opt)
static int option_len(unsigned char *opt)
{
return opt[1];
}
@@ -720,7 +721,7 @@ static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dh
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
{
int len = strlen(string);
size_t len = strlen(string);
if (p + len + 3 < end)
{
@@ -769,7 +770,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, unsigned int minsize)
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize)
{
int overload = 0;
unsigned char *ret;
@@ -805,36 +806,9 @@ 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_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt)
{
@@ -850,16 +824,50 @@ static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *
return netid ? option_find2(NULL, opts, opt) : NULL;
}
static unsigned char *do_opt(struct dhcp_opt *opt, unsigned char *p, unsigned char *end, struct in_addr local)
{
if (p + opt->len + 3 >= end)
return p;
*(p++) = opt->opt;
*(p++) = opt->len;
if (opt->len == 0)
return p;
if (opt->is_addr && !opt->vendor_class)
{
int j;
struct in_addr *a = (struct in_addr *)opt->val;
for (j = 0; j < opt->len; j+=INADDRSZ, a++)
{
/* zero means "self" (but not in vendorclass options.) */
if (a->s_addr == 0)
memcpy(p, &local, INADDRSZ);
else
memcpy(p, a, INADDRSZ);
p += INADDRSZ;
}
}
else
{
memcpy(p, opt->val, opt->len);
p += opt->len;
}
return p;
}
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
struct daemon *daemon,
char *hostname,
struct in_addr iface_addr,
struct dhcp_netid *netid,
struct in_addr subnet_addr)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
char *vendor_class = NULL;
if (in_list(req_options, OPTION_MAXMESSAGE))
p = option_put(p, end, OPTION_MAXMESSAGE, 2, end - (unsigned char *)daemon->dhcp_packet);
@@ -888,7 +896,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
if (in_list(req_options, OPTION_DNSSERVER) &&
!option_find2(netid, config_opts, OPTION_DNSSERVER))
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) &&
!option_find2(netid, config_opts, OPTION_DOMAINNAME))
@@ -904,8 +912,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
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)
opt != option_find2(netid, config_opts, opt->opt))
continue;
/* For the options we have default values on
@@ -917,32 +924,54 @@ static unsigned char *do_req_options(struct dhcp_context *context,
opt->opt == OPTION_ROUTER ||
opt->opt == OPTION_DNSSERVER))
continue;
*(p++) = opt->opt;
*(p++) = opt->len;
if (opt->len == 0)
continue;
if (opt->is_addr)
/* opt->val has terminating zero */
if (opt->opt == OPTION_VENDOR_ID)
vendor_class = opt->val;
else
p = do_opt(opt, p, end, context->local);
}
if (in_list(req_options, OPTION_VENDOR_ID))
{
for (opt = daemon->vendor_opts; opt; opt = opt->next)
if (!opt->netid || match_netid(opt->netid, netid))
{
if (vendor_class && strcmp(vendor_class, opt->vendor_class) != 0)
syslog(LOG_WARNING, "More than one vendor class matches, using %s", vendor_class);
else
vendor_class = opt->vendor_class;
}
if (vendor_class)
{
int j;
struct in_addr *a = (struct in_addr *)opt->val;
for (j = 0; j < opt->len; j+=INADDRSZ, a++)
p = option_put_string(p, end, OPTION_VENDOR_ID, vendor_class);
if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
{
/* zero means "self" */
if (a->s_addr == 0)
memcpy(p, &iface_addr, INADDRSZ);
else
memcpy(p, a, INADDRSZ);
p += INADDRSZ;
unsigned char *plen, *oend = end;
/* encapsulated options can only be 256 bytes,
even of the packet is larger */
if (p + 256 < end)
oend = p + 256;
if (p + 3 >= oend)
return p;
*(p++) = OPTION_VENDOR_CLASS_OPT;
plen = p++; /* fill in later */
for (opt = daemon->vendor_opts; opt; opt = opt->next)
if ((!opt->netid || match_netid(opt->netid, netid)) &&
strcmp(vendor_class, opt->vendor_class) == 0)
p = do_opt(opt, p, oend, context->local);
*plen = p - plen - 1;
}
}
else
{
memcpy(p, opt->val, opt->len);
p += opt->len;
}
}
}
return p;
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -113,27 +113,29 @@ int legal_char(char c)
int canonicalise(char *s)
{
/* check for legal chars and remove trailing .
also fail empty string. */
int l = strlen(s);
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(s);
char c;
if (l == 0) return 0;
if (l == 0 || l > MAXDNAME) return 0;
if (s[l-1] == '.')
{
if (l == 1) return 0;
s[l-1] = 0;
}
while ((c = *s++))
if (c != '.' && !legal_char(c))
if (c == '.')
dotgap = 0;
else if (!legal_char(c) || (++dotgap > MAXLABEL))
return 0;
return 1;
}
/* for use during startup */
void *safe_malloc(int size)
void *safe_malloc(size_t size)
{
void *ret = malloc(size);
@@ -156,7 +158,7 @@ char *safe_string_alloc(char *cp)
return ret;
}
void complain(char *message, char *arg1)
static void log_err(char *message, char *arg1)
{
char *errmess = strerror(errno);
@@ -170,9 +172,17 @@ void complain(char *message, char *arg1)
syslog(LOG_CRIT, message, arg1, errmess);
}
void complain(char *message, int lineno, char *file)
{
char buff[256];
sprintf(buff, "%s at line %d of %s", message, lineno, file);
log_err(buff, NULL);
}
void die(char *message, char *arg1)
{
complain(message, arg1);
log_err(message, arg1);
syslog(LOG_CRIT, "FAILED to start up");
exit(1);
}
@@ -268,3 +278,50 @@ int retry_send(void)
return 0;
}
void prettyprint_time(char *buf, unsigned int t)
{
if (t == 0xffffffff)
sprintf(buf, "infinite");
else
{
unsigned int x, p = 0;
if ((x = t/3600))
p += sprintf(&buf[p], "%dh", x);
if ((x = (t/60)%60))
p += sprintf(&buf[p], "%dm", x);
if ((x = t%60))
p += sprintf(&buf[p], "%ds", x);
}
}
/* in may equal out, when maxlen may be -1 (No max len). */
int parse_hex(char *in, unsigned char *out, int maxlen, unsigned int *wildcard_mask)
{
int mask = 0, i = 0;
char *r;
while (maxlen == -1 || i < maxlen)
{
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
if (*r == 0)
maxlen = i;
if (r != in )
{
*r = 0;
mask = mask << 1;
if (strcmp(in, "*") == 0)
mask |= 1;
else
out[i] = strtol(in, NULL, 16);
i++;
}
in = r+1;
}
if (wildcard_mask)
*wildcard_mask = mask;
return i;
}