Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91dccd0958 | ||
|
|
0a852541d3 | ||
|
|
f6b7dc47c7 |
124
CHANGELOG
124
CHANGELOG
@@ -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
30
FAQ
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.19
|
||||
Version: 2.22
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.19
|
||||
Version: 2.22
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Productivity/Networking/DNS/Servers
|
||||
|
||||
117
dnsmasq.8
117
dnsmasq.8
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
2
doc.html
2
doc.html
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
90
src/cache.c
90
src/cache.c
@@ -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);
|
||||
}
|
||||
|
||||
20
src/config.h
20
src/config.h
@@ -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
|
||||
|
||||
263
src/dhcp.c
263
src/dhcp.c
@@ -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;
|
||||
}
|
||||
|
||||
114
src/dnsmasq.c
114
src/dnsmasq.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
143
src/dnsmasq.h
143
src/dnsmasq.h
@@ -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
|
||||
|
||||
160
src/forward.c
160
src/forward.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
150
src/lease.c
150
src/lease.c
@@ -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
154
src/netlink.c
Normal 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
|
||||
|
||||
|
||||
115
src/network.c
115
src/network.c
@@ -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 ||
|
||||
|
||||
752
src/option.c
752
src/option.c
File diff suppressed because it is too large
Load Diff
695
src/rfc1035.c
695
src/rfc1035.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
281
src/rfc2131.c
281
src/rfc2131.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
75
src/util.c
75
src/util.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user