Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b7dc47c7 | ||
|
|
bb01cb9604 | ||
|
|
59353a6b56 | ||
|
|
26128d2747 |
118
CHANGELOG
118
CHANGELOG
@@ -1253,3 +1253,121 @@ version 2.16
|
||||
Added dynamic-dnsmasq from Peter Willis to the contrib
|
||||
section.
|
||||
|
||||
version 2.17
|
||||
Correctly deduce the size of numeric dhcp-options, rather
|
||||
than making wild guesses. Also cope with negative values.
|
||||
|
||||
Fixed use of C library reserved symbol "index" which broke
|
||||
under certain combinations of library and compiler.
|
||||
|
||||
Make bind-interfaces work for IPv6 interfaces too.
|
||||
|
||||
Warn if an interface is given for listening which doesn't
|
||||
currently exist when not in bind-interfaces mode. (This is
|
||||
already a fatal error when bind-interfaces is set.)
|
||||
|
||||
Allow the --interface and --except-interface options to
|
||||
take a comma-separated list of interfaces.
|
||||
|
||||
Tweak --dhcp-userclass matching code to work with the
|
||||
ISC dhclient which violates RFC3004 unless its
|
||||
configuration is very warped. Thanks to Cedric Duval for
|
||||
the bug report.
|
||||
|
||||
Allow more than one network-id tag in a dhcp-option. All
|
||||
the tags must match to enable the option.
|
||||
|
||||
Added dhcp-ignore option to disable classes of hosts based
|
||||
on network-id tags. Also allow BOOTP options to be
|
||||
controlled by network tags.
|
||||
|
||||
Fill in sname, file and siaddr fields in replies to
|
||||
DHCPINFORM messages.
|
||||
|
||||
Don't send NAK replies to DHCPREQUEST packets for disabled
|
||||
clients. Credit to Cedric Duval for spotting this.
|
||||
|
||||
Fix rare crash associated with long DNS names and CNAME
|
||||
records. Thanks to Holger Hoffstatte and especially Steve
|
||||
Grecni for help chasing that one down.
|
||||
|
||||
version 2.18
|
||||
Reworked the Linux interface discovery code (again) to
|
||||
cope with interfaces which have only IPv6 addresses and
|
||||
interfaces with more than one IPv6 address. Thanks to
|
||||
Martin Pels for help with that.
|
||||
|
||||
Fix problems which occured when more than one dhcp-range
|
||||
was specified in the same subnet: sometimes parameters
|
||||
(lease time, network-id tag) from the wrong one would be
|
||||
used. Thanks to Rory Campbell-Lange for the bug report.
|
||||
|
||||
Reset cache statistics when clearing the cache.
|
||||
|
||||
Enable long command line options on FreeBSD when the
|
||||
C library supports them.
|
||||
|
||||
version 2.19
|
||||
Tweaked the Linux-only interface discovery code to cope
|
||||
with interface-indexes larger than 8 bits in
|
||||
/proc/net/if_inet6. This only affects Linux, obviously.
|
||||
Thanks to Richard Atterer for the bug report.
|
||||
|
||||
Check for under-length option fields in DHCP packets, a
|
||||
zero length client-id, in particluar, could seriously
|
||||
confuse dnsmasq 'till now. Thanks to Will Murname for help
|
||||
with that.
|
||||
|
||||
If a DHCP-allocated address has an associated name in
|
||||
/etc/hosts, and the client does not provide a hostname
|
||||
parameter and there is no hostname in a matching dhcp-host
|
||||
option, send the /etc/hosts name as the hostname in
|
||||
the DHCP lease. Thanks to Will Murname for the suggestion.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
||||
17
FAQ
17
FAQ
@@ -115,7 +115,7 @@ A: Resolver code sometime does strange things when given names without
|
||||
--expand-hosts and --domain-suffix options.
|
||||
|
||||
Q: Can I get dnsmasq to save the contents of its cache to disk when
|
||||
I shut my machine down and re-load when it starts again.
|
||||
I shut my machine down and re-load when it starts again?
|
||||
|
||||
A: No, that facility is not provided. Very few names in the DNS have
|
||||
their time-to-live set for longer than a few hours so most of the
|
||||
@@ -299,7 +299,22 @@ A: Because when a Gentoo box shuts down, it releases its lease with
|
||||
dnsmasq ignores it until is times out and restarts the process.
|
||||
To fix this, set the dhcp-authoritative flag in dnsmasq.
|
||||
|
||||
Q: My laptop has two network interfaces, a wired one and a wireless
|
||||
one. I never use both interfaces at the same time, and I'd like the
|
||||
same IP and configuration to be used irrespcetive of which
|
||||
interface is in use. How can I do that.
|
||||
|
||||
A: By default, the identity of a machine is determined by using the
|
||||
MAC address, which is associated with interface hardware. Once an
|
||||
IP is bound to the MAC address of one interface, it cannot be
|
||||
associated with another MAC address until after the DHCP lease
|
||||
expires. The solution to this is to use a client-id as the machine
|
||||
identity rather than the MAC address. If you arrange for the same
|
||||
client-id to sent when either interface is in use, the DHCP server
|
||||
will recognise the same machine, and use the same address. The
|
||||
method for setting the client-id varies with DHCP client software,
|
||||
dhcpcd uses the "-I" flag. Windows uses a registry setting,
|
||||
see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.16
|
||||
Version: 2.20
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.16
|
||||
Version: 2.20
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Productivity/Networking/DNS/Servers
|
||||
|
||||
113
dnsmasq.8
113
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,23 @@ 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 \-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
|
||||
@@ -307,7 +336,8 @@ 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
|
||||
@@ -366,7 +396,7 @@ have exactly the same effect as
|
||||
.B --dhcp-host
|
||||
options containing the same information.
|
||||
.TP
|
||||
.B \-O, --dhcp-option=[network-id,]<opt>,[<value>[,<value>]]
|
||||
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]]<opt>,[<value>[,<value>]]
|
||||
Specfify different or extra options to DHCP clients. By default,
|
||||
dnsmasq sends some standard options to DHCP clients, the netmask and
|
||||
broadcast address are set to the same as the host running dnsmasq, and
|
||||
@@ -380,14 +410,12 @@ 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
|
||||
and a text string. If the optional network-id is given then
|
||||
this option is only sent to machines on the network whose dhcp-range
|
||||
contains a matching network-id.
|
||||
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, and there are option numbers for which it is not
|
||||
possible to generate the correct data type; it is quite possible to
|
||||
option number is sent, it is quite possible to
|
||||
persuade dnsmasq to generate illegal DHCP packets with injudicious use
|
||||
of this flag.
|
||||
.TP
|
||||
@@ -412,10 +440,17 @@ to different classes of hosts. It is possible, for instance to use
|
||||
this to set a different printer server for hosts in the class
|
||||
"accounts" than for hosts in the class "engineering".
|
||||
.TP
|
||||
.B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]]
|
||||
.B \ -J, --dhcp-ignore=<network-id>[,<network-id>]
|
||||
When all the given network-ids match the set of network-ids derived
|
||||
from the net, host, vendor and user classes, ignore the host and do
|
||||
not allocate it a DHCP lease.
|
||||
.TP
|
||||
.B \-M, --dhcp-boot=[net:<network-id>,]<filename>,[<servername>[,<server address>]]
|
||||
Set BOOTP options to be returned by the DHCP server. These are needed
|
||||
for machines which network boot, and tell the machine where to collect
|
||||
its initial configuration.
|
||||
its initial configuration. If the optional network-id(s) are given,
|
||||
they must match for this configuration to be sent. Note that
|
||||
network-ids are prefixed by "net:" to distinguish them.
|
||||
.TP
|
||||
.B \-X, --dhcp-lease-max=<number>
|
||||
Limits dnsmasq to the specified maximum number of DHCP leases. The
|
||||
@@ -540,6 +575,40 @@ and run dnsmasq with the
|
||||
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
|
||||
.B dhcp-range
|
||||
used to allocate the address, one from any matching
|
||||
.B dhcp-host
|
||||
and possibly many from matching vendor classes and user
|
||||
classes sent by the DHCP client. Any
|
||||
.B dhcp-option
|
||||
which has network-id tags will be used in preference to an untagged
|
||||
.B dhcp-option,
|
||||
provided that _all_ the tags match somewhere in the
|
||||
set collected as described above. The prefix '#' on a tag means 'not'
|
||||
so --dhcp=option=#purple,3,1.2.3.4 sends the option when the
|
||||
network-id tag purple is not in the set of valid tags.
|
||||
.PP
|
||||
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=
|
||||
|
||||
@@ -292,6 +285,50 @@ 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.
|
||||
|
||||
# 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
|
||||
|
||||
# For debugging purposes, log each DNS query as it passes through
|
||||
# dnsmasq.
|
||||
#log-queries
|
||||
|
||||
42
doc.html
42
doc.html
@@ -23,7 +23,8 @@ Mac OS X.
|
||||
Dnsmasq is included in at least the following Linux distributions:
|
||||
Gentoo, Debian, Slackware, Suse,
|
||||
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, CoyoteLinux and
|
||||
Clarkconnect. It is also available as a FreeBSD port and is used in Linksys wireless routers.
|
||||
Clarkconnect. It is also available as a FreeBSD port and is used in
|
||||
Linksys wireless routers and the m0n0wall project.
|
||||
<P>
|
||||
Dnsmasq provides the following features:
|
||||
<DIR>
|
||||
@@ -41,22 +42,18 @@ machine: If the names of local machines are there, then they can all
|
||||
be addressed without having to maintain /etc/hosts on each machine.
|
||||
</LI>
|
||||
<LI>
|
||||
Dnsmasq will serve names from the DHCP leases file on the firewall machine:
|
||||
If machines specify a hostname when they take out a DHCP lease, then they are
|
||||
addressable in the local DNS. <B>UPDATE</B> Dnsmasq version 2 now offers an integrated DHCP server
|
||||
instead of the lease file reader. This gives better control of the
|
||||
interaction with new functions (for example fixed IP leasess and
|
||||
attaching names to ethernet addresses centrally) it's also much
|
||||
smaller than dnsmasq and ISC dhcpd which is important for router distros.
|
||||
The integrated DHCP server supports static and dynamic DHCP leases and
|
||||
multiple networks and IP ranges. It works across BOOTP relays and
|
||||
supports DHCP options including RFC3397 DNS search lists.
|
||||
Machines which are configured by DHCP have their names automatically
|
||||
included in the DNS and the names can specified by each machine or
|
||||
centrally by associating a name with a MAC address in the dnsmasq
|
||||
config file.
|
||||
</LI>
|
||||
<LI>
|
||||
Dnsmasq caches internet addresses (A records and AAAA records) and address-to-name
|
||||
mappings (PTR records), reducing the load on upstream servers and
|
||||
improving performance (especially on modem connections). From version
|
||||
0.95 the cache honours time-to-live information and removes old
|
||||
records as they expire. From version 0.996 dnsmasq does negative
|
||||
caching. From version 1.2 dnsmasq supports IPv6 addresses, both
|
||||
in its cache and in /etc/hosts.
|
||||
improving performance (especially on modem connections).
|
||||
</LI>
|
||||
<LI>
|
||||
Dnsmasq can be configured to automatically pick up the addresses of
|
||||
@@ -76,14 +73,8 @@ upstream servers handling only those domains. This makes integration
|
||||
with private DNS systems easy.
|
||||
</LI>
|
||||
<LI>
|
||||
Dnsmasq can be configured to return an MX record
|
||||
for the firewall host. This makes it easy to configure the mailer on the local
|
||||
machines to forward all mail to the central mailer on the firewall host. Never
|
||||
lose root messages from your machines again!
|
||||
</LI>
|
||||
<LI>
|
||||
For version 1.15 dnsmasq has a facility to work around Verisign's infamous wildcard A record
|
||||
in the .com and .net TLDs
|
||||
Dnsmasq supports MX records and can be configured to return MX records
|
||||
for any or all local machines.
|
||||
</LI>
|
||||
</DIR>
|
||||
|
||||
@@ -115,12 +106,19 @@ bzip2 dnsmasq-zzz.tar
|
||||
Ulrich Ivens has a nice HOWTO in German on installing dnsmasq at <A
|
||||
HREF="http://howto.linux-hardware-shop.de/dnsmasq.html">http://howto.linux-hardware-shop.de/dnsmasq.html</A>
|
||||
and Damien Raude-Morvan has one in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
|
||||
There is a good article about dnsmasq at <A
|
||||
HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
|
||||
|
||||
<H2>License.</H2>
|
||||
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
|
||||
for details.
|
||||
|
||||
<H2>Contact.</H2>
|
||||
Dnsmasq was written by Simon Kelley. You can contact me at <A HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>. Bugreports, patches, and suggestions for improvements gratefully accepted.
|
||||
There is a dnsmasq mailing list at <A
|
||||
HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
|
||||
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
|
||||
first location for queries, bugreports, suggestions etc.
|
||||
Dnsmasq was written by Simon Kelley. You can contact me at <A
|
||||
HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
|
||||
</BODY>
|
||||
|
||||
|
||||
@@ -9,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 @@
|
||||
|
||||
94
src/cache.c
94
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
|
||||
@@ -17,7 +17,7 @@ static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
|
||||
static int cache_inserted, cache_live_freed, insert_error;
|
||||
static union bigname *big_free;
|
||||
static int bignames_left, log_queries, cache_size, hash_size;
|
||||
static int index;
|
||||
static int uid;
|
||||
|
||||
static void cache_free(struct crec *crecp);
|
||||
static void cache_unlink(struct crec *crecp);
|
||||
@@ -36,7 +36,7 @@ void cache_init(int size, int logq)
|
||||
cache_size = size;
|
||||
big_free = NULL;
|
||||
bignames_left = size/10;
|
||||
index = 0;
|
||||
uid = 0;
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
@@ -48,7 +48,7 @@ void cache_init(int size, int logq)
|
||||
{
|
||||
cache_link(crecp);
|
||||
crecp->flags = 0;
|
||||
crecp->uid = index++;
|
||||
crecp->uid = uid++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ static void cache_free(struct crec *crecp)
|
||||
{
|
||||
crecp->flags &= ~F_FORWARD;
|
||||
crecp->flags &= ~F_REVERSE;
|
||||
crecp->uid = index++; /* invalidate CNAMES pointing to this. */
|
||||
crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
|
||||
|
||||
if (cache_tail)
|
||||
cache_tail->next = crecp;
|
||||
@@ -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) &&
|
||||
@@ -602,6 +636,8 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
|
||||
struct crec *cache, **up, *tmp;
|
||||
int i;
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
|
||||
{
|
||||
@@ -673,7 +709,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
|
||||
if (!host_name)
|
||||
return;
|
||||
|
||||
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4)))
|
||||
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4 | F_CNAME)))
|
||||
{
|
||||
if (crec->flags & F_HOSTS)
|
||||
{
|
||||
@@ -681,7 +717,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
|
||||
{
|
||||
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
|
||||
syslog(LOG_WARNING,
|
||||
"not giving name %s to the DHCP lease of %s because"
|
||||
"not giving name %s to the DHCP lease of %s because "
|
||||
"the name exists in %s with address %s",
|
||||
host_name, inet_ntoa(*host_address),
|
||||
record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
|
||||
@@ -689,7 +725,7 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
|
||||
return;
|
||||
}
|
||||
else if (!(crec->flags & F_DHCP))
|
||||
cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
|
||||
cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
|
||||
}
|
||||
|
||||
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
|
||||
@@ -833,7 +869,15 @@ 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
|
||||
strcpy(addrbuff, "<CNAME>");
|
||||
}
|
||||
else
|
||||
#ifdef HAVE_IPV6
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
|
||||
18
src/config.h
18
src/config.h
@@ -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,7 +12,7 @@
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#define VERSION "2.16"
|
||||
#define VERSION "2.20"
|
||||
|
||||
#define FTABSIZ 150 /* max number of outstanding requests */
|
||||
#define MAX_PROCS 20 /* max no children for TCP requests */
|
||||
@@ -180,7 +180,7 @@ NOTES:
|
||||
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
|
||||
HAVE_DEV_RANDOM - FreeBSD and NetBSD
|
||||
(OpenBSD with hardware random number generator)
|
||||
HAVE_GETOPT_LONG - NetBSD
|
||||
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
|
||||
(FreeBSD and OpenBSD only if you link GNU getopt)
|
||||
|
||||
*/
|
||||
@@ -205,8 +205,10 @@ NOTES:
|
||||
#define HAVE_DEV_RANDOM
|
||||
#undef HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_PSELECT
|
||||
/* Don't fork into background on uClinux */
|
||||
#if defined(__uClinux__)
|
||||
/* Never use fork() on uClinux. Note that this is subtly different from the
|
||||
--keep-in-foreground option, since it also suppresses forking new
|
||||
processes for TCP connections. It's intended for use on MMU-less kernels. */
|
||||
# define NO_FORK
|
||||
#endif
|
||||
|
||||
@@ -254,7 +256,12 @@ typedef unsigned long in_addr_t;
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
#undef HAVE_GETOPT_LONG
|
||||
/* Later verions of FreeBSD have getopt_long() */
|
||||
#if defined(optional_argument) && defined(required_argument)
|
||||
# define HAVE_GETOPT_LONG
|
||||
#else
|
||||
# undef HAVE_GETOPT_LONG
|
||||
#endif
|
||||
#define HAVE_ARC4RANDOM
|
||||
#define HAVE_RANDOM
|
||||
#define HAVE_DEV_URANDOM
|
||||
@@ -271,7 +278,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 */
|
||||
|
||||
129
src/dhcp.c
129
src/dhcp.c
@@ -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;
|
||||
@@ -47,6 +54,8 @@ void dhcp_init(struct daemon *daemon)
|
||||
daemon->dhcpfd = fd;
|
||||
|
||||
if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1 ||
|
||||
(flags = fcntl(fd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) ||
|
||||
setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
|
||||
die("cannot create ICMP raw socket: %s.", NULL);
|
||||
@@ -73,8 +82,6 @@ void dhcp_init(struct daemon *daemon)
|
||||
socket receive buffer size to one to avoid that. (zero is
|
||||
rejected as non-sensical by some BSD kernels) */
|
||||
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
|
||||
(flags = fcntl(fd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
|
||||
die("cannot create DHCP packet socket: %s. "
|
||||
"Is CONFIG_PACKET enabled in your kernel?", NULL);
|
||||
@@ -358,8 +365,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
|
||||
iov[0].iov_len = sizeof(struct ether_header);
|
||||
iov[1].iov_base = (char *)rawpacket;
|
||||
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
|
||||
while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 &&
|
||||
errno == EINTR);
|
||||
while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 && retry_send());
|
||||
#else
|
||||
struct sockaddr_ll dest;
|
||||
|
||||
@@ -370,13 +376,13 @@ void dhcp_packet(struct daemon *daemon, time_t now)
|
||||
memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
|
||||
while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
|
||||
0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
|
||||
errno == EINTR);
|
||||
retry_send());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
{
|
||||
/* Check is an address is OK for this network, check all
|
||||
possible ranges. */
|
||||
@@ -391,12 +397,34 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
if (!context->static_only &&
|
||||
addr >= start &&
|
||||
addr <= end)
|
||||
return 1;
|
||||
return context;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr)
|
||||
{
|
||||
/* We start of with a set of possible contexts, all on the current subnet.
|
||||
These are chained on ->current.
|
||||
Here we have an address, and return the actual context correponding to that
|
||||
address. Note that none may fit, if the address came a dhcp-host and is outside
|
||||
any dhcp-range. In that case we return a static range is possible, or failing that,
|
||||
any context on the subnet. (If there's more than one, this is a dodgy configuration:
|
||||
maybe there should be a warning.) */
|
||||
|
||||
struct dhcp_context *tmp = address_available(context, taddr);
|
||||
|
||||
if (tmp)
|
||||
return tmp;
|
||||
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
if (tmp->static_only)
|
||||
return tmp;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
|
||||
{
|
||||
struct dhcp_config *config;
|
||||
@@ -408,17 +436,52 @@ 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 ;
|
||||
unsigned int i, j;
|
||||
|
||||
for (; context; context = context->current)
|
||||
if (!context->static_only)
|
||||
if (context->static_only)
|
||||
continue;
|
||||
else if (netids && !context->filter_netid)
|
||||
continue;
|
||||
else if (!netids && context->filter_netid)
|
||||
continue;
|
||||
else if (netids && context->filter_netid && !match_netid(&context->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++)
|
||||
@@ -450,6 +513,10 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
|
||||
|
||||
} while (addr.s_addr != start.s_addr);
|
||||
}
|
||||
|
||||
if (netids)
|
||||
return address_allocate(context, daemon, addrp, hwaddr, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -635,3 +702,41 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
}
|
||||
}
|
||||
|
||||
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
|
||||
for this address. If it has a domain part, that must match the set domain and
|
||||
it gets stripped. */
|
||||
char *host_from_dns(struct daemon *daemon, struct in_addr addr)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
|
||||
char *hostname = NULL;
|
||||
|
||||
if (lookup && (lookup->flags & F_HOSTS))
|
||||
{
|
||||
hostname = daemon->dhcp_buff;
|
||||
hostname[256] = 0;
|
||||
strncpy(hostname, cache_get_name(lookup), 256);
|
||||
hostname = strip_hostname(daemon, hostname);
|
||||
}
|
||||
|
||||
return hostname;
|
||||
}
|
||||
|
||||
char *strip_hostname(struct daemon *daemon, char *hostname)
|
||||
{
|
||||
char *dot = strchr(hostname, '.');
|
||||
if (dot)
|
||||
{
|
||||
if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
|
||||
{
|
||||
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
|
||||
hostname = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dot = 0; /* truncate */
|
||||
if (strlen(hostname) == 0)
|
||||
hostname = NULL; /* nothing left */
|
||||
}
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -30,6 +30,7 @@ int main (int argc, char **argv)
|
||||
struct irec *interfaces;
|
||||
struct sigaction sigact;
|
||||
sigset_t sigmask;
|
||||
struct iname *if_tmp;
|
||||
|
||||
sighup = 1; /* init cache the first time through */
|
||||
sigusr1 = 0; /* but don't dump */
|
||||
@@ -81,8 +82,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)))
|
||||
{
|
||||
@@ -92,7 +94,6 @@ int main (int argc, char **argv)
|
||||
|
||||
if (daemon->options & OPT_NOWILD)
|
||||
{
|
||||
struct iname *if_tmp;
|
||||
daemon->listeners = create_bound_listeners(interfaces, daemon->port);
|
||||
|
||||
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
@@ -263,6 +264,11 @@ int main (int argc, char **argv)
|
||||
if (bind_fallback)
|
||||
syslog(LOG_WARNING, "setting --bind-interfaces option because of OS limitations");
|
||||
|
||||
if (!(daemon->options & OPT_NOWILD))
|
||||
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
if (if_tmp->name && !if_tmp->used)
|
||||
syslog(LOG_WARNING, "warning: interface %s does not currently exist", if_tmp->name);
|
||||
|
||||
if (daemon->dhcp)
|
||||
{
|
||||
struct dhcp_context *dhcp_tmp;
|
||||
@@ -288,13 +294,12 @@ int main (int argc, char **argv)
|
||||
"DHCP, IP range %s -- %s, lease time %s",
|
||||
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
|
||||
}
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
if (daemon->dhcp)
|
||||
syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
|
||||
#endif
|
||||
|
||||
if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
|
||||
syslog(LOG_WARNING, "running as root");
|
||||
|
||||
@@ -505,51 +510,33 @@ 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())
|
||||
{
|
||||
num_kids++;
|
||||
close(confd);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
char *buff;
|
||||
@@ -578,7 +565,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);
|
||||
|
||||
@@ -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,8 +27,15 @@
|
||||
|
||||
/* 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. */
|
||||
#include <getopt.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
@@ -50,13 +57,11 @@
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
# include <getopt.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <stdarg.h>
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
# include <netinet/if_ether.h>
|
||||
#else
|
||||
@@ -96,6 +101,7 @@
|
||||
#define OPT_RESOLV_DOMAIN 32768
|
||||
#define OPT_NO_FORK 65536
|
||||
#define OPT_AUTHORITATIVE 131072
|
||||
#define OPT_LOCALISE 262144
|
||||
|
||||
struct all_addr {
|
||||
union {
|
||||
@@ -119,9 +125,16 @@ struct doctor {
|
||||
|
||||
struct mx_record {
|
||||
char *mxname, *mxtarget;
|
||||
int preference;
|
||||
struct mx_record *next;
|
||||
};
|
||||
|
||||
struct srv_record {
|
||||
char *srvname, *srvtarget;
|
||||
int srvport, priority, weight;
|
||||
struct srv_record *next;
|
||||
};
|
||||
|
||||
union bigname {
|
||||
char name[MAXDNAME];
|
||||
union bigname *next; /* freelist */
|
||||
@@ -211,11 +224,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;
|
||||
};
|
||||
|
||||
@@ -269,6 +284,10 @@ struct dhcp_netid {
|
||||
struct dhcp_netid *next;
|
||||
};
|
||||
|
||||
struct dhcp_netid_list {
|
||||
struct dhcp_netid *list;
|
||||
struct dhcp_netid_list *next;
|
||||
};
|
||||
struct dhcp_config {
|
||||
unsigned int flags;
|
||||
int clid_len; /* length of client identifier */
|
||||
@@ -293,12 +312,19 @@ struct dhcp_config {
|
||||
struct dhcp_opt {
|
||||
int opt, len, is_addr;
|
||||
unsigned char *val;
|
||||
char *netid;
|
||||
struct dhcp_netid *netid;
|
||||
struct dhcp_opt *next;
|
||||
};
|
||||
|
||||
struct dhcp_boot {
|
||||
char *file, *sname;
|
||||
struct in_addr next_server;
|
||||
struct dhcp_netid *netid;
|
||||
struct dhcp_boot *next;
|
||||
};
|
||||
|
||||
struct dhcp_vendor {
|
||||
int len, is_vendor, used;
|
||||
int len, is_vendor;
|
||||
char *data;
|
||||
struct dhcp_netid netid;
|
||||
struct dhcp_vendor *next;
|
||||
@@ -308,7 +334,7 @@ struct dhcp_context {
|
||||
unsigned int lease_time, addr_epoch;
|
||||
struct in_addr netmask, broadcast, router;
|
||||
struct in_addr start, end; /* range of available addresses */
|
||||
int static_only;
|
||||
int static_only, filter_netid;
|
||||
struct dhcp_netid netid;
|
||||
struct dhcp_context *next, *current;
|
||||
};
|
||||
@@ -349,6 +375,7 @@ struct daemon {
|
||||
char *lease_file;
|
||||
char *username, *groupname;
|
||||
char *domain_suffix;
|
||||
struct srv_record *srvnames;
|
||||
char *runfile;
|
||||
struct iname *if_names, *if_addrs, *if_except;
|
||||
struct bogus_addr *bogus_addr;
|
||||
@@ -361,9 +388,8 @@ struct daemon {
|
||||
struct dhcp_config *dhcp_conf;
|
||||
struct dhcp_opt *dhcp_opts;
|
||||
struct dhcp_vendor *dhcp_vendors;
|
||||
char *dhcp_file;
|
||||
char *dhcp_sname;
|
||||
struct in_addr dhcp_next_server;
|
||||
struct dhcp_boot *boot_config;
|
||||
struct dhcp_netid_list *dhcp_ignore;
|
||||
int dhcp_max;
|
||||
unsigned int min_leasetime;
|
||||
struct doctor *doctors;
|
||||
@@ -410,12 +436,13 @@ 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);
|
||||
int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
|
||||
unsigned int questions_crc(HEADER *header, unsigned int plen);
|
||||
int resize_packet(HEADER *header, unsigned int plen,
|
||||
unsigned char *pheader, unsigned int hlen);
|
||||
@@ -443,13 +470,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);
|
||||
|
||||
@@ -457,9 +486,12 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port);
|
||||
void dhcp_init(struct daemon *daemon);
|
||||
void dhcp_packet(struct daemon *daemon, time_t now);
|
||||
|
||||
int address_available(struct dhcp_context *context, struct in_addr addr);
|
||||
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
|
||||
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
|
||||
int 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,
|
||||
@@ -467,6 +499,8 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
void dhcp_update_configs(struct dhcp_config *configs);
|
||||
void dhcp_read_ethers(struct daemon *daemon);
|
||||
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
|
||||
char *strip_hostname(struct daemon *daemon, char *hostname);
|
||||
char *host_from_dns(struct daemon *daemon, struct in_addr addr);
|
||||
|
||||
/* lease.c */
|
||||
void lease_update_file(int force, time_t now);
|
||||
|
||||
190
src/forward.c
190
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
|
||||
@@ -65,45 +65,43 @@ static void send_from(int fd, int nowild, char *packet, int len,
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if (!nowild && to->sa.sa_family == AF_INET)
|
||||
if (!nowild)
|
||||
{
|
||||
struct cmsghdr *cmptr;
|
||||
msg.msg_control = &control_u;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
{
|
||||
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
|
||||
#if defined(IP_PKTINFO)
|
||||
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
|
||||
pkt->ipi_ifindex = 0;
|
||||
pkt->ipi_spec_dst = source->addr.addr4;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
||||
cmptr->cmsg_level = SOL_IP;
|
||||
cmptr->cmsg_type = IP_PKTINFO;
|
||||
#elif defined(IP_SENDSRCADDR)
|
||||
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
|
||||
*a = source->addr.addr4;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
|
||||
cmptr->cmsg_level = IPPROTO_IP;
|
||||
cmptr->cmsg_type = IP_SENDSRCADDR;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
cmptr = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (to->sa.sa_family == AF_INET6)
|
||||
{
|
||||
msg.msg_control = &control_u;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
{
|
||||
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
|
||||
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
|
||||
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
|
||||
pkt->ipi6_addr = source->addr.addr6;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
cmptr->cmsg_type = IPV6_PKTINFO;
|
||||
cmptr->cmsg_level = IPV6_LEVEL;
|
||||
}
|
||||
}
|
||||
if (to->sa.sa_family == AF_INET)
|
||||
{
|
||||
#if defined(IP_PKTINFO)
|
||||
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
|
||||
pkt->ipi_ifindex = 0;
|
||||
pkt->ipi_spec_dst = source->addr.addr4;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
|
||||
cmptr->cmsg_level = SOL_IP;
|
||||
cmptr->cmsg_type = IP_PKTINFO;
|
||||
#elif defined(IP_SENDSRCADDR)
|
||||
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
|
||||
*a = source->addr.addr4;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
|
||||
cmptr->cmsg_level = IPPROTO_IP;
|
||||
cmptr->cmsg_type = IP_SENDSRCADDR;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
|
||||
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
|
||||
pkt->ipi6_addr = source->addr.addr6;
|
||||
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
|
||||
cmptr->cmsg_type = IPV6_PKTINFO;
|
||||
cmptr->cmsg_level = IPV6_LEVEL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
retry:
|
||||
if (sendmsg(fd, &msg, 0) == -1)
|
||||
@@ -199,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)
|
||||
@@ -392,7 +390,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,
|
||||
@@ -462,7 +460,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
|
||||
{
|
||||
header->id = htons(forward->orig_id);
|
||||
header->ra = 1; /* recursion if available */
|
||||
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
|
||||
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
|
||||
&forward->source, &forward->dest, forward->iface);
|
||||
forward->new_id = 0; /* cancel */
|
||||
}
|
||||
@@ -476,7 +474,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
|
||||
unsigned short type;
|
||||
struct iname *tmp;
|
||||
struct all_addr dst_addr;
|
||||
int check_dst = !(daemon->options & OPT_NOWILD);
|
||||
struct in_addr netmask, dst_addr_4;
|
||||
int m, n, if_index = 0;
|
||||
struct iovec iov[1];
|
||||
struct msghdr msg;
|
||||
@@ -494,6 +492,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;
|
||||
|
||||
@@ -508,61 +514,59 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
|
||||
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
|
||||
return;
|
||||
|
||||
source_addr.sa.sa_family = listen->family;
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
{
|
||||
check_dst = 1;
|
||||
source_addr.in6.sin6_flowinfo = htonl(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
|
||||
return;
|
||||
|
||||
#if defined(IP_PKTINFO)
|
||||
if (check_dst && listen->family == AF_INET)
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
|
||||
{
|
||||
dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
|
||||
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
|
||||
}
|
||||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
|
||||
if (check_dst && listen->family == AF_INET)
|
||||
{
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
|
||||
dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
|
||||
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
|
||||
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
{
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
|
||||
{
|
||||
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
|
||||
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (n < (int)sizeof(HEADER) || header->qr)
|
||||
return;
|
||||
|
||||
/* enforce available interface configuration */
|
||||
if (check_dst)
|
||||
source_addr.sa.sa_family = listen->family;
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
source_addr.in6.sin6_flowinfo = htonl(0);
|
||||
#endif
|
||||
|
||||
if (!(daemon->options & OPT_NOWILD))
|
||||
{
|
||||
struct ifreq ifr;
|
||||
|
||||
if (msg.msg_controllen < sizeof(struct cmsghdr))
|
||||
return;
|
||||
|
||||
#if defined(IP_PKTINFO)
|
||||
if (listen->family == AF_INET)
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
|
||||
{
|
||||
dst_addr_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)
|
||||
if (listen->family == AF_INET)
|
||||
{
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
|
||||
dst_addr_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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
{
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
|
||||
{
|
||||
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
|
||||
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* enforce available interface configuration */
|
||||
|
||||
if (if_index == 0)
|
||||
return;
|
||||
|
||||
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;
|
||||
@@ -572,6 +576,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)
|
||||
@@ -615,7 +626,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
|
||||
@@ -652,7 +664,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;
|
||||
@@ -694,7 +707,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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
303
src/network.c
303
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,21 +14,38 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *name, 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
|
||||
sure that loopback interfaces are in that set. */
|
||||
if (daemon->if_names && is_loopback)
|
||||
{
|
||||
struct iname *lo;
|
||||
for (lo = daemon->if_names; lo; lo = lo->next)
|
||||
if (lo->name && strcmp(lo->name, name) == 0)
|
||||
{
|
||||
lo->isloop = 1;
|
||||
break;
|
||||
}
|
||||
if (!lo)
|
||||
{
|
||||
lo = safe_malloc(sizeof(struct iname));
|
||||
lo->name = safe_string_alloc(name);
|
||||
lo->isloop = lo->used = 1;
|
||||
lo->next = daemon->if_names;
|
||||
daemon->if_names = lo;
|
||||
}
|
||||
}
|
||||
|
||||
/* check blacklist */
|
||||
if (daemon->if_except)
|
||||
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
|
||||
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)
|
||||
{
|
||||
@@ -36,37 +53,44 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list, char *na
|
||||
|
||||
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;
|
||||
#endif
|
||||
union mysockaddr addr;
|
||||
struct irec *iface = NULL;
|
||||
char *buf, *ptr;
|
||||
struct ifreq *ifr = NULL;
|
||||
@@ -74,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)
|
||||
{
|
||||
@@ -87,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
|
||||
{
|
||||
@@ -99,16 +130,15 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
|
||||
free(buf);
|
||||
}
|
||||
|
||||
for (ptr = buf; ptr < buf + len; )
|
||||
for (ptr = buf; ptr < buf + ifc.ifc_len; )
|
||||
{
|
||||
union mysockaddr addr;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
/* subsequent entries may not be aligned, so copy into
|
||||
an aligned buffer to avoid nasty complaints about
|
||||
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;
|
||||
@@ -122,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)
|
||||
@@ -139,78 +172,85 @@ 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;
|
||||
|
||||
/* If we are restricting the set of interfaces to use, make
|
||||
sure that loopback interfaces are in that set. */
|
||||
if (daemon->if_names && (ifr->ifr_flags & IFF_LOOPBACK))
|
||||
if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
|
||||
{
|
||||
struct iname *lo;
|
||||
for (lo = daemon->if_names; lo; lo = lo->next)
|
||||
if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0)
|
||||
{
|
||||
lo->isloop = 1;
|
||||
break;
|
||||
}
|
||||
if (!lo)
|
||||
if (chainp)
|
||||
{
|
||||
lo = safe_malloc(sizeof(struct iname));
|
||||
lo->name = safe_string_alloc(ifr->ifr_name);
|
||||
lo->isloop = lo->used = 1;
|
||||
lo->next = daemon->if_names;
|
||||
daemon->if_names = lo;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
iface = add_iface(daemon, iface, ifr->ifr_name, &addr);
|
||||
|
||||
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
|
||||
{
|
||||
FILE *f = fopen(IP6INTERFACES, "r");
|
||||
int found = 0;
|
||||
union mysockaddr addr6;
|
||||
|
||||
if (f)
|
||||
{
|
||||
unsigned int plen, scope, flags, if_idx;
|
||||
char devname[20], addrstring[32];
|
||||
|
||||
while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n",
|
||||
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
|
||||
{
|
||||
if (strcmp(devname, ifr->ifr_name) == 0)
|
||||
{
|
||||
int i;
|
||||
unsigned char *addr6p = (unsigned char *) &addr6.in6.sin6_addr;
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
addr6.sa.sa_family = AF_INET6;
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned int byte;
|
||||
sscanf(addrstring+i+i, "%02x", &byte);
|
||||
addr6p[i] = byte;
|
||||
}
|
||||
addr6.in6.sin6_port = htons(daemon->port);
|
||||
addr6.in6.sin6_flowinfo = htonl(0);
|
||||
addr6.in6.sin6_scope_id = htonl(scope);
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (found)
|
||||
iface = add_iface(daemon, iface, ifr->ifr_name, &addr6);
|
||||
}
|
||||
#endif /* LINUX */
|
||||
}
|
||||
|
||||
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
|
||||
if ((f = fopen(IP6INTERFACES, "r")))
|
||||
{
|
||||
unsigned int plen, scope, flags, if_idx;
|
||||
char devname[20], addrstring[32];
|
||||
|
||||
while (fscanf(f, "%32s %x %x %x %x %20s\n",
|
||||
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
|
||||
{
|
||||
int i;
|
||||
struct ifreq sifr;
|
||||
unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sa.sa_family = AF_INET6;
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned int byte;
|
||||
sscanf(addrstring+i+i, "%02x", &byte);
|
||||
addr6p[i] = byte;
|
||||
}
|
||||
addr.in6.sin6_port = htons(daemon->port);
|
||||
addr.in6.sin6_flowinfo = htonl(0);
|
||||
addr.in6.sin6_scope_id = htonl(scope);
|
||||
|
||||
strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
|
||||
if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
|
||||
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
|
||||
@@ -219,7 +259,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
|
||||
#endif
|
||||
close(fd);
|
||||
|
||||
return iface;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -356,34 +396,53 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
|
||||
struct irec *iface;
|
||||
int flags = port, opt = 1;
|
||||
|
||||
/* Create bound listeners only for IPv4, IPv6 always binds the wildcard */
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (!create_ipv6_listener(&listeners, port))
|
||||
die("failed to to create listening socket: %s", NULL);
|
||||
#endif
|
||||
|
||||
for (iface = interfaces ;iface; iface = iface->next)
|
||||
if (iface->addr.sa.sa_family == AF_INET)
|
||||
{
|
||||
struct listener *new = safe_malloc(sizeof(struct listener));
|
||||
new->family = iface->addr.sa.sa_family;
|
||||
new->next = listeners;
|
||||
listeners = new;
|
||||
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
|
||||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
|
||||
setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
/* See Stevens 16.6 */
|
||||
(flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
(flags = fcntl(new->fd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(new->fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
|
||||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
|
||||
listen(new->tcpfd, 5) == -1)
|
||||
die("failed to to create listening socket: %s", NULL);
|
||||
}
|
||||
{
|
||||
struct listener *new = safe_malloc(sizeof(struct listener));
|
||||
new->family = iface->addr.sa.sa_family;
|
||||
new->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 ||
|
||||
setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
/* See Stevens 16.6 */
|
||||
(flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
(flags = fcntl(new->fd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(new->fd, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
die("failed to create listening socket: %s", NULL);
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (iface->addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
|
||||
die("failed to set IPV6 options on listening socket: %s", NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
|
||||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
if (iface->addr.sa.sa_family == AF_INET6 && errno == ENODEV)
|
||||
{
|
||||
close(new->tcpfd);
|
||||
close(new->fd);
|
||||
free(new);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
die("failed to bind listening socket: %s", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
listeners = new;
|
||||
if (listen(new->tcpfd, 5) == -1)
|
||||
die("failed to listen on socket: %s", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
379
src/option.c
379
src/option.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
|
||||
@@ -21,7 +21,7 @@ struct myoption {
|
||||
int val;
|
||||
};
|
||||
|
||||
#define OPTSTRING "ZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:"
|
||||
#define OPTSTRING "yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:"
|
||||
|
||||
static struct myoption opts[] = {
|
||||
{"version", 0, 0, 'v'},
|
||||
@@ -72,9 +72,12 @@ static struct myoption opts[] = {
|
||||
{"alias", 1, 0, 'V' },
|
||||
{"dhcp-vendorclass", 1, 0, 'U'},
|
||||
{"dhcp-userclass", 1, 0, 'j'},
|
||||
{"dhcp-ignore", 1, 0, 'J'},
|
||||
{"edns-packet-max", 1, 0, 'P'},
|
||||
{"keep-in-foreground", 0, 0, 'k'},
|
||||
{"dhcp-authoritative", 0, 0, 'K'},
|
||||
{"srv-host", 1, 0, 'W'},
|
||||
{"localise-queries", 0, 0, 'y'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -101,14 +104,18 @@ static struct optflags optmap[] = {
|
||||
{ 'D', OPT_NODOTS_LOCAL },
|
||||
{ 'z', OPT_NOWILD },
|
||||
{ 'Z', OPT_ETHERS },
|
||||
{ 'y', OPT_LOCALISE },
|
||||
{ 'v', 0},
|
||||
{ 'w', 0},
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static char *usage =
|
||||
"Usage: dnsmasq [options]\n"
|
||||
"\nValid options are :\n"
|
||||
"Usage: dnsmasq [options]\n\n"
|
||||
#ifndef HAVE_GETOPT_LONG
|
||||
"Use short options only on the command line.\n"
|
||||
#endif
|
||||
"Valid options are :\n"
|
||||
"-a, --listen-address=ipaddr Specify local address(es) to listen on.\n"
|
||||
"-A, --address=/domain/ipaddr Return ipaddr for all hosts in specified domains.\n"
|
||||
"-b, --bogus-priv Fake reverse lookups for RFC1918 private address ranges.\n"
|
||||
@@ -128,11 +135,12 @@ static char *usage =
|
||||
"-i, --interface=interface Specify interface(s) to listen on.\n"
|
||||
"-I, --except-interface=int Specify interface(s) NOT to listen on.\n"
|
||||
"-j, --dhcp-userclass=<id>,<class> Map DHCP user class to option set.\n"
|
||||
"-J, --dhcp-ignore=<id> Don't do DHCP for hosts in option set.\n"
|
||||
"-k, --keep-in-foreground Do NOT fork into the background, do NOT run in debug mode.\n"
|
||||
"-K, --dhcp-authoritative Assume we are the only DHCP server on the local network.\n"
|
||||
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
|
||||
"-L, --localmx Return MX records for local hosts.\n"
|
||||
"-m, --mx-host=host_name Specify the MX name to reply to.\n"
|
||||
"-m, --mx-host=host_name,target,pref Specify an MX record.\n"
|
||||
"-M, --dhcp-boot=<bootp opts> Specify BOOTP options to DHCP server.\n"
|
||||
"-n, --no-poll Do NOT poll " RESOLVFILE " file, reload only on SIGHUP.\n"
|
||||
"-N, --no-negcache Do NOT cache failed search results.\n"
|
||||
@@ -147,15 +155,17 @@ static char *usage =
|
||||
"-S, --server=/domain/ipaddr Specify address(es) of upstream servers with optional domains.\n"
|
||||
" --local=/domain/ Never forward queries to specified domains.\n"
|
||||
"-s, --domain=domain Specify the domain to be assigned in DHCP leases.\n"
|
||||
"-t, --mx-target=host_name Specify the host in an MX reply.\n"
|
||||
"-t, --mx-target=host_name Specify default target in an MX record.\n"
|
||||
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
|
||||
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
|
||||
"-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n"
|
||||
"-v, --version Display dnsmasq version and copyright information.\n"
|
||||
"-V, --alias=addr,addr,mask Translate IPv4 addresses from upstream servers.\n"
|
||||
"-W, --srv-host=name,port,pri,weight Specify a SRV record.\n"
|
||||
"-w, --help Display this message.\n"
|
||||
"-x, --pid-file=path Specify path of PID file. (defaults to " RUNFILE ").\n"
|
||||
"-X, --dhcp-lease-max=number Specify maximum number of DHCP leases (defaults to %d).\n"
|
||||
"-y, --localise-queries Answer DNS queries based on the interface a query was sent to."
|
||||
"-z, --bind-interfaces Bind only to interfaces in use.\n"
|
||||
"-Z, --read-ethers Read DHCP static host information from " ETHERSFILE ".\n"
|
||||
"\n";
|
||||
@@ -167,7 +177,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
char *problem = NULL, *buff = safe_malloc(MAXDNAME);
|
||||
int option = 0, i;
|
||||
FILE *file_save = NULL, *f = NULL;
|
||||
char *file_name_save = NULL, *conffile = CONFFILE;
|
||||
char *comma, *file_name_save = NULL, *conffile = CONFFILE;
|
||||
int hosts_index = 1, conffile_set = 0;
|
||||
int line_save = 0, lineno = 0;
|
||||
opterr = 0;
|
||||
@@ -367,25 +377,41 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
|
||||
case 'm':
|
||||
{
|
||||
char *comma = strchr(optarg, ',');
|
||||
if (comma)
|
||||
*(comma++) = 0;
|
||||
int pref = 1;
|
||||
struct mx_record *new;
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
{
|
||||
char *prefstr;
|
||||
*(comma++) = 0;
|
||||
if ((prefstr=strchr(comma, ',')))
|
||||
{
|
||||
*(prefstr++) = 0;
|
||||
if (!atoi_check(prefstr, &pref))
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad MX preference";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad MX name";
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct mx_record *new = safe_malloc(sizeof(struct mx_record));
|
||||
new->next = daemon->mxnames;
|
||||
daemon->mxnames = new;
|
||||
new->mxname = safe_string_alloc(optarg);
|
||||
new->mxtarget = safe_string_alloc(comma); /* may be NULL */
|
||||
}
|
||||
|
||||
new = safe_malloc(sizeof(struct mx_record));
|
||||
new->next = daemon->mxnames;
|
||||
daemon->mxnames = new;
|
||||
new->mxname = safe_string_alloc(optarg);
|
||||
new->mxtarget = safe_string_alloc(comma); /* may be NULL */
|
||||
new->preference = pref;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 't':
|
||||
if (!canonicalise(optarg))
|
||||
{
|
||||
@@ -428,8 +454,10 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
{
|
||||
do {
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
new->next = daemon->if_names;
|
||||
daemon->if_names = new;
|
||||
/* new->name may be NULL if someone does
|
||||
@@ -438,20 +466,24 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
new->isloop = new->used = 0;
|
||||
if (strchr(optarg, ':'))
|
||||
daemon->options |= OPT_NOWILD;
|
||||
break;
|
||||
}
|
||||
|
||||
optarg = comma;
|
||||
} while (optarg);
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
{
|
||||
do {
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
new->next = daemon->if_except;
|
||||
daemon->if_except = new;
|
||||
new->name = safe_string_alloc(optarg);
|
||||
if (strchr(optarg, ':'))
|
||||
daemon->options |= OPT_NOWILD;
|
||||
break;
|
||||
}
|
||||
|
||||
daemon->options |= OPT_NOWILD;
|
||||
optarg = comma;
|
||||
} while (optarg);
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
{
|
||||
struct in_addr addr;
|
||||
@@ -468,8 +500,10 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
}
|
||||
|
||||
case 'a':
|
||||
{
|
||||
do {
|
||||
struct iname *new = safe_malloc(sizeof(struct iname));
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
new->next = daemon->if_addrs;
|
||||
#ifdef HAVE_IPV6
|
||||
if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr))
|
||||
@@ -500,14 +534,14 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
{
|
||||
option = '?'; /* error */
|
||||
free(new);
|
||||
new = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new)
|
||||
daemon->if_addrs = new;
|
||||
break;
|
||||
}
|
||||
|
||||
daemon->if_addrs = new;
|
||||
optarg = comma;
|
||||
} while (optarg);
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
case 'A':
|
||||
{
|
||||
@@ -725,7 +759,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
case 'F':
|
||||
{
|
||||
int k, leasepos = 2;
|
||||
char *cp, *comma, *a[5] = { NULL, NULL, NULL, NULL, NULL };
|
||||
char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
|
||||
struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
|
||||
|
||||
new->next = daemon->dhcp;
|
||||
@@ -735,7 +769,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
new->broadcast.s_addr = 0;
|
||||
new->router.s_addr = 0;
|
||||
new->netid.net = NULL;
|
||||
new->static_only = 0;
|
||||
new->static_only = new->filter_netid = 0;
|
||||
|
||||
problem = "bad dhcp-range";
|
||||
|
||||
@@ -746,7 +780,14 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
if (*cp != ',' && (comma = strchr(optarg, ',')))
|
||||
{
|
||||
*comma = 0;
|
||||
new->netid.net = safe_string_alloc(optarg);
|
||||
if (strstr(optarg, "net:") == optarg)
|
||||
{
|
||||
new->netid.net = safe_string_alloc(optarg+4);
|
||||
new->netid.next = NULL;
|
||||
new->filter_netid = 1;
|
||||
}
|
||||
else
|
||||
new->netid.net = safe_string_alloc(optarg);
|
||||
a[0] = comma + 1;
|
||||
}
|
||||
else
|
||||
@@ -902,10 +943,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
memcpy(new->clid, arg, len);
|
||||
}
|
||||
}
|
||||
else if ((arg[0] == 'n' || arg[0] == 'N') &&
|
||||
(arg[1] == 'e' || arg[1] == 'E') &&
|
||||
(arg[2] == 't' || arg[3] == 'T') &&
|
||||
arg[3] == ':')
|
||||
else if (strstr(arg, "net:") == arg)
|
||||
{
|
||||
new->flags |= CONFIG_NETID;
|
||||
new->netid.net = safe_string_alloc(arg+4);
|
||||
@@ -1005,7 +1043,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
case 'O':
|
||||
{
|
||||
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
|
||||
char *cp, *comma;
|
||||
char *cp;
|
||||
int addrs, digs, is_addr, is_hex, is_dec;
|
||||
|
||||
new->next = daemon->dhcp_opts;
|
||||
@@ -1016,25 +1054,30 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
{
|
||||
struct dhcp_netid *np = NULL;
|
||||
*comma++ = 0;
|
||||
|
||||
for (cp = optarg; *cp; cp++)
|
||||
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
|
||||
do {
|
||||
for (cp = optarg; *cp; cp++)
|
||||
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
|
||||
break;
|
||||
if (!*cp)
|
||||
break;
|
||||
|
||||
if (*cp)
|
||||
{
|
||||
new->netid = safe_string_alloc(optarg);
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
}
|
||||
|
||||
new->netid = safe_malloc(sizeof (struct dhcp_netid));
|
||||
new->netid->net = safe_string_alloc(optarg);
|
||||
new->netid->next = np;
|
||||
np = new->netid;
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
} while (optarg);
|
||||
}
|
||||
|
||||
if ((new->opt = atoi(optarg)) == 0)
|
||||
if (!optarg || (new->opt = atoi(optarg)) == 0)
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad dhcp-opt";
|
||||
problem = "bad dhcp-option";
|
||||
}
|
||||
else if (comma && new->opt == 119)
|
||||
{
|
||||
@@ -1052,7 +1095,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
if (!canonicalise(optarg))
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad dhcp-search-opt";
|
||||
problem = "bad domain in dhcp-option";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1115,7 +1158,7 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
}
|
||||
else if (*cp == '.')
|
||||
is_dec = is_hex = 0;
|
||||
else if (!(*cp >='0' && *cp <= '9'))
|
||||
else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
|
||||
{
|
||||
is_dec = is_addr = 0;
|
||||
if (!((*cp >='A' && *cp <= 'F') ||
|
||||
@@ -1150,31 +1193,24 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
}
|
||||
else if (is_dec)
|
||||
{
|
||||
/* Given that we don't know the length,
|
||||
this appaling hack is the best available */
|
||||
unsigned int val = atoi(comma);
|
||||
if (val < 256)
|
||||
int i, val = atoi(comma);
|
||||
/* assume numeric arg is 1 byte except for
|
||||
options where it is known otherwise. */
|
||||
switch (new->opt)
|
||||
{
|
||||
default:
|
||||
new->len = 1;
|
||||
new->val = safe_malloc(1);
|
||||
*(new->val) = val;
|
||||
}
|
||||
else if (val < 65536)
|
||||
{
|
||||
break;
|
||||
case 13: case 22: case 25: case 26:
|
||||
new->len = 2;
|
||||
new->val = safe_malloc(2);
|
||||
*(new->val) = val>>8;
|
||||
*(new->val+1) = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
case 2: case 24: case 35: case 38:
|
||||
new->len = 4;
|
||||
new->val = safe_malloc(4);
|
||||
*(new->val) = val>>24;
|
||||
*(new->val+1) = val>>16;
|
||||
*(new->val+2) = val>>8;
|
||||
*(new->val+3) = val;
|
||||
break;
|
||||
}
|
||||
new->val = safe_malloc(new->len);
|
||||
for (i=0; i<new->len; i++)
|
||||
new->val[i] = val>>((new->len - i - 1)*8);
|
||||
}
|
||||
else if (is_addr)
|
||||
{
|
||||
@@ -1224,19 +1260,57 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
|
||||
case 'M':
|
||||
{
|
||||
char *comma;
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma = 0;
|
||||
daemon->dhcp_file = safe_string_alloc(optarg);
|
||||
if (comma)
|
||||
struct dhcp_netid *id = NULL;
|
||||
while (optarg && strstr(optarg, "net:") == optarg)
|
||||
{
|
||||
optarg = comma+1;
|
||||
struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
|
||||
newid->next = id;
|
||||
id = newid;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma = 0;
|
||||
daemon->dhcp_sname = safe_string_alloc(optarg);
|
||||
if (comma && (daemon->dhcp_next_server.s_addr = inet_addr(comma+1)) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
*comma++ = 0;
|
||||
newid->net = safe_string_alloc(optarg+4);
|
||||
optarg = comma;
|
||||
};
|
||||
|
||||
if (!optarg)
|
||||
option = '?';
|
||||
else
|
||||
{
|
||||
char *dhcp_file, *dhcp_sname = NULL;
|
||||
struct in_addr dhcp_next_server;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
dhcp_file = safe_string_alloc(optarg);
|
||||
dhcp_next_server.s_addr = 0;
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
dhcp_sname = safe_string_alloc(optarg);
|
||||
if (comma && (dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
}
|
||||
if (option != '?')
|
||||
{
|
||||
struct dhcp_boot *new = safe_malloc(sizeof(struct dhcp_boot));
|
||||
new->file = dhcp_file;
|
||||
new->sname = dhcp_sname;
|
||||
new->next_server = dhcp_next_server;
|
||||
new->netid = id;
|
||||
new->next = daemon->boot_config;
|
||||
daemon->boot_config = new;
|
||||
}
|
||||
}
|
||||
|
||||
if (option == '?')
|
||||
{
|
||||
struct dhcp_netid *tmp;
|
||||
for (; id; id = tmp)
|
||||
{
|
||||
tmp = id->next;
|
||||
free(id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1244,8 +1318,6 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
case 'U':
|
||||
case 'j':
|
||||
{
|
||||
char *comma;
|
||||
|
||||
if (!(comma = strchr(optarg, ',')))
|
||||
option = '?';
|
||||
else
|
||||
@@ -1262,7 +1334,27 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case 'J':
|
||||
{
|
||||
struct dhcp_netid_list *new = safe_malloc(sizeof(struct dhcp_netid_list));
|
||||
struct dhcp_netid *list = NULL;
|
||||
new->next = daemon->dhcp_ignore;
|
||||
daemon->dhcp_ignore = new;
|
||||
do {
|
||||
struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*comma++ = 0;
|
||||
member->next = list;
|
||||
list = member;
|
||||
member->net = safe_string_alloc(optarg);
|
||||
optarg = comma;
|
||||
} while (optarg);
|
||||
|
||||
new->list = list;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'V':
|
||||
{
|
||||
char *a[3] = { NULL, NULL, NULL };
|
||||
@@ -1300,6 +1392,84 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'W':
|
||||
{
|
||||
int port = 1, priority = 0, weight = 0;
|
||||
char *name, *target = NULL;
|
||||
struct srv_record *new;
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*(comma++) = 0;
|
||||
|
||||
if (!canonicalise(optarg))
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad SRV record";
|
||||
break;
|
||||
}
|
||||
name = safe_string_alloc(optarg);
|
||||
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*(comma++) = 0;
|
||||
if (!canonicalise(optarg))
|
||||
{
|
||||
option = '?';
|
||||
problem = "bad SRV target";
|
||||
break;
|
||||
}
|
||||
target = safe_string_alloc(optarg);
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*(comma++) = 0;
|
||||
if (!atoi_check(optarg, &port))
|
||||
{
|
||||
option = '?';
|
||||
problem = "invalid port number";
|
||||
break;
|
||||
}
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*(comma++) = 0;
|
||||
if (!atoi_check(optarg, &priority))
|
||||
{
|
||||
option = '?';
|
||||
problem = "invalid priority";
|
||||
break;
|
||||
}
|
||||
if (comma)
|
||||
{
|
||||
optarg = comma;
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
*(comma++) = 0;
|
||||
if (!atoi_check(optarg, &weight))
|
||||
{
|
||||
option = '?';
|
||||
problem = "invalid weight";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new = safe_malloc(sizeof(struct srv_record));
|
||||
new->next = daemon->srvnames;
|
||||
daemon->srvnames = new;
|
||||
new->srvname = name;
|
||||
new->srvtarget = target;
|
||||
new->srvport = port;
|
||||
new->priority = priority;
|
||||
new->weight = weight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1312,7 +1482,11 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
complain(buff, NULL);
|
||||
}
|
||||
else
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
die("bad command line options: %s.", problem ? problem : "try --help");
|
||||
#else
|
||||
die("bad command line options: %s.", problem ? problem : "try -w");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1344,24 +1518,39 @@ struct daemon *read_opts (int argc, char **argv)
|
||||
#endif /* IPv6 */
|
||||
}
|
||||
|
||||
/* only one of these need be specified: the other defaults to the
|
||||
host-name */
|
||||
/* only one of these need be specified: the other defaults to the host-name */
|
||||
if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
|
||||
{
|
||||
if (gethostname(buff, MAXDNAME) == -1)
|
||||
die("cannot get host-name: %s", NULL);
|
||||
|
||||
|
||||
if (!daemon->mxnames)
|
||||
{
|
||||
daemon->mxnames = safe_malloc(sizeof(struct mx_record));
|
||||
daemon->mxnames->next = NULL;
|
||||
daemon->mxnames->mxtarget = NULL;
|
||||
daemon->mxnames->mxname = safe_string_alloc(buff);
|
||||
}
|
||||
}
|
||||
|
||||
if (!daemon->mxtarget)
|
||||
daemon->mxtarget = safe_string_alloc(buff);
|
||||
}
|
||||
|
||||
if (daemon->domain_suffix)
|
||||
{
|
||||
/* add domain for any srv record without one. */
|
||||
struct srv_record *srv;
|
||||
|
||||
for (srv = daemon->srvnames; srv; srv = srv->next)
|
||||
if (strchr(srv->srvname, '.') && strchr(srv->srvname, '.') == strrchr(srv->srvname, '.'))
|
||||
{
|
||||
strcpy(buff, srv->srvname);
|
||||
strcat(buff, ".");
|
||||
strcat(buff, daemon->domain_suffix);
|
||||
free(srv->srvname);
|
||||
srv->srvname = safe_string_alloc(buff);
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->options & OPT_NO_RESOLV)
|
||||
daemon->resolv_files = 0;
|
||||
|
||||
541
src/rfc1035.c
541
src/rfc1035.c
@@ -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
|
||||
@@ -420,43 +429,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)
|
||||
@@ -653,7 +625,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
|
||||
if (!cname_count--)
|
||||
return; /* looped CNAMES */
|
||||
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
|
||||
if (cpp)
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
@@ -673,7 +645,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
|
||||
if (aqtype == T_A)
|
||||
dns_doctor(header, daemon->doctors, (struct in_addr *)p1);
|
||||
newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD);
|
||||
if (cpp)
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
@@ -700,7 +672,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
|
||||
if (ttl || cpp)
|
||||
{
|
||||
newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);
|
||||
if (cpp)
|
||||
if (newc && cpp)
|
||||
{
|
||||
cpp->addr.cname.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
@@ -773,13 +745,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 +753,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,18 +763,24 @@ 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_record *mx;
|
||||
struct srv_record *srv;
|
||||
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4|F_IPV6)) &&
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
|
||||
(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
return 1;
|
||||
|
||||
for (; mx; mx = mx->next)
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (hostname_isequal(name, mx->mxname))
|
||||
return 1;
|
||||
|
||||
|
||||
for (srv = daemon->srvnames; srv; srv = srv->next)
|
||||
if (hostname_isequal(name, srv->srvname))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -862,8 +828,103 @@ 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 */
|
||||
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':
|
||||
sval = va_arg(ap, char *);
|
||||
j = strlen(sval);
|
||||
*p++ = j;
|
||||
memcpy(p, sval, j);
|
||||
p += j;
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap); /* clean up variable argument pointer */
|
||||
|
||||
j = p - sav - 2;
|
||||
PUTSHORT(j, sav); /* Now, store real RDLength */
|
||||
|
||||
if (offset)
|
||||
*offset = sav - (unsigned char *)header;
|
||||
|
||||
/* 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,10 +933,10 @@ 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;
|
||||
int dryrun = 0, sec_reqd = 0;
|
||||
struct crec *crecp;
|
||||
int nxdomain, auth;
|
||||
int nxdomain = 0, auth = 1, trunc = 0;
|
||||
|
||||
if (!qdcount || header->opcode != QUERY )
|
||||
return 0;
|
||||
@@ -914,7 +975,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++)
|
||||
{
|
||||
@@ -940,7 +1000,6 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
int len;
|
||||
if (hostname_isequal(name, "version.bind"))
|
||||
sprintf(name, "dnsmasq-%s", VERSION);
|
||||
else if (hostname_isequal(name, "authors.bind"))
|
||||
@@ -949,150 +1008,145 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
|
||||
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++;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL,
|
||||
T_TXT, C_CHAOS, "t", name))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (qclass == C_IN)
|
||||
{
|
||||
if ((daemon->options & OPT_FILTER) &&
|
||||
(qtype == T_SOA || qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
|
||||
if (qtype == T_PTR || qtype == T_ANY)
|
||||
{
|
||||
ans = 1;
|
||||
log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
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)));
|
||||
}
|
||||
|
||||
for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
|
||||
{
|
||||
unsigned short type = T_A;
|
||||
int addrsz = INADDRSZ;
|
||||
|
||||
if (flag == F_IPV6)
|
||||
if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
|
||||
{
|
||||
if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
|
||||
{
|
||||
#ifdef HAVE_IPV6
|
||||
type = T_AAAA;
|
||||
addrsz = IN6ADDRSZ;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
/* 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);
|
||||
}
|
||||
|
||||
if (qtype != type && qtype != T_ANY && qtype != T_CNAME)
|
||||
}
|
||||
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);
|
||||
|
||||
cname_restart:
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)))
|
||||
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 */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
break;
|
||||
|
||||
if (crecp->flags & F_CNAME)
|
||||
{
|
||||
if (qtype == T_CNAME)
|
||||
ans = 1;
|
||||
|
||||
if (!dryrun)
|
||||
{
|
||||
ansp = add_text_record(header, nameoffset, ansp, crecp->ttd - now, 0, T_CNAME,
|
||||
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 (qtype == T_CNAME)
|
||||
break;
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
continue;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -1103,68 +1157,93 @@ 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;
|
||||
struct mx_record *mx;
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (hostname_isequal(name, mx->mxname))
|
||||
{
|
||||
ans = found = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX,
|
||||
mx->mxtarget ? mx->mxtarget : daemon->mxtarget, NULL);
|
||||
anscount++;
|
||||
}
|
||||
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", mx->preference,
|
||||
mx->mxtarget ? mx->mxtarget : daemon->mxtarget))
|
||||
anscount++;
|
||||
}
|
||||
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;
|
||||
struct srv_record *srv;
|
||||
|
||||
if (qtype == T_MAILB)
|
||||
ans = 1, nxdomain = 1;
|
||||
for (srv = daemon->srvnames; srv; srv = srv->next)
|
||||
if (hostname_isequal(name, srv->srvname))
|
||||
{
|
||||
found = ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
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,
|
||||
NULL, T_SRV, C_IN, "sssd",
|
||||
srv->priority, srv->weight, srv->srvport, srv->srvtarget))
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1175,7 +1254,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
|
||||
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
|
||||
|
||||
426
src/rfc2131.c
426
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
|
||||
@@ -57,13 +57,14 @@
|
||||
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
|
||||
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
|
||||
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
|
||||
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
|
||||
static void bootp_option_put(struct dhcp_packet *mess,
|
||||
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
|
||||
static int option_len(unsigned char *opt);
|
||||
static void *option_ptr(unsigned char *opt);
|
||||
static struct in_addr option_addr(unsigned char *opt);
|
||||
static unsigned int option_uint(unsigned char *opt, int size);
|
||||
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize);
|
||||
static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
unsigned char *p, unsigned char *end,
|
||||
unsigned char *req_options,
|
||||
@@ -81,17 +82,18 @@ static int have_config(struct dhcp_config *config, unsigned int mask)
|
||||
int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now)
|
||||
{
|
||||
struct dhcp_context *context, *context_tmp;
|
||||
unsigned char *opt, *clid;
|
||||
unsigned char *opt, *clid = NULL;
|
||||
struct dhcp_lease *lease, *ltmp;
|
||||
struct dhcp_vendor *vendor;
|
||||
int clid_len;
|
||||
struct dhcp_netid_list *id_list;
|
||||
int clid_len = 0, ignore = 0;
|
||||
struct dhcp_packet *mess = &daemon->dhcp_packet->data;
|
||||
unsigned char *p = mess->options + sizeof(u32); /* skip cookie */
|
||||
unsigned char *end = (unsigned char *)(daemon->dhcp_packet + 1);
|
||||
char *hostname = NULL;
|
||||
char *req_options = NULL;
|
||||
char *message = NULL;
|
||||
unsigned int renewal_time, expires_time, def_time;
|
||||
unsigned int time;
|
||||
struct dhcp_config *config;
|
||||
struct dhcp_netid *netid = NULL;
|
||||
struct in_addr addr, subnet_addr;
|
||||
@@ -123,7 +125,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
return 0;
|
||||
|
||||
/* check for DHCP rather than BOOTP */
|
||||
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
|
||||
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
|
||||
{
|
||||
mess_type = option_uint(opt, 1);
|
||||
|
||||
@@ -133,12 +135,21 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
|
||||
/* Some buggy clients set ciaddr when they shouldn't, so clear that here since
|
||||
it can affect the context-determination code. */
|
||||
if ((option_find(mess, sz, OPTION_REQUESTED_IP) || mess_type == DHCPDISCOVER))
|
||||
if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
|
||||
mess->ciaddr.s_addr = 0;
|
||||
|
||||
/* Check for RFC3011 subnet selector */
|
||||
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT)))
|
||||
if ((opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
|
||||
subnet_addr = option_addr(opt);
|
||||
|
||||
/* If there is no client identifier option, use the hardware address */
|
||||
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
|
||||
{
|
||||
clid_len = option_len(opt);
|
||||
clid = option_ptr(opt);
|
||||
}
|
||||
else
|
||||
clid = mess->chaddr;
|
||||
}
|
||||
|
||||
/* Determine network for this packet. If the machine has an address already, and we don't have
|
||||
@@ -159,13 +170,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
{
|
||||
context_tmp->current = context;
|
||||
context = context_tmp;
|
||||
|
||||
/* start to build netid chain */
|
||||
if (context_tmp->netid.net)
|
||||
{
|
||||
context_tmp->netid.next = netid;
|
||||
netid = &context_tmp->netid;
|
||||
}
|
||||
}
|
||||
|
||||
if (!context)
|
||||
@@ -178,63 +182,77 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
|
||||
mess->op = BOOTREPLY;
|
||||
|
||||
config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL);
|
||||
|
||||
if (mess_type == 0)
|
||||
{
|
||||
/* BOOTP request */
|
||||
config = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, NULL);
|
||||
if (have_config(config, CONFIG_ADDR) &&
|
||||
!have_config(config, CONFIG_DISABLE) &&
|
||||
!lease_find_by_addr(config->addr))
|
||||
struct dhcp_netid id;
|
||||
char save = mess->file[128];
|
||||
struct in_addr *logaddr = NULL;
|
||||
|
||||
if (have_config(config, CONFIG_ADDR))
|
||||
{
|
||||
struct dhcp_netid id;
|
||||
char save = mess->file[128];
|
||||
end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
|
||||
logaddr = &config->addr;
|
||||
mess->yiaddr = config->addr;
|
||||
mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
|
||||
if (have_config(config, CONFIG_NAME))
|
||||
hostname = config->hostname;
|
||||
if (have_config(config, CONFIG_NETID))
|
||||
{
|
||||
config->netid.next = netid;
|
||||
netid = &config->netid;
|
||||
}
|
||||
/* Match incoming filename field as a netid. */
|
||||
if (mess->file[0])
|
||||
{
|
||||
mess->file[128] = 0; /* ensure zero term. */
|
||||
id.net = mess->file;
|
||||
id.next = netid;
|
||||
netid = &id;
|
||||
}
|
||||
p = do_req_options(context, p, end, NULL, daemon,
|
||||
hostname, iface_addr, netid, subnet_addr);
|
||||
/* must do this after do_req_options since it overwrites filename field. */
|
||||
bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
|
||||
p = option_end(p, end, mess);
|
||||
log_packet(NULL, &config->addr, mess->chaddr, iface_name, NULL);
|
||||
mess->file[128] = save;
|
||||
return p - (unsigned char *)mess;
|
||||
if (lease_find_by_addr(config->addr))
|
||||
message = "address in use";
|
||||
context = narrow_context(context, config->addr);
|
||||
}
|
||||
return 0;
|
||||
else
|
||||
message = "no address configured";
|
||||
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
message = "disabled";
|
||||
|
||||
end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
|
||||
|
||||
if (have_config(config, CONFIG_NAME))
|
||||
hostname = config->hostname;
|
||||
|
||||
if (context->netid.net && !context->filter_netid)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
netid = &context->netid;
|
||||
}
|
||||
|
||||
if (have_config(config, CONFIG_NETID))
|
||||
{
|
||||
config->netid.next = netid;
|
||||
netid = &config->netid;
|
||||
}
|
||||
|
||||
/* Match incoming filename field as a netid. */
|
||||
if (mess->file[0])
|
||||
{
|
||||
mess->file[128] = 0; /* ensure zero term. */
|
||||
id.net = mess->file;
|
||||
id.next = netid;
|
||||
netid = &id;
|
||||
}
|
||||
|
||||
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
|
||||
if (match_netid(id_list->list, netid))
|
||||
message = "disabled";
|
||||
|
||||
p = do_req_options(context, p, end, NULL, daemon,
|
||||
hostname, iface_addr, netid, subnet_addr);
|
||||
/* must do this after do_req_options since it overwrites filename field. */
|
||||
mess->siaddr = iface_addr;
|
||||
bootp_option_put(mess, daemon->boot_config, netid);
|
||||
p = option_end(p, end, mess);
|
||||
log_packet(NULL, logaddr, mess->chaddr, iface_name, message);
|
||||
mess->file[128] = save;
|
||||
|
||||
if (message)
|
||||
return 0;
|
||||
else
|
||||
return p - (unsigned char *)mess;
|
||||
}
|
||||
|
||||
/* If there is no client identifier option, use the hardware address */
|
||||
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
|
||||
{
|
||||
clid = option_ptr(opt);
|
||||
clid_len = option_len(opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
config = find_config(daemon->dhcp_conf, context, clid, clid_len, mess->chaddr, NULL);
|
||||
|
||||
if (have_config(config, CONFIG_NAME))
|
||||
hostname = config->hostname;
|
||||
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
|
||||
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
hostname = daemon->dhcp_buff;
|
||||
@@ -244,33 +262,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
/* ensure there are no strange chars in there */
|
||||
if (!canonicalise(hostname))
|
||||
hostname = NULL;
|
||||
else
|
||||
else if ((hostname = strip_hostname(daemon, hostname)) && !config)
|
||||
{
|
||||
char *dot = strchr(hostname, '.');
|
||||
if (dot)
|
||||
{
|
||||
if (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix))
|
||||
{
|
||||
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
|
||||
hostname = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dot = 0; /* truncate */
|
||||
if (strlen(hostname) == 0)
|
||||
hostname = NULL; /* nothing left */
|
||||
}
|
||||
}
|
||||
|
||||
/* Search again now we have a hostname.
|
||||
Only accept configs without CLID and HWADDR here, (they won't match)
|
||||
to avoid impersonation by name. */
|
||||
if (!config)
|
||||
{
|
||||
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname);
|
||||
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
|
||||
config = new;
|
||||
}
|
||||
struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0, mess->chaddr, hostname);
|
||||
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
|
||||
config = new;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,46 +279,45 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
netid = &config->netid;
|
||||
}
|
||||
|
||||
/* Theres a chance that carefully chosen data could match the same
|
||||
vendor/user option twice and make a loop in the netid chain. */
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
vendor->used = 0;
|
||||
/* user-class options are, according to RFC3004, supposed to contain
|
||||
a set of counted strings. Here we check that this is so (by seeing
|
||||
if the counts are consistent with the overall option length) and if
|
||||
so zero the counts so that we don't get spurious matches between
|
||||
the vendor string and the counts. If the lengths don't add up, we
|
||||
assume that the option is a single string and non RFC3004 compliant
|
||||
and just do the substring match. dhclient provides these broken options. */
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
if (vendor->is_vendor && !vendor->used)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
|
||||
{
|
||||
vendor->used = 1;
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS)))
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
|
||||
{
|
||||
unsigned char *ucp = option_ptr(opt);
|
||||
int j;
|
||||
for (j = 0; j < option_len(opt); j += ucp[j] + 1)
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
if (!vendor->is_vendor && !vendor->used)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (ucp[j] - vendor->len); i++)
|
||||
if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0)
|
||||
{
|
||||
vendor->used = 1;
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned char *ucp = option_ptr(opt);
|
||||
int tmp, j;
|
||||
for (j = 0; j < option_len(opt); j += ucp[j] + 1);
|
||||
if (j == option_len(opt))
|
||||
for (j = 0; j < option_len(opt); j = tmp)
|
||||
{
|
||||
tmp = j + ucp[j] + 1;
|
||||
ucp[j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
|
||||
{
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if all the netids in the ignore list are present, ignore this client */
|
||||
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
|
||||
if (match_netid(id_list->list, netid))
|
||||
ignore = 1;
|
||||
|
||||
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
|
||||
if (have_config(config, CONFIG_NOCLID))
|
||||
{
|
||||
@@ -330,44 +328,22 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
|
||||
{
|
||||
unsigned int req_time = option_uint(opt, 4);
|
||||
|
||||
if (def_time == 0xffffffff ||
|
||||
(req_time != 0xffffffff && req_time < def_time))
|
||||
expires_time = renewal_time = req_time;
|
||||
else
|
||||
expires_time = renewal_time = def_time;
|
||||
}
|
||||
else
|
||||
{
|
||||
renewal_time = def_time;
|
||||
if (lease)
|
||||
expires_time = (unsigned int)difftime(lease->expires, now);
|
||||
else
|
||||
expires_time = def_time;
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = daemon->dhcp_buff2;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
memcpy(req_options, option_ptr(opt), option_len(opt));
|
||||
req_options[option_len(opt)] = OPTION_END;
|
||||
}
|
||||
|
||||
switch (mess_type)
|
||||
{
|
||||
case DHCPDECLINE:
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
||||
(iface_addr.s_addr != option_addr(opt).s_addr))
|
||||
return 0;
|
||||
|
||||
/* sanitise any message. Paranoid? Moi? */
|
||||
if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
|
||||
if ((opt = option_find(mess, sz, OPTION_MESSAGE, 1)))
|
||||
{
|
||||
char *p = option_ptr(opt), *q = daemon->dhcp_buff;
|
||||
int i;
|
||||
@@ -382,7 +358,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
message = daemon->dhcp_buff;
|
||||
}
|
||||
|
||||
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
|
||||
return 0;
|
||||
|
||||
log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
|
||||
@@ -404,7 +380,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
return 0;
|
||||
|
||||
case DHCPRELEASE:
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
||||
(iface_addr.s_addr != option_addr(opt).s_addr))
|
||||
return 0;
|
||||
|
||||
@@ -418,9 +394,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
return 0;
|
||||
|
||||
case DHCPDISCOVER:
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
|
||||
addr = option_addr(opt);
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
if (ignore || have_config(config, CONFIG_DISABLE))
|
||||
message = "ignored";
|
||||
else if (have_config(config, CONFIG_ADDR) &&
|
||||
(!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
|
||||
@@ -430,23 +406,40 @@ 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);
|
||||
|
||||
if (message)
|
||||
return 0;
|
||||
|
||||
bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
|
||||
mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
|
||||
context = narrow_context(context, mess->yiaddr);
|
||||
if (context->netid.net && !context->filter_netid)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
netid = &context->netid;
|
||||
}
|
||||
|
||||
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
|
||||
{
|
||||
unsigned int req_time = option_uint(opt, 4);
|
||||
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
|
||||
time = req_time;
|
||||
}
|
||||
else if (lease && lease->expires != 0)
|
||||
time = (unsigned int)difftime(lease->expires, now);
|
||||
|
||||
mess->siaddr = iface_addr;
|
||||
bootp_option_put(mess, daemon->boot_config, netid);
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
|
||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||
if (expires_time != 0xffffffff)
|
||||
if (time != 0xffffffff)
|
||||
{
|
||||
p = option_put(p, end, OPTION_T1, 4, (expires_time/2));
|
||||
p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8));
|
||||
p = option_put(p, end, OPTION_T1, 4, (time/2));
|
||||
p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
|
||||
}
|
||||
p = do_req_options(context, p, end, req_options, daemon,
|
||||
NULL, iface_addr, netid, subnet_addr);
|
||||
@@ -456,14 +449,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
case DHCPREQUEST:
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
message = "disabled";
|
||||
else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
if (ignore || have_config(config, CONFIG_DISABLE))
|
||||
return 0;
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
|
||||
{
|
||||
/* SELECTING or INIT_REBOOT */
|
||||
mess->yiaddr = option_addr(opt);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)))
|
||||
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
|
||||
{
|
||||
/* SELECTING */
|
||||
if (iface_addr.s_addr != option_addr(opt).s_addr)
|
||||
@@ -495,9 +488,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
|
||||
/* desynchronise renewals */
|
||||
fuzz = rand16();
|
||||
while (fuzz > (renewal_time/16))
|
||||
fuzz = fuzz/2;
|
||||
|
||||
mess->yiaddr = mess->ciaddr;
|
||||
}
|
||||
|
||||
@@ -550,20 +540,39 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
{
|
||||
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
|
||||
|
||||
context = narrow_context(context, mess->yiaddr);
|
||||
if (context->netid.net && !context->filter_netid)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
netid = &context->netid;
|
||||
}
|
||||
|
||||
time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME, 4)))
|
||||
{
|
||||
unsigned int req_time = option_uint(opt, 4);
|
||||
if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
|
||||
time = req_time;
|
||||
}
|
||||
|
||||
lease_set_hwaddr(lease, mess->chaddr);
|
||||
if (!hostname)
|
||||
hostname = host_from_dns(daemon, mess->yiaddr);
|
||||
if (hostname)
|
||||
lease_set_hostname(lease, hostname, daemon->domain_suffix);
|
||||
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
|
||||
lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time);
|
||||
|
||||
bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
|
||||
mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
|
||||
mess->siaddr = iface_addr;
|
||||
bootp_option_put(mess, daemon->boot_config, netid);
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
|
||||
if (renewal_time != 0xffffffff)
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
|
||||
if (time != 0xffffffff)
|
||||
{
|
||||
p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
|
||||
p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
|
||||
while (fuzz > (time/16))
|
||||
fuzz = fuzz/2;
|
||||
p = option_put(p, end, OPTION_T1, 4, (time/2) - fuzz);
|
||||
p = option_put(p, end, OPTION_T2, 4, ((time * 7)/8) - fuzz);
|
||||
}
|
||||
p = do_req_options(context, p, end, req_options, daemon,
|
||||
hostname, iface_addr, netid, subnet_addr);
|
||||
@@ -573,7 +582,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
case DHCPINFORM:
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
if (ignore || have_config(config, CONFIG_DISABLE))
|
||||
message = "ignored";
|
||||
|
||||
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message);
|
||||
@@ -581,8 +590,19 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
|
||||
if (message || mess->ciaddr.s_addr == 0)
|
||||
return 0;
|
||||
|
||||
context = narrow_context(context, mess->ciaddr);
|
||||
if (context->netid.net)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
netid = &context->netid;
|
||||
}
|
||||
|
||||
mess->siaddr = iface_addr;
|
||||
bootp_option_put(mess, daemon->boot_config, netid);
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
if (!hostname)
|
||||
hostname = host_from_dns(daemon, mess->yiaddr);
|
||||
p = do_req_options(context, p, end, req_options, daemon,
|
||||
hostname, iface_addr, netid, subnet_addr);
|
||||
p = option_end(p, end, mess);
|
||||
@@ -641,14 +661,35 @@ static unsigned int option_uint(unsigned char *opt, int size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
|
||||
static void bootp_option_put(struct dhcp_packet *mess,
|
||||
struct dhcp_boot *boot_opts, struct dhcp_netid *netids)
|
||||
{
|
||||
struct dhcp_boot *tmp;
|
||||
|
||||
for (tmp = boot_opts; tmp; tmp = tmp->next)
|
||||
if (match_netid(tmp->netid, netids))
|
||||
break;
|
||||
if (!tmp)
|
||||
/* No match, look for one without a netid */
|
||||
for (tmp = boot_opts; tmp; tmp = tmp->next)
|
||||
if (!tmp->netid)
|
||||
break;
|
||||
|
||||
/* Do this _after_ the matching above, since in
|
||||
BOOTP mode, one if the things we match is the filename. */
|
||||
|
||||
memset(mess->sname, 0, sizeof(mess->sname));
|
||||
memset(mess->file, 0, sizeof(mess->file));
|
||||
if (sname)
|
||||
strncpy(mess->sname, sname, sizeof(mess->sname)-1);
|
||||
if (filename)
|
||||
strncpy(mess->file, filename, sizeof(mess->file)-1);
|
||||
|
||||
if (tmp)
|
||||
{
|
||||
if (tmp->sname)
|
||||
strncpy(mess->sname, tmp->sname, sizeof(mess->sname)-1);
|
||||
if (tmp->file)
|
||||
strncpy(mess->file, tmp->file, sizeof(mess->file)-1);
|
||||
if (tmp->next_server.s_addr)
|
||||
mess->siaddr = tmp->next_server;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
|
||||
@@ -698,13 +739,13 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
|
||||
|
||||
while (*p != OPTION_END)
|
||||
{
|
||||
if (end && (p >= end))
|
||||
if (p >= end)
|
||||
return 0; /* malformed packet */
|
||||
else if (*p == OPTION_PAD)
|
||||
p++;
|
||||
else if (*p == OPTION_OVERLOAD)
|
||||
{
|
||||
if (end && (p >= end - 3))
|
||||
if (p >= end - 3)
|
||||
return 0; /* malformed packet */
|
||||
if (overload)
|
||||
*overload = *(p+2);
|
||||
@@ -713,10 +754,10 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
|
||||
else
|
||||
{
|
||||
int opt_len;;
|
||||
if (end && (p >= end - 2))
|
||||
if (p >= end - 2)
|
||||
return 0; /* malformed packet */
|
||||
opt_len = option_len(p);
|
||||
if (end && (p >= end - (2 + opt_len)))
|
||||
if (p >= end - (2 + opt_len))
|
||||
return 0; /* malformed packet */
|
||||
if (*p == opt)
|
||||
return p;
|
||||
@@ -727,7 +768,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type)
|
||||
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize)
|
||||
{
|
||||
int overload = 0;
|
||||
unsigned char *ret;
|
||||
@@ -740,7 +781,11 @@ static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_ty
|
||||
|
||||
if (!ret && (overload & 2))
|
||||
ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload);
|
||||
|
||||
|
||||
/* Check the option field is big enough */
|
||||
if (ret && (option_len(ret) < minsize))
|
||||
ret = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -762,17 +807,14 @@ static int in_list(unsigned char *list, int opt)
|
||||
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
|
||||
{
|
||||
struct dhcp_opt *tmp;
|
||||
struct dhcp_netid *tmp1;
|
||||
|
||||
for (tmp = opts; tmp; tmp = tmp->next)
|
||||
if (tmp->opt == opt)
|
||||
{
|
||||
if (netid)
|
||||
{
|
||||
if (tmp->netid)
|
||||
for (tmp1 = netid; tmp1; tmp1 = tmp1->next)
|
||||
if (strcmp(tmp->netid, tmp1->net) == 0)
|
||||
return tmp;
|
||||
if (match_netid(tmp->netid, netid))
|
||||
return tmp;
|
||||
}
|
||||
else if (!tmp->netid)
|
||||
return tmp;
|
||||
|
||||
12
src/util.c
12
src/util.c
@@ -113,20 +113,22 @@ 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 */
|
||||
int 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;
|
||||
|
||||
Reference in New Issue
Block a user