Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfa666f24b | ||
|
|
feba5c1d25 | ||
|
|
de37951cf4 | ||
|
|
a222641cb0 | ||
|
|
a84fa1d085 | ||
|
|
33820b7ed9 | ||
|
|
8a911ccc75 |
298
CHANGELOG
298
CHANGELOG
@@ -843,5 +843,303 @@ release 2.4
|
||||
|
||||
Added "bind-interfaces" option correctly.
|
||||
|
||||
release 2.5
|
||||
Made "where are we allocating addresses?" code in DHCP
|
||||
server cope with requests via a relay which is on a
|
||||
directly connected network for which there is not a
|
||||
configured netmask. This strange state of affairs occurs
|
||||
with win4lin. Thanks to Alex Melt and Jim Horner for bug
|
||||
reports and testing with this.
|
||||
|
||||
Fixed trivial-but-irritating missing #include which broke
|
||||
compilation on *BSD.
|
||||
|
||||
Force --bind-interfaces if IP-aliased interface
|
||||
specifications are used, since the sockets API provides
|
||||
no other sane way to determine which alias of an
|
||||
interface a packet was sent to. Thanks to Javier Kohen
|
||||
for the bug report.
|
||||
|
||||
release 2.6
|
||||
Support Token Ring DHCP. Thanks to Dag Wieers for help
|
||||
testing. Note that Token ring support only works on Linux
|
||||
currently.
|
||||
|
||||
Fix compilation on MacOS X. Thanks to Bernhard Ehlers for
|
||||
the patch.
|
||||
|
||||
Added new "ignore" keyword for
|
||||
dhcp-host. "dhcp-host=11:22:33:44:55:66,ignore" will
|
||||
cause the DHCP server to ignore any host with the given
|
||||
MAC address, leaving it to other servers on the
|
||||
network. This also works with client-id and hostnames.
|
||||
Suggestion by Alex Melt.
|
||||
|
||||
Fixed parsing of hex client IDs. Problem spotted by Peter
|
||||
Fichtner.
|
||||
|
||||
Allow conf-file options in configuration file, to
|
||||
provide an include function.
|
||||
|
||||
Re-read /etc/ethers on receipt of SIGHUP.
|
||||
|
||||
Added back the ability to read ISC dhcpd lease files, by
|
||||
popular demand. Note that this is deprecated and for
|
||||
backwards compatibility only. You can get back the 4K of
|
||||
memory that the code occupies by undefining
|
||||
"HAVE_ISC_READER" in src/config.h
|
||||
|
||||
Added ability to disable "pool" DHCP address allocation
|
||||
whilst leaving static leases working. The syntax is
|
||||
"dhcp-range=192.168.0.0,static"
|
||||
Thanks to Grzegorz Nosek for the suggestion.
|
||||
|
||||
Generalized dnsmasq-rh.spec file to work on Mandrake too,
|
||||
and removed dnsmasq-mdk.spec. Thanks to Doug Keller.
|
||||
|
||||
Allow DHCP options which are tied to specific static
|
||||
leases in the same way as to specific networks.
|
||||
|
||||
Generalised the dhcp-option parser a bit to allow hex
|
||||
strings as parameters. This is now legal:
|
||||
dhcp-option=128,e4:45:74:68:00:00
|
||||
Inspired by a patch from Joel Nordell.
|
||||
|
||||
Changed the semantics of argument-less dhcp-options for
|
||||
the default-setting ones, ie 1, 3, 6 and 28. Now, doing
|
||||
eg, dhcp-option=3 stops dnsmasq from sending a default
|
||||
router option at all. Thanks to Scott Emmons for pointing
|
||||
out that this is useful.
|
||||
|
||||
Fixed dnsmasq.conf parsing bug which interpreted port
|
||||
numbers in server= lines as a comment. To start a
|
||||
comment, a '#' character must now be a the start of a
|
||||
line or preceded by whitespace. Thanks to Christian
|
||||
Haggstrom for the bug report.
|
||||
|
||||
release 2.7
|
||||
Allow the dhcp-host specification of id:* which makes
|
||||
dnsmasq ignore any client-id. This is useful to ensure
|
||||
that a dual-boot machine sees the same lease when one OS
|
||||
gives a client-id and the other doesn't. It's also useful
|
||||
when PXE boot DHCP does not use client IDs but the OS it boots
|
||||
does. Thanks to Grzegorz Nosek for suggesting this enhancement.
|
||||
|
||||
No longer assume that ciaddr is zero in received DHCPDISCOVER
|
||||
messages, just for security against broken clients.
|
||||
|
||||
Set default of siaddr field to the address of the machine running
|
||||
dnsmasq when not explicitly set using dhcp-boot
|
||||
option. This is the ISC dhcpd behaviour.
|
||||
|
||||
Send T1 and T2 options in DHCPOFFER packets. This is required
|
||||
by the DHCP client in some JetDirect printers. Thanks
|
||||
to Paul Mattal for work on this.
|
||||
|
||||
Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel.
|
||||
The code which added loopback interfaces to the list
|
||||
was confusing the DHCP code, which expected one interface only.
|
||||
Solved by adding loopback interfaces to address list instead.
|
||||
|
||||
Add dhcp-vendorclass option to allow options to be sent only
|
||||
to certain classes of clients.
|
||||
|
||||
Tweaked option search code so that if a netid-qualified
|
||||
option is used, any unqualified option is ignored.
|
||||
|
||||
Changed the method of picking new dynamic IP
|
||||
addresses. This used to use the next consecutive
|
||||
address as long it was free, now it uses a hash
|
||||
from the client hardware address. This reduces the amount
|
||||
of address movement for clients which let their lease
|
||||
expire and allows consecutive DHCPOFFERS to the same host
|
||||
to (almost always) be for the same address, without
|
||||
storing state before a lease is granted.
|
||||
|
||||
Tweaked option handling code to return all possible
|
||||
options rather than none when DHCP "requested options"
|
||||
field is missing. This fixes interoperability with
|
||||
ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for
|
||||
help with this.
|
||||
|
||||
release 2.8
|
||||
Pad DHCP packets to a minimum size of 300 bytes. This
|
||||
fixes interoperability problems with the Linux in-kernel
|
||||
DHCP/BOOTP client. Thanks to Richard Musil for
|
||||
diagnosing this and supplying a patch.
|
||||
|
||||
Fixed option-parsing bug and potential memory leak. Patch
|
||||
from Richard Musil.
|
||||
|
||||
Improved vendor class configuration and added user class
|
||||
configuration. Specifically: (1) options are matched on
|
||||
the netids from dhcp-range, dhcp-host, vendor class and
|
||||
user class(es). Multiple net-ids are allowed and options
|
||||
are searched on them all. (2) matches agains vendor class
|
||||
and user class are now on a substring, if the given
|
||||
string is a substring of the vendor/user class, then a
|
||||
match occurs. Thanks again to Richard Musil for prompting
|
||||
this.
|
||||
|
||||
Make "#" match any domain on --address and --server
|
||||
flags. --address=/#/1.2.3.4 will return 1.2.3.4 for _any_
|
||||
domain not otherwise matched. Of course
|
||||
--server=/#/1.2.3.4 is exactly equivalent to
|
||||
--server=1.2.3.4. Special request from Josh Howlett.
|
||||
|
||||
Fixed a nasty bug which would cause dnsmasq to lose track
|
||||
of leases for hosts which had a --dhcp-host flag without
|
||||
a name specification. The mechanism for this was that
|
||||
the hostname could get erroneously set as a zero-length
|
||||
string and then written to the leases file as a
|
||||
mal-formed line. Restarting dnsmasq would then lose the lease.
|
||||
Alex Hermann's work helped chase down this problem.
|
||||
|
||||
Add checks against DHCP clients which return zero-length
|
||||
hostnames. This avoids the potential lease-loss problems
|
||||
reffered to above. Also, if a client sends a hostname when
|
||||
it creates a lease but subsequently sends no or a
|
||||
zero-length hostname whilst renewing, continue to use the
|
||||
existing hostname, don't wipe it out.
|
||||
|
||||
Tweaked option parsing to flag some parameter errors.
|
||||
|
||||
release 2.9
|
||||
Fixed interface filter code for two effects: 1) Fixed bug
|
||||
where queries sent via loopback interface
|
||||
but to the address of another interface were ignored
|
||||
unless the loopback interface was explicitly configured.
|
||||
2) on OpenBSD failure to configure one interface now
|
||||
causes a fatal error on startup rather than an huge
|
||||
stream of log messages. Thanks to Erik Jan Tromp for
|
||||
finding that bug.
|
||||
|
||||
Changed server selection strategy to improve performance
|
||||
when there are many available servers and some are
|
||||
broken. The new algorithm is to pick as before for the
|
||||
first try, but if a query is retried, to send to all
|
||||
available servers in parallel. The first one to reply
|
||||
then becomes prefered for the next query. This should
|
||||
improve reliability without generating significant extra
|
||||
upstream load.
|
||||
|
||||
Fixed breakage of special servers/addresses for
|
||||
unqualified domains introduced in version 2.8
|
||||
|
||||
Allow fallback to "bind-interfaces" at runtime: Some
|
||||
verions of *BSD seem to have enough stuff in the header
|
||||
files to build but no kernel support. Also now log if
|
||||
"bind-interfaces" is forced on.
|
||||
|
||||
Log replies from upstream servers which refuse to do
|
||||
recursion - dnsmasq is not a recursive nameserver and
|
||||
relies on upstream servers to do the recursion, this
|
||||
flags a configuration error.
|
||||
|
||||
Disable client-id matching for hosts whose MAC address is
|
||||
read from /etc/ethers. Patch from Oleg I. Vdovikin.
|
||||
|
||||
Extended --mx-host flag to allow arbitrary targets for MX
|
||||
records, suggested by Moritz Bunkus.
|
||||
|
||||
Fixed build under NetBSD 2.0 - thanks to Felix Deichmann
|
||||
for the patch.
|
||||
|
||||
Deal correctly with repeated addresses in /etc/hosts. The
|
||||
first name found is now returned for reverse lookups,
|
||||
rather than all of them.
|
||||
|
||||
Add back fatal errors when nonexistant
|
||||
interfaces or interface addresses are given but only in
|
||||
"bind-interfaces" mode. Principle of least surprise applies.
|
||||
|
||||
Allow # as the argument to --domain, meaning "read the
|
||||
domain from the first search directive in
|
||||
/etc.resolv.conf". Feature suggested by Evan Jones.
|
||||
|
||||
release 2.10
|
||||
Allow --query-port to be set to a low port by creating and
|
||||
binding the socket before dropping root. (Suggestion from
|
||||
Jamie Lokier)
|
||||
|
||||
Support TCP queries. It turned out to be possible to do
|
||||
this with a couple of hundred lines of code, once I knew
|
||||
how. The executable size went up by a few K on i386.
|
||||
There are a few limitations: data obtained via TCP is not
|
||||
cached, and dynamically-created interfaces may break under
|
||||
certain circumstances. Source-address or query-port
|
||||
specifications are ignored for TCP.
|
||||
|
||||
NAK attempts to renew a DHCP lease where the DHCP range
|
||||
has changed and the lease is no longer in the allowed
|
||||
range. Jamie Lokier pointed out this bug.
|
||||
|
||||
NAK attempts to renew a pool DHCP lease when a statically
|
||||
allocated address has become available, forcing a host to
|
||||
move to it's allocated address. Lots of people have
|
||||
suggested this change and been rebuffed (they know who
|
||||
they are) the straws that broke the camel's back were Tim
|
||||
Cutts and Jamie Lokier.
|
||||
|
||||
Remove any nameserver records from answers which are
|
||||
modified by --alias flags. If the answer is modified, it
|
||||
cannot any longer be authoritative.
|
||||
|
||||
Change behaviour of "bogus-priv" option to return NXDOMAIN
|
||||
rather than a PTR record with the dotted-quad address as
|
||||
name. The new behaviour doesn't provoke tcpwrappers like
|
||||
the old behavior did.
|
||||
|
||||
Added a patch for the Suse rpm. That changes the default
|
||||
group to one suitable for Suse and disables inclusion of
|
||||
the ISC lease-file reader code. Thanks to Andy Cambeis for
|
||||
his ongoing work on Suse packaging.
|
||||
|
||||
Support forwarding of EDNS.0 The maximum UDP packet size
|
||||
defaults to 1280, but may be changed with the
|
||||
--edns-packet-max option. Detect queries with the do bit
|
||||
set and always forward them, since DNSSEC records are
|
||||
not cached. This behaviour is required to make
|
||||
DNSSECbis work properly though dnsmasq. Thanks to Simon
|
||||
Josefsson for help with this.
|
||||
|
||||
Move default config file location under OpenBSD from
|
||||
/usr/local/etc/dnsmasq.conf to /etc/dnsmasq.conf. Bug
|
||||
report from Jonathan Weiss.
|
||||
|
||||
Use a lease with matching MAC address for a host which
|
||||
doesn't present a client-id, even if there was a client ID
|
||||
at some point in the past. This reduces surprises when
|
||||
changing DHCP clients, adding id:* to a host, and from the
|
||||
semantics change of /etc/ethers in 2.9. Thanks to Bernard
|
||||
Sammer for finding that.
|
||||
|
||||
Added a "contrib" directory and in it the dnslist utility,
|
||||
from Thomas Tuttle.
|
||||
|
||||
Fixed "fail to start up" problems under Linux with IPv6
|
||||
enabled. It's not clear that these were an issue in
|
||||
released versions, but they manifested themselves when TCP
|
||||
support was added. Thanks to Michael Hamilton for
|
||||
assistance with this.
|
||||
|
||||
version 2.11
|
||||
Fixed DHCP problem which could result in two leases in the
|
||||
database with the same address. This looked much more
|
||||
alarming then it was, since it could only happen when a
|
||||
machine changes MAC address but kept the same name. The
|
||||
old lease would persist until it timed out but things
|
||||
would still work OK.
|
||||
|
||||
Check that IP addresses in all dhcp-host directives are
|
||||
unique and die horribly if they are not, since otherwise
|
||||
endless protocol loops can occur.
|
||||
|
||||
Use IPV6_RECVPKTINFO as socket option rather than
|
||||
IPV6_PKTINFO where available. This keeps late-model FreeBSD
|
||||
happy.
|
||||
|
||||
Set source interface when replying to IPv6 UDP
|
||||
queries. This is needed to cope with link-local addresses.
|
||||
|
||||
|
||||
|
||||
100
FAQ
100
FAQ
@@ -20,12 +20,10 @@ A: The high ports that dnsmasq opens is for replies from the upstream
|
||||
Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
|
||||
that?
|
||||
|
||||
A: Yes, they do, so technically dnsmasq is not RFC-compliant. In practice, the
|
||||
sorts of queries which dnsmasq is used for are always sent via UDP. Adding
|
||||
TCP support would make dnsmasq much more heavyweight for no practical
|
||||
benefit. If you really want to do zone transfers, forward port 53 TCP
|
||||
using in-kernel port-forwarding or a port-fowarder like rinetd.
|
||||
|
||||
A: Update: from version 2.10, it does. There are a few limitations:
|
||||
data obtained via TCP is not cached, and dynamically-created
|
||||
interfaces may break under certain circumstances. Source-address
|
||||
or query-port specifications are ignored for TCP.
|
||||
|
||||
Q: When I send SIGUSR1 to dump the contents of the cache, some entries have
|
||||
no IP address and are for names like mymachine.mydomain.com.mydomain.com.
|
||||
@@ -74,6 +72,8 @@ A: Use the standard DNS convention of <reversed address>.in-addr.arpa.
|
||||
For instance to send reverse queries on the range 192.168.0.0 to
|
||||
192.168.0.255 to a nameserver at 10.0.0.1 do
|
||||
server=/0.168.192.in-addr.arpa/10.0.0.1
|
||||
Note that the "bogus-priv" option take priority over this option,
|
||||
so the above will not work when the bogus-priv option is set.
|
||||
|
||||
Q: Dnsmasq fails to start with an error like this: "dnsmasq: bind
|
||||
failed: Cannot assign requested address". What's the problem?
|
||||
@@ -181,9 +181,91 @@ A: There are a couple of configuration gotchas which have been
|
||||
and from ports 67 and 68 and broadcast packets with source
|
||||
address 0.0.0.0 and destination address 255.255.255.255 are not
|
||||
dropped by iptables/ipchains.
|
||||
|
||||
Q: I'm running Debian, and my machines get an address fine with DHCP,
|
||||
but their names are not appearing in the DNS.
|
||||
|
||||
A: By default, none of the DHCP clients send the host-name when asking
|
||||
for a lease. For most of the clients, you can set the host-name to
|
||||
send with the "hostname" keyword in /etc/network/interfaces. (See
|
||||
"man interfaces" for details.) That doesn't work for dhclient, were
|
||||
you have to add something like "send host-name daisy" to
|
||||
/etc/dhclient.conf
|
||||
|
||||
Q: I'm network booting my machines, and trying to give them static
|
||||
DHCP-assigned addresses. The machine gets its correct address
|
||||
whilst booting, but then the OS starts and it seems to get
|
||||
allocated a different address.
|
||||
|
||||
A: What is happening is this: The boot process sends a DHCP
|
||||
request and gets allocated the static address corresponding to its
|
||||
MAC address. The boot loader does not send a client-id. Then the OS
|
||||
starts and repeats the DHCP process, but it it does send a
|
||||
client-id. Dnsmasq cannot assume that the two requests are from the
|
||||
same machine (since the client ID's don't match) and even though
|
||||
the MAC address has a static allocation, that address is still in
|
||||
use by the first incarnation of the machine (the one from the boot,
|
||||
without a client ID.) dnsmasq therefore has to give the machine a
|
||||
dynamic address from its pool. There are three ways to solve this:
|
||||
(1) persuade your DHCP client not to send a client ID, or (2) set up
|
||||
the static assignment to the client ID, not the MAC address. The
|
||||
default client-id will be 01:<MAC address>, so change the dhcp-host
|
||||
line from "dhcp-host=11:22:33:44:55:66,1.2.3.4" to
|
||||
"dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4" or (3) tell dnsmasq to
|
||||
ignore client IDs for a particular MAC address, like this:
|
||||
dhcp-host=11:22:33:44:55:66,id:*
|
||||
|
||||
Q: What network types are supported by the DHCP server?
|
||||
|
||||
|
||||
|
||||
|
||||
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
|
||||
Linux Token Ring is also supported.
|
||||
|
||||
Q: What is this strange "bind-interface" option?
|
||||
|
||||
A: The DNS spec says that the reply to a DNS query must come from the
|
||||
same address it was sent to. The traditional way to write an UDP
|
||||
server to do this is to find all of the addresses belonging to the
|
||||
machine (ie all the interfaces on the machine) and then create a
|
||||
socket for each interface which is bound to the address of the
|
||||
interface. Then when a packet is sent to address A, it is received
|
||||
on the socket bound to address A and when the reply is also sent
|
||||
via that socket, the source address is set to A by the kernel and
|
||||
everything works. This is the how dnsmasq works when
|
||||
"bind-interfaces" is set, with the obvious extension that is misses
|
||||
out creating sockets for some interfaces depending on the
|
||||
--interface, --address and --except-interface flags. The
|
||||
disadvantage of this approach is that it breaks if interfaces don't
|
||||
exist or are not configured when the daemon starts and does the
|
||||
socket creation step. In a hotplug-aware world this is a real
|
||||
problem.
|
||||
|
||||
The alternative approach is to have only one socket, which is bound
|
||||
to the correct port and the wildcard IP address (0.0.0.0). That
|
||||
socket will receive _all_ packets sent to port 53, no matter what
|
||||
destination address they have. This solves the problem of
|
||||
interfaces which are created or reconfigured after daemon
|
||||
start-up. To make this work is more complicated because of the
|
||||
"reply source address" problem. When a UDP packet is sent by a
|
||||
socket bound to 0.0.0.0 its source address will be set to the
|
||||
address of one of the machine's interfaces, but which one is not
|
||||
determined and can vary depending on the OS being run. To get round
|
||||
this it is neccessary to use a scary advanced API to determine the
|
||||
address to which a query was sent, and force that to be the source
|
||||
address in the reply. For IPv4 this stuff in non-portable and quite
|
||||
often not even available (It's different between FreeBSD 5.x and
|
||||
Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
|
||||
have it at all.) Hence "bind-interfaces" has to always be available
|
||||
as a fall back. For IPv6 the API is standard and universally
|
||||
available.
|
||||
|
||||
It could be argued that if the --interface or --address flags are
|
||||
used then binding interfaces is more appropriate, but using
|
||||
wildcard binding means that dnsmasq will quite happily start up
|
||||
after being told to use interfaces which don't exist, but which are
|
||||
created later. Wildcard binding breaks the scenario when dnsmasq is
|
||||
listening on one interface and another server (most probably BIND)
|
||||
is listening on another. It's not possible for BIND to bind to an
|
||||
(address,port) pair when dnsmasq has bound (wildcard,port), hence
|
||||
the ability to explicitly turn off wildcard binding.
|
||||
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -10,7 +10,7 @@ all :
|
||||
@cd $(SRC); $(MAKE) dnsmasq
|
||||
|
||||
clean :
|
||||
rm -f *~ */*~ $(SRC)/*.o $(SRC)/dnsmasq core build
|
||||
rm -f *~ contrib/*/*~ */*~ $(SRC)/*.o $(SRC)/dnsmasq core build
|
||||
|
||||
install : all
|
||||
install -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
@@ -7,9 +7,11 @@ Version 1.x of dnsmasq includes a facility for reading the dhcp.leases
|
||||
file written by ISC dhcpd. This allows the names of machines which
|
||||
have addresses allocated by DHCP to be included in the DNS.
|
||||
|
||||
Version 2.x of dnsmasq removes the ISC dhcpd integration and replaces
|
||||
it with a DHCP server integrated into dnsmasq. This is an incompatible
|
||||
change in dnsmasq but it has the following advantages:
|
||||
Version 2.x of dnsmasq replaces the ISC dhcpd integration with a DHCP
|
||||
server integrated into dnsmasq. Versions 2.0-2.5 removed the ISC
|
||||
integration completely, but in version 2.6 it was re-enabled for
|
||||
backwards compatibility purposes. The change to an integrated DHCP
|
||||
server has the following advantages:
|
||||
|
||||
* Small. ISC dhcpd is a large and comprehensive DHCP solution. The
|
||||
dnsmasq DHCP server adds about 15k to DNS-only dnsmasq and provides
|
||||
@@ -29,7 +31,6 @@ change in dnsmasq but it has the following advantages:
|
||||
with the dnsmasq DHCP server.
|
||||
|
||||
|
||||
|
||||
DHCP configuration
|
||||
------------------
|
||||
|
||||
@@ -55,9 +56,9 @@ in the same way as before.
|
||||
Having started dnsmasq, tell any hosts on the network to renew their
|
||||
DHCP lease, so that dnsmasq's DHCP server becomes aware of them. For
|
||||
Linux, this is best done by killing-and-restarting the DHCP client
|
||||
daemon or taking the network interface down and then back up. For
|
||||
Windows use winipcfg.exe
|
||||
|
||||
daemon or taking the network interface down and then back up. For
|
||||
Windows 9x/Me, use the graphical tool "winipcfg". For Windows
|
||||
NT/2000/XP, use the command-line "ipconfig /renew"
|
||||
|
||||
For more complex DHCP configuration, refer to the doc/setup.html, the
|
||||
dnsmasq manpage and the annotated example configuration file. Also
|
||||
|
||||
57
contrib/dnslist/dhcp.css
Normal file
57
contrib/dnslist/dhcp.css
Normal file
@@ -0,0 +1,57 @@
|
||||
body
|
||||
{
|
||||
font-family: sans-serif;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 .updated
|
||||
{
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-collapse: collapse;
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
|
||||
th
|
||||
{
|
||||
background: #DDD;
|
||||
border-top: 2px solid #000;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Any row */
|
||||
|
||||
tr
|
||||
{
|
||||
border-top: 2px solid #000;
|
||||
}
|
||||
|
||||
/* Any row but the first or second (overrides above rule) */
|
||||
|
||||
tr + tr + tr
|
||||
{
|
||||
border-top: 2px solid #999;
|
||||
}
|
||||
|
||||
tr.offline td.hostname
|
||||
{
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.hostname { width: 10em; }
|
||||
.ip_addr { width: 10em; background: #DDD; }
|
||||
.ether_addr { width: 15em; }
|
||||
.client_id { width: 15em; background: #DDD; }
|
||||
.status { width: 5em; }
|
||||
.since { width: 10em; background: #DDD; }
|
||||
.lease { width: 10em; }
|
||||
608
contrib/dnslist/dnslist.pl
Executable file
608
contrib/dnslist/dnslist.pl
Executable file
@@ -0,0 +1,608 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# dnslist - Read state file from dnsmasq and create a nice web page to display
|
||||
# a list of DHCP clients.
|
||||
#
|
||||
# Copyright (C) 2004 Thomas Tuttle
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program*; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# * The license is in fact included at the end of this file, and can
|
||||
# either be viewed by reading everything after "__DATA__" or by
|
||||
# running dnslist with the '-l' option.
|
||||
#
|
||||
# Version: 0.2
|
||||
# Author: Thomas Tuttle
|
||||
# Email: dnslist.20.thinkinginbinary@spamgourmet.org
|
||||
# License: GNU General Public License, version 2.0
|
||||
#
|
||||
# v. 0.0: Too ugly to publish, thrown out.
|
||||
#
|
||||
# v. 0.1: First rewrite.
|
||||
# Added master host list so offline hosts can still be displayed.
|
||||
# Fixed modification detection (a newer modification time is lower.)
|
||||
#
|
||||
# v. 0.2: Fixed Client ID = "*" => "None"
|
||||
# Fixed HTML entities (a client ID of ????<? screwed it up)
|
||||
# Fixed command-line argument processing (apparently, "shift @ARGV" !=
|
||||
# "$_ = shift @ARGV"...)
|
||||
# Added license information.
|
||||
|
||||
use Template;
|
||||
|
||||
# Location of state file. (This is the dnsmasq default.)
|
||||
# Change with -s <file>
|
||||
my $dnsmasq_state_file = '/var/lib/misc/dnsmasq.leases';
|
||||
# Location of template. (Assumed to be in current directory.)
|
||||
# Change with -t <file>
|
||||
my $html_template_file = 'dnslist.tt2';
|
||||
# File to write HTML page to. (This is where Slackware puts WWW pages. It may
|
||||
# be different on other systems. Make sure the permissions are set correctly
|
||||
# for it.)
|
||||
my $html_output_file = '/var/www/htdocs/dhcp.html';
|
||||
# Time to wait after each page update. (The state file is checked for changes
|
||||
# before each update but is not read in each time, in case it is very big. The
|
||||
# page is rewritten just so the "(updated __/__ __:__:__)" text changes ;-)
|
||||
my $wait_time = 2;
|
||||
|
||||
# Read command-line arguments.
|
||||
while ($_ = shift @ARGV) {
|
||||
if (/-s/) { $dnsmasq_state_file = shift; next; }
|
||||
if (/-t/) { $html_template_file = shift; next; }
|
||||
if (/-o/) { $html_output_file = shift; next; }
|
||||
if (/-d/) { $wait_time = shift; next; }
|
||||
if (/-l/) { show_license(); exit; }
|
||||
die "usage: dnslist [-s state_file] [-t template_file] [-o output_file] [-d delay_time]\n";
|
||||
}
|
||||
|
||||
# Master list of clients, offline and online.
|
||||
my $list = {};
|
||||
# Sorted host list. (It's actually sorted by IP--the sub &byip() compares two
|
||||
# IP addresses, octet by octet, and figures out which is higher.)
|
||||
my @hosts = ();
|
||||
# Last time the state file was changed.
|
||||
my $last_state_change;
|
||||
|
||||
# Check for a change to the state file.
|
||||
sub check_state {
|
||||
if (defined $last_state_change) {
|
||||
if (-M $dnsmasq_state_file < $last_state_change) {
|
||||
print "check_state: state file has been changed.\n";
|
||||
$last_state_change = -M $dnsmasq_state_file;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
# Last change undefined, so we are running for the first time.
|
||||
print "check_state: reading state file at startup.\n";
|
||||
read_state();
|
||||
$last_state_change = -M $dnsmasq_state_file;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Read data in state file.
|
||||
sub read_state {
|
||||
my $old;
|
||||
my $new;
|
||||
# Open file.
|
||||
unless (open STATE, $dnsmasq_state_file) {
|
||||
warn "read_state: can't open $dnsmasq_state_file!\n";
|
||||
return 0;
|
||||
}
|
||||
# Mark all hosts as offline, saving old state.
|
||||
foreach $ether (keys %{$list}) {
|
||||
$list->{$ether}->{'old_online'} = $list->{$ether}->{'online'};
|
||||
$list->{$ether}->{'online'} = 0;
|
||||
}
|
||||
# Read hosts.
|
||||
while (<STATE>) {
|
||||
chomp;
|
||||
@host{qw/raw_lease ether_addr ip_addr hostname raw_client_id/} = split /\s+/;
|
||||
$ether = $host{ether_addr};
|
||||
# Mark each online host as online.
|
||||
$list->{$ether}->{'online'} = 1;
|
||||
# Copy data to master list.
|
||||
foreach $key (keys %host) {
|
||||
$list->{$ether}->{$key} = $host{$key};
|
||||
}
|
||||
}
|
||||
close STATE;
|
||||
# Handle changes in offline/online state. (The sub &do_host() handles
|
||||
# all of the extra stuff to do with a host's data once it is read.
|
||||
foreach $ether (keys %{$list}) {
|
||||
$old = $list->{$ether}->{'old_online'};
|
||||
$new = $list->{$ether}->{'online'};
|
||||
if (not $old) {
|
||||
if (not $new) {
|
||||
do_host($ether, 'offline');
|
||||
} else {
|
||||
do_host($ether, 'join');
|
||||
}
|
||||
} else {
|
||||
if (not $new) {
|
||||
do_host($ether, 'leave');
|
||||
} else {
|
||||
do_host($ether, 'online');
|
||||
}
|
||||
}
|
||||
}
|
||||
# Sort hosts by IP ;-)
|
||||
@hosts = sort byip values %{$list};
|
||||
# Copy sorted list to template data store.
|
||||
$data->{'hosts'} = [ @hosts ];
|
||||
}
|
||||
|
||||
# Do stuff per host.
|
||||
sub do_host {
|
||||
my ($ether, $status) = @_;
|
||||
|
||||
# Find textual representation of DHCP client ID.
|
||||
if ($list->{$ether}->{'raw_client_id'} eq '*') {
|
||||
$list->{$ether}->{'text_client_id'} = 'None';
|
||||
} else {
|
||||
my $text = "";
|
||||
foreach $char (split /:/, $list->{$ether}->{'raw_client_id'}) {
|
||||
$char = pack('H2', $char);
|
||||
if (ord($char) >= 32 and ord($char) <= 127) {
|
||||
$text .= $char;
|
||||
} else {
|
||||
$text .= "?";
|
||||
}
|
||||
}
|
||||
$list->{$ether}->{'text_client_id'} = $text;
|
||||
}
|
||||
|
||||
# Convert lease expiration date/time to text.
|
||||
if ($list->{$ether}->{'raw_lease'} == 0) {
|
||||
$list->{$ether}->{'text_lease'} = 'Never';
|
||||
} else {
|
||||
$list->{$ether}->{'text_lease'} = nice_time($list->{$ether}->{'raw_lease'});
|
||||
}
|
||||
|
||||
if ($status eq 'offline') {
|
||||
# Nothing to do.
|
||||
} elsif ($status eq 'online') {
|
||||
# Nothing to do.
|
||||
} elsif ($status eq 'join') {
|
||||
# Update times for joining host.
|
||||
print "do_host: $ether joined the network.\n";
|
||||
$list->{$ether}->{'join_time'} = time;
|
||||
$list->{$ether}->{'since'} = nice_time(time);
|
||||
} elsif ($status eq 'leave') {
|
||||
# Update times for leaving host.
|
||||
print "do_host: $ether left the network.\n";
|
||||
$list->{$ether}->{'leave_time'} = time;
|
||||
$list->{$ether}->{'since'} = nice_time(time);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Convert time to a string representation.
|
||||
sub nice_time {
|
||||
my $time = shift;
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $dst) = localtime($time);
|
||||
$sec = pad($sec, '0', 2);
|
||||
$min = pad($min, '0', 2);
|
||||
$hour = pad($hour, '0', 2);
|
||||
$mon = pad($mon, '0', 2);
|
||||
$mday = pad($mday, '0', 2);
|
||||
return "$mon/$mday $hour:$min:$sec";
|
||||
}
|
||||
|
||||
# Pad string to a certain length by repeatedly prepending another string.
|
||||
sub pad {
|
||||
my ($text, $pad, $length) = @_;
|
||||
while (length($text) < $length) {
|
||||
$text = "$pad$text";
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
# Compare two IP addresses. (Uses $a and $b from sort.)
|
||||
sub byip {
|
||||
# Split into octets.
|
||||
my @a = split /\./, $a->{ip_addr};
|
||||
my @b = split /\./, $b->{ip_addr};
|
||||
# Compare octets.
|
||||
foreach $n (0..3) {
|
||||
return $a[$n] <=> $b[$n] if ($a[$n] != $b[$n]);
|
||||
}
|
||||
# If we get here there is no difference.
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Output HTML file.
|
||||
sub write_output {
|
||||
# Create new template object.
|
||||
my $template = Template->new(
|
||||
{
|
||||
ABSOLUTE => 1, # /var/www/... is an absolute path
|
||||
OUTPUT => $html_output_file # put it here, not STDOUT
|
||||
}
|
||||
);
|
||||
$data->{'updated'} = nice_time(time); # add "(updated ...)" to file
|
||||
unless ($template->process($html_template_file, $data)) { # do it
|
||||
warn "write_output: Template Toolkit error: " . $template->error() . "\n";
|
||||
return 0;
|
||||
}
|
||||
print "write_output: page updated.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub show_license {
|
||||
while (<DATA>) {
|
||||
print;
|
||||
$line++;
|
||||
if ($line == 24) { <>; $line = 1; }
|
||||
}
|
||||
}
|
||||
|
||||
# Main loop.
|
||||
while (1) {
|
||||
# Check for state change.
|
||||
if (check_state()) {
|
||||
read_state();
|
||||
sleep 1; # Sleep for a second just so we don't wear anything
|
||||
# out. (By not sleeping the whole time after a change
|
||||
# we can detect rapid changes more easily--like if 300
|
||||
# hosts all come back online, they show up quicker.)
|
||||
} else {
|
||||
sleep $wait_time; # Take a nap.
|
||||
}
|
||||
write_output(); # Write the file anyway.
|
||||
}
|
||||
__DATA__
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
32
contrib/dnslist/dnslist.tt2
Normal file
32
contrib/dnslist/dnslist.tt2
Normal file
@@ -0,0 +1,32 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>DHCP Clients</title>
|
||||
<link rel="stylesheet" href="dhcp.css"/>
|
||||
<meta http-equiv="Refresh" content="2"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DHCP Clients <span class="updated">(updated [% updated %])</span></h1>
|
||||
<table cols="7">
|
||||
<tr>
|
||||
<th class="hostname">Hostname</th>
|
||||
<th class="ip_addr">IP Address</th>
|
||||
<th class="ether_addr">Ethernet Address</th>
|
||||
<th class="client_id">DHCP Client ID</th>
|
||||
<th class="status">Status</th>
|
||||
<th class="since">Since</th>
|
||||
<th class="lease">Lease Expires</th>
|
||||
</tr>
|
||||
[% FOREACH host IN hosts %]
|
||||
<tr class="[% IF host.online %]online[% ELSE %]offline[% END %]">
|
||||
<td class="hostname">[% host.hostname %]</td>
|
||||
<td class="ip_addr">[% host.ip_addr %]</td>
|
||||
<td class="ether_addr">[% host.ether_addr %]</td>
|
||||
<td class="client_id">[% host.text_client_id %] ([% host.raw_client_id %])</td>
|
||||
<td class="status">[% IF host.online %]Online[% ELSE %]Offline[% END %]</td>
|
||||
<td class="since">[% host.since %]</td>
|
||||
<td class="lease">[% host.text_lease %]</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
130
dnsmasq-mdk.spec
130
dnsmasq-mdk.spec
@@ -1,130 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# General mumbojumbo
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.4
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
Vendor: Simon Kelley
|
||||
Packager: Simon Kelley
|
||||
Distribution: Mandrake Linux
|
||||
URL: http://www.thekelleys.org.uk/dnsmasq
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Requires: chkconfig
|
||||
BuildRoot: /var/tmp/%{name}-%{version}
|
||||
Summary: A lightweight caching nameserver
|
||||
|
||||
%description
|
||||
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
|
||||
is designed to provide DNS and, optionally, DHCP, to a small network. It can
|
||||
serve the names of local machines which are not in the global DNS. The DHCP
|
||||
server integrates with the DNS server and allows machines with DHCP-allocated
|
||||
addresses to appear in the DNS with names configured either in each host or
|
||||
in a central configuration file. Dnsmasq supports static and dynamic DHCP
|
||||
leases and BOOTP for network booting of diskless machines.
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Build
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%build
|
||||
make
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Install
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/sbin
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/etc/rc.d/init.d
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
|
||||
cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
|
||||
strip src/dnsmasq
|
||||
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
|
||||
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
|
||||
###############################################################################
|
||||
#
|
||||
# Clean up
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-install scriptlet
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%post
|
||||
/sbin/chkconfig --add dnsmasq
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Pre-uninstall scriptlet
|
||||
#
|
||||
# If there's a time when your package needs to have one last look around before
|
||||
# the user erases it, the place to do it is in the %preun script. Anything that
|
||||
# a package needs to do immediately prior to RPM taking any action to erase the
|
||||
# package, can be done here.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%preun
|
||||
if [ $1 = 0 ]; then # execute this only if we are NOT doing an upgrade
|
||||
service dnsmasq stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del dnsmasq
|
||||
fi
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-uninstall scriptlet
|
||||
#
|
||||
# The %postun script executes after the package has been removed. It is the
|
||||
# last chance for a package to clean up after itself.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%postun
|
||||
if [ "$1" -ge "1" ]; then
|
||||
service dnsmasq restart >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# File list
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0
|
||||
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
|
||||
%attr(0664,root,root) /etc/dnsmasq.conf
|
||||
%config /etc/rc.d/init.d/dnsmasq
|
||||
%config /etc/dnsmasq.conf
|
||||
%attr(0755,root,root) /usr/sbin/dnsmasq
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.bz2
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.4
|
||||
Version: 2.11
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
@@ -58,7 +58,6 @@ cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
|
||||
strip src/dnsmasq
|
||||
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
|
||||
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
gzip $RPM_BUILD_ROOT/usr/share/man/man8/dnsmasq.8
|
||||
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
|
||||
|
||||
###############################################################################
|
||||
@@ -128,6 +127,6 @@ fi
|
||||
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
|
||||
%attr(0664,root,root) /etc/dnsmasq.conf
|
||||
%attr(0755,root,root) /usr/sbin/dnsmasq
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.gz
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq*
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.4
|
||||
Version: 2.11
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Productivity/Networking/DNS/Servers
|
||||
@@ -16,7 +16,7 @@ Provides: dns_daemon
|
||||
Conflicts: bind bind8 bind9
|
||||
PreReq: %fillup_prereq %insserv_prereq
|
||||
Autoreqprov: on
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
BuildRoot: /var/tmp/%{name}-%{version}
|
||||
Summary: A lightweight caching nameserver
|
||||
|
||||
@@ -39,6 +39,8 @@ leases and BOOTP for network booting of diskless machines.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
patch -p0 <rpm/%{name}-SuSE.patch
|
||||
|
||||
%build
|
||||
%{?suse_update_config:%{suse_update_config -f}}
|
||||
make
|
||||
|
||||
162
dnsmasq.8
162
dnsmasq.8
@@ -23,7 +23,10 @@ is lightweight and easy to configure. It is intended as be run on
|
||||
small router/firewalls and provide a DNS (and optionally, DHCP) service to a LAN.
|
||||
.SH OPTIONS
|
||||
Note that in general missing parameters are allowed and switch off
|
||||
functions, for instance "--pid-file=" disables writing a PID file.
|
||||
functions, for instance "--pid-file=" disables writing a PID file. On
|
||||
BSD, unless the GNU getopt library is linked, the long form of the
|
||||
options does not work on the command line; it is still recognised in
|
||||
the configuration file.
|
||||
.TP
|
||||
.B \-h, --no-hosts
|
||||
Don't read the hostnames in /etc/hosts.
|
||||
@@ -70,36 +73,62 @@ Print the version number.
|
||||
Listen on <port> instead of the standard DNS port (53). Useful mainly for
|
||||
debugging.
|
||||
.TP
|
||||
.B \-P, --edns-packet-max=<size>
|
||||
Specify the largest EDNS.0 UDP packet which is supported by the DNS
|
||||
forwarder. Defaults to 1280, which is the RFC2671-recommended maximum
|
||||
for ethernet.
|
||||
.TP
|
||||
.B \-Q, --query-port=<query_port>
|
||||
Send outbound DNS queries from, and listen for their replies on, the specific UDP port <query_port> instead of using one chosen at runtime. Useful to simplify your
|
||||
firewall rules; without this, your firewall would have to allow connections from outside DNS servers to a range of UDP ports, or dynamically adapt to the
|
||||
port being used by the current dnsmasq instance.
|
||||
.TP
|
||||
.B \-i, --interface=<interface name>
|
||||
Listen only on the specified interface. More than one interface may be specified. Dnsmasq always listens on the loopback (local) interface. If no
|
||||
.B \-i
|
||||
flags are given, dnsmasq listens on all available interfaces unless overridden by
|
||||
.B \-a
|
||||
Listen only on the specified interface(s). Dnsmasq automatically adds
|
||||
the loopback (local) interface to the list of interfaces to use when
|
||||
the
|
||||
.B \--interface
|
||||
option is used. If no
|
||||
.B \--interface
|
||||
or
|
||||
.B \-I
|
||||
flags.
|
||||
.B \--listen-address
|
||||
options are given dnsmasq listens on all available interfaces except any
|
||||
given in
|
||||
.B \--except-interface
|
||||
options. If IP alias interfaces (eg "eth1:0") are used with
|
||||
.B --interface
|
||||
or
|
||||
.B --except-interface
|
||||
options, then the
|
||||
.B --bind-interfaces
|
||||
option will be automatically set. This is required for deeply boring
|
||||
sockets-API reasons.
|
||||
.TP
|
||||
.B \-I, --except-interface=<interface name>
|
||||
Do not listen on the specified interface.
|
||||
Do not listen on the specified interface. Note that the order of
|
||||
.B \--listen-address
|
||||
.B --interface
|
||||
and
|
||||
.B --except-interface
|
||||
options does not matter and that
|
||||
.B --except-interface
|
||||
options always override the others.
|
||||
.TP
|
||||
.B \-a, --listen-address=<ipaddr>
|
||||
Listen only on the given IP address. As with
|
||||
.B \-i
|
||||
more than one address may be specified. Unlike
|
||||
.B \-i
|
||||
the loopback interface is not special: if dnsmasq is to listen on the loopback interface,
|
||||
it's IP, 127.0.0.1, must be explicitly given. If no
|
||||
.B \-a
|
||||
flags are given, dnsmasq listens on all available interfaces unless overridden by
|
||||
.B \-i
|
||||
or
|
||||
.B \-I
|
||||
flags.
|
||||
Listen on the given IP address(es). Both
|
||||
.B \--interface
|
||||
and
|
||||
.B \--listen-address
|
||||
options may be given, in which case the set of both interfaces and
|
||||
addresses is used. Note that if no
|
||||
.B \--interface
|
||||
option is given, but
|
||||
.B \--listen-address
|
||||
is, dnsmasq will not automatically listen on the loopback
|
||||
interface. To achieve this, its IP address, 127.0.0.1, must be
|
||||
explicitly given as a
|
||||
.B \--listen-address
|
||||
option.
|
||||
.TP
|
||||
.B \-z, --bind-interfaces
|
||||
On systems which support it, dnsmasq binds the wildcard address,
|
||||
@@ -108,11 +137,16 @@ 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.
|
||||
running another nameserver on the same machine or 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.
|
||||
.TP
|
||||
.B \-b, --bogus-priv
|
||||
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
|
||||
which are not found in /etc/hosts or the DHCP leases file are resolved to the IP address in dotted-quad form.
|
||||
which are not found in /etc/hosts or the DHCP leases file are answered
|
||||
with "no such domain" rather than being forwarded upstream.
|
||||
.TP
|
||||
.B \-V, --alias=<old-ip>,<new-ip>[,<mask>]
|
||||
Modify IPv4 addresses returned from upstream nameservers; old-ip is
|
||||
@@ -211,10 +245,17 @@ with the specified IP address which may be IPv4 or IPv6. To give
|
||||
both IPv4 and IPv6 addresses for a domain, use repeated -A flags.
|
||||
Note that /etc/hosts and DHCP leases override this for individual
|
||||
names. A common use of this is to redirect the entire doubleclick.net
|
||||
domain to some friendly local web server to avoid banner ads.
|
||||
domain to some friendly local web server to avoid banner ads. The
|
||||
domain specification works in the same was as for --server, with the
|
||||
additional facility that /#/ matches any domain. Thus
|
||||
--address=/#/1.2.3.4 will always return 1.2.3.4 for any query not
|
||||
answered from /etc/hosts or DHCP and not sent to an upstream
|
||||
nameserver by a more specific --server directive.
|
||||
.TP
|
||||
.B \-m, --mx-host=<mx name>
|
||||
Return an MX record named <mx name> pointing to the host specified in the --mx-target switch
|
||||
.B \-m, --mx-host=<mx name>[,<hostname>]
|
||||
Return an MX record named <mx name> pointing to the given hostname (if
|
||||
given), or
|
||||
the host specified in the --mx-target switch
|
||||
or, if that switch is not given, the host on which dnsmasq
|
||||
is running. This is useful for directing mail from systems on a LAN
|
||||
to a central server.
|
||||
@@ -262,9 +303,16 @@ given using the
|
||||
.B interface
|
||||
option. This limitation currently affects OpenBSD. The optional
|
||||
network-id is a alphanumeric label which marks this network so that
|
||||
dhcp options may be specified on a per-network basis.
|
||||
dhcp options may be specified on a per-network basis. The end address
|
||||
may be replaced by the keyword
|
||||
.B static
|
||||
which tells dnsmasq to enable DHCP for the network specified, but not
|
||||
to dynamically allocate IP addresses. Only hosts which have static
|
||||
addresses given via
|
||||
.B dhcp-host
|
||||
or from /etc/ethers will be served.
|
||||
.TP
|
||||
.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][,<ipaddr>][,<hostname>][,<lease_time>]
|
||||
.B \-G, --dhcp-host=[[<hwaddr>]|[id:[<client_id>][*]]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
|
||||
Specify per host parameters for the DHCP server. This allows a machine
|
||||
with a particular hardware address to be always allocated the same
|
||||
hostname, IP address and lease time. A hostname specified like this
|
||||
@@ -286,11 +334,23 @@ hardware addresses to identify hosts by prefixing with 'id:'. Thus:
|
||||
.B --dhcp-host=id:01:02:03:04,.....
|
||||
refers to the host with client identifier 01:02:03:04. It is also
|
||||
allowed to specify the client ID as text, like this:
|
||||
.B --dhcp-host=id:clientidastext,.....
|
||||
.B --dhcp-host=id:clientidastext,.....
|
||||
The special option id:* means "ignore any client-id
|
||||
and use MAC addresses only." This is useful when a client presents a client-id sometimes
|
||||
but not others.
|
||||
If a name appears in /etc/hosts, the associated address can be
|
||||
allocated to a DHCP lease, but only if a
|
||||
.B --dhcp-host
|
||||
option specifying the name also exists.
|
||||
option specifying the name also exists. The special keyword "ignore"
|
||||
tells dnsmasq to never offer a DHCP lease to a machine. The machine
|
||||
can be specified by hardware address, client ID or hostname, for
|
||||
instance
|
||||
.B --dhcp-host=00:20:e0:3b:13:af,ignore
|
||||
This is
|
||||
useful when there is another DHCP server on the network which should
|
||||
be used by some machines. The net:<network-id> parameter enables DHCP options just
|
||||
for this host in the same way as the the network-id in
|
||||
.B dhcp-range.
|
||||
.TP
|
||||
.B \-Z, --read-ethers
|
||||
Read /etc/ethers for information about hosts for the DHCP server. The
|
||||
@@ -312,9 +372,11 @@ specfied in RFC2132. For example, to set the default route option to
|
||||
192.168.4.4, do
|
||||
.B --dhcp-option=3,192.168.4.4
|
||||
and to set the time-server address to 192.168.0.4, do
|
||||
.B dhcp-option=42,192.168.0.4
|
||||
.B --dhcp-option=42,192.168.0.4
|
||||
The special address 0.0.0.0 is taken to mean "the address of the
|
||||
machine running dnsmasq". If the optional network-id is given then
|
||||
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.
|
||||
Be careful: no checking is done that the correct type of data for the
|
||||
@@ -323,6 +385,27 @@ possible to generate the correct data type; it is quite possible to
|
||||
persuade dnsmasq to generate illegal DHCP packets with injudicious use
|
||||
of this flag.
|
||||
.TP
|
||||
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
|
||||
Map from a vendor-class string to a network id. Most DHCP clients provide a
|
||||
"vendor class" which represents, in some sense, the type of host. This option
|
||||
maps vendor classes to network ids, so that DHCP options may be selectively delivered
|
||||
to different classes of hosts. For example
|
||||
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
|
||||
will allow options to be set only for HP printers like so:
|
||||
.B --dhcp-option=printers,3,192.168.4.4
|
||||
The vendor-class string is
|
||||
substring matched against the vendor-class supplied by the client, to
|
||||
allow fuzzy matching.
|
||||
.TP
|
||||
.B \-j, --dhcp-userclass=<network-id>,<user-class>
|
||||
Map from a user-class string to a network id (with substring
|
||||
matching, like vendor classes). Most DHCP clients provide a
|
||||
"user class" which is configurable. This option
|
||||
maps user classes to network ids, so that DHCP options may be selectively delivered
|
||||
to different classes of hosts. It is possible, for instance to use
|
||||
this to set a different printer server for hosts in the class
|
||||
"accounts" than for hosts in the class "engineering".
|
||||
.TP
|
||||
.B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]]
|
||||
Set BOOTP options to be returned by the DHCP server. These are needed
|
||||
for machines which network boot, and tell the machine where to collect
|
||||
@@ -335,7 +418,12 @@ create thousands of leases and use lots of memory in the dnsmasq
|
||||
process.
|
||||
.TP
|
||||
.B \-l, --dhcp-leasefile=<path>
|
||||
Use the specified file to store DHCP lease information.
|
||||
Use the specified file to store DHCP lease information. If this option
|
||||
is given but no dhcp-range option is given then dnsmasq version 1
|
||||
behaviour is activated. The file given is assumed to be an ISC dhcpd
|
||||
lease file and parsed for leases which are then added to the DNS
|
||||
system if they have a hostname. This functionality may have been
|
||||
excluded from dnsmasq at compile time, in which case an error will occur.
|
||||
.TP
|
||||
.B \-s, --domain=<domain>
|
||||
Specifies the domain for the DHCP server. This has two effects;
|
||||
@@ -345,19 +433,23 @@ for DHCP-configured hosts to claim. The intention is to constrain hostnames so t
|
||||
.B --domain-suffix=thekelleys.org.uk
|
||||
and have a machine whose DHCP hostname is "laptop". The IP address for that machine is available from
|
||||
.B dnsmasq
|
||||
both as "laptop" and "laptop.thekelleys.org.uk".
|
||||
both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
|
||||
given as "#" then the domain is read from the first "search" directive
|
||||
in /etc/resolv.conf (or equivalent).
|
||||
.TP
|
||||
.B \-E, --expand-hosts
|
||||
Add the domain-suffix to simple names (without a period) in /etc/hosts
|
||||
in the same way as for DHCP-derived names.
|
||||
.SH CONFIG FILE
|
||||
At startup, dnsmasq reads /etc/dnsmasq.conf, if it exists. (On
|
||||
FreeBSD, the file is /usr/local/etc/dnsmasq.conf) The format of this
|
||||
FreeBSD and OpenBSD, the file is /usr/local/etc/dnsmasq.conf) The format of this
|
||||
file consists of one option per line, exactly as the long options detailed
|
||||
in the OPTIONS section but without the leading "--". Lines starting with # are comments and ignored. For
|
||||
options which may only be specified once, the configuration file overrides
|
||||
the command line. Use the --conf-file option to specify a different
|
||||
configuration file.
|
||||
configuration file. The conf-file option is also allowed in
|
||||
configuration files, to include multiple configuration files. Only one
|
||||
level of nesting is allowed.
|
||||
.SH NOTES
|
||||
When it receives a SIGHUP,
|
||||
.B dnsmasq
|
||||
|
||||
@@ -157,6 +157,28 @@ filterwin2k
|
||||
# it asks for a DHCP lease.
|
||||
#dhcp-host=judge
|
||||
|
||||
# Never offer DHCP service to a machine whose ethernet
|
||||
# address is 11:22:33:44:55:66
|
||||
#dhcp-host=11:22:33:44:55:66,ignore
|
||||
|
||||
# Ignore any client-id presented by the machine with ethernet
|
||||
# address 11:22:33:44:55:66. This is useful to prevent a machine
|
||||
# being treated differently when running under different OS's or
|
||||
# between PXE boot and OS boot.
|
||||
#dhcp-host=11:22:33:44:55:66,id:*
|
||||
|
||||
# Send extra options which are tagged as "red" to
|
||||
# the machine with ethernet address 11:22:33:44:55:66
|
||||
#dhcp-host=11:22:33:44:55:66,net:red
|
||||
|
||||
# Send extra options which are tagged as "red" to any machine whose
|
||||
# DHCP vendorclass string includes the substring "Linux"
|
||||
#dhcp-vendorclass=red,Linux
|
||||
|
||||
# Send extra options which are tagged as "red" to any machine one
|
||||
# of whose DHCP userclass strings includes the substring "accounts"
|
||||
#dhcp-userclass=red,accounts
|
||||
|
||||
# If this line is uncommented, dnsmasq will read /etc/ethers and act
|
||||
# on the ethernet-address/IP pairs found there just as if they had
|
||||
# been given as --dhcp-host options. Useful if you keep
|
||||
@@ -193,6 +215,10 @@ filterwin2k
|
||||
# Set the "all subnets are local" flag
|
||||
#dhcp-option=27,1
|
||||
|
||||
# Send the etherboot magic flag and then etherboot options (a string).
|
||||
#dhcp-option=128,e4:45:74:68:00:00
|
||||
#dhcp-option=129,NIC=eepro100
|
||||
|
||||
# Specify an option which will only be sent to the "red" network
|
||||
# (see dhcp-range for the declaration of the "red" network)
|
||||
#dhcp-option=red,42,192.168.1.1
|
||||
@@ -250,13 +276,13 @@ filterwin2k
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
# For debugging purposes, log each DNS query as it passes through
|
||||
# dnsmasq.
|
||||
#log-queries
|
||||
|
||||
|
||||
# Include a another lot of configuration options.
|
||||
#conf-file=/etc/dnsmasq.more.conf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
13
doc.html
13
doc.html
@@ -18,8 +18,11 @@ connected to the internet via a modem, cable-modem or ADSL
|
||||
connection but would be a good choice for any small network where low
|
||||
resource use and ease of configuration are important.
|
||||
<P>
|
||||
Dnsmasq is included in at least the following Linux distributions: Gentoo, Debian,
|
||||
Smoothwall, IP-Cop, floppyfw, Firebox, Freesco and
|
||||
Supported platforms include Linux (with glibc and uclibc), *BSD and
|
||||
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.
|
||||
<P>
|
||||
Dnsmasq provides the following features:
|
||||
@@ -86,7 +89,7 @@ in the .com and .net TLDs
|
||||
|
||||
<H2>Download.</H2>
|
||||
|
||||
Download dnsmasq <A HREF="http://www.thekelleys.org.uk/dnsmasq/"> here</A>.
|
||||
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
|
||||
The tarball includes this documentation, source, manpage and control files for building .rpms.
|
||||
There are also pre-built i386 .rpms, and a
|
||||
<A HREF="CHANGELOG"> CHANGELOG</A>.
|
||||
@@ -109,7 +112,9 @@ bzip2 dnsmasq-zzz.tar
|
||||
</PRE>
|
||||
|
||||
<H2>Links.</H2>
|
||||
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>
|
||||
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>
|
||||
|
||||
<H2>License.</H2>
|
||||
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
|
||||
|
||||
42
rpm/dnsmasq-SuSE.patch
Normal file
42
rpm/dnsmasq-SuSE.patch
Normal file
@@ -0,0 +1,42 @@
|
||||
--- dnsmasq.8 2004-06-21 21:55:47.000000000 +0200
|
||||
+++ dnsmasq.8 2004-06-22 23:30:18.000000000 +0200
|
||||
@@ -63,7 +63,7 @@
|
||||
.TP
|
||||
.B \-g, --group=<groupname>
|
||||
Specify the group which dnsmasq will run
|
||||
-as. The defaults to "dip", if available, to facilitate access to
|
||||
+as. The defaults to "dialout", if available, to facilitate access to
|
||||
/etc/ppp/resolv.conf which is not normally world readable.
|
||||
.TP
|
||||
.B \-v, --version
|
||||
--- dnsmasq.conf.example 2004-05-26 12:59:56.000000000 +0200
|
||||
+++ dnsmasq.conf.example 2004-06-22 23:32:36.000000000 +0200
|
||||
@@ -62,7 +62,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-06-22 21:14:50.000000000 +0200
|
||||
+++ src/config.h 2004-06-22 23:31:46.000000000 +0200
|
||||
@@ -38,7 +38,7 @@
|
||||
#endif
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
#define CHUSER "nobody"
|
||||
-#define CHGRP "dip"
|
||||
+#define CHGRP "dialout"
|
||||
#define IP6INTERFACES "/proc/net/if_inet6"
|
||||
#define UPTIME "/proc/uptime"
|
||||
#define DHCP_SERVER_PORT 67
|
||||
@@ -171,7 +171,7 @@
|
||||
|
||||
/* platform independent options. */
|
||||
#undef HAVE_BROKEN_RTC
|
||||
-#define HAVE_ISC_READER
|
||||
+#undef HAVE_ISC_READER
|
||||
|
||||
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
|
||||
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
CFLAGS?= -O2
|
||||
|
||||
OBJS = cache.o rfc1035.o util.o option.o forward.o \
|
||||
network.o dnsmasq.o dhcp.o lease.o rfc2131.o
|
||||
OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o \
|
||||
network.o dnsmasq.o dhcp.o lease.o rfc2131.o
|
||||
|
||||
.c.o: dnsmasq.h config.h
|
||||
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c
|
||||
|
||||
21
src/cache.c
21
src/cache.c
@@ -472,19 +472,21 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
|
||||
|
||||
/* Remove duplicates in hosts files. */
|
||||
if (lookup && (lookup->flags & F_HOSTS) &&
|
||||
if (lookup && (lookup->flags & F_HOSTS) &&
|
||||
memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
free(cache);
|
||||
else
|
||||
{
|
||||
/* Ensure there is only one address -> name mapping (first one trumps) */
|
||||
if (cache_find_by_addr(NULL, addr, 0, flags & (F_IPV4 | F_IPV6)))
|
||||
flags &= ~F_REVERSE;
|
||||
cache->flags = flags;
|
||||
memcpy(&cache->addr, addr, addrlen);
|
||||
cache_hash(cache);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, unsigned short addn_flag)
|
||||
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int is_addn)
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
char *line;
|
||||
@@ -529,6 +531,9 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
|
||||
else
|
||||
continue;
|
||||
|
||||
if (is_addn)
|
||||
flags |= F_ADDN;
|
||||
|
||||
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
|
||||
{
|
||||
struct crec *cache;
|
||||
@@ -543,16 +548,12 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
|
||||
strcpy(cache->name.sname, token);
|
||||
strcat(cache->name.sname, ".");
|
||||
strcat(cache->name.sname, domain_suffix);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
|
||||
/* Only first name is cannonical and used for reverse lookups */
|
||||
flags &= ~F_REVERSE;
|
||||
add_hosts_entry(cache, &addr, addrlen, flags);
|
||||
}
|
||||
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
|
||||
{
|
||||
strcpy(cache->name.sname, token);
|
||||
add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
|
||||
/* Clear this here in case not done above. */
|
||||
flags &= ~F_REVERSE;
|
||||
add_hosts_entry(cache, &addr, addrlen, flags);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -604,7 +605,7 @@ void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
|
||||
read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
|
||||
if (addn_hosts)
|
||||
{
|
||||
read_hostsfile(addn_hosts, opts, buff, domain_suffix, F_ADDN);
|
||||
read_hostsfile(addn_hosts, opts, buff, domain_suffix, 1);
|
||||
addn_file = addn_hosts;
|
||||
}
|
||||
}
|
||||
|
||||
43
src/config.h
43
src/config.h
@@ -12,12 +12,16 @@
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#define VERSION "2.4"
|
||||
#define VERSION "2.11"
|
||||
|
||||
#define FTABSIZ 150 /* max number of outstanding requests */
|
||||
#define TIMEOUT 20 /* drop queries after TIMEOUT seconds */
|
||||
#define MAX_PROCS 20 /* max no children for TCP requests */
|
||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||
#define EDNS_PKTSZ 1280 /* default max EDNS.0 UDP packet from RFC2671 */
|
||||
#define TIMEOUT 20 /* drop UDP queries after TIMEOUT seconds */
|
||||
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
|
||||
#define CACHESIZ 150 /* default cache size */
|
||||
#define MAXTOK 50 /* token in DHCP leases */
|
||||
#define MAXLEASES 150 /* maximum number of DHCP leases */
|
||||
#define SMALLDNAME 40 /* most domain names are smaller than this */
|
||||
#define HOSTSFILE "/etc/hosts"
|
||||
@@ -30,9 +34,12 @@
|
||||
#define RUNFILE "/var/run/dnsmasq.pid"
|
||||
#if defined(__FreeBSD__) || defined (__OpenBSD__)
|
||||
# define LEASEFILE "/var/db/dnsmasq.leases"
|
||||
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
|
||||
#else
|
||||
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
|
||||
#endif
|
||||
#if defined(__FreeBSD__)
|
||||
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
|
||||
#else
|
||||
# define CONFFILE "/etc/dnsmasq.conf"
|
||||
#endif
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
@@ -58,22 +65,11 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* determine if we can find the destination address of recieved packets
|
||||
and set the source address of sent ones. If so, we can use one socket
|
||||
bound to INADDR_ANY and cope with dynamically created interfaces.
|
||||
Linux does this differently to FreeBSD. */
|
||||
|
||||
#if defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
|
||||
# define HAVE_UDP_SRC_DST
|
||||
#else
|
||||
# undef HAVE_UDP_SRC_DST
|
||||
#endif
|
||||
|
||||
/* Decide if we're going to support IPv6 */
|
||||
/* We assume that systems which don't have IPv6
|
||||
headers don't have ntop and pton either */
|
||||
|
||||
#if defined(INET6_ADDRSTRLEN) && !defined(NO_IPV6)
|
||||
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
|
||||
# define HAVE_IPV6
|
||||
# define ADDRSTRLEN INET6_ADDRSTRLEN
|
||||
# if defined(SOL_IPV6)
|
||||
@@ -99,7 +95,6 @@
|
||||
new system, you may want to edit these.
|
||||
May replace this with Autoconf one day.
|
||||
|
||||
|
||||
HAVE_LINUX_IPV6_PROC
|
||||
define this to do IPv6 interface discovery using
|
||||
proc/net/if_inet6 ala LINUX.
|
||||
@@ -121,6 +116,10 @@ HAVE_BROKEN_RTC
|
||||
work on other systems by teaching dnsmasq_time() in utils.c how to
|
||||
read the system uptime.
|
||||
|
||||
HAVE_ISC_READER
|
||||
define this to include the old ISC dhcpcd integration. Note that you cannot
|
||||
set both HAVE_ISC_READER and HAVE_BROKEN_RTC.
|
||||
|
||||
HAVE_GETOPT_LONG
|
||||
define this if you have GNU libc or GNU getopt.
|
||||
|
||||
@@ -175,6 +174,16 @@ NOTES:
|
||||
|
||||
*/
|
||||
|
||||
/* platform independent options. */
|
||||
#undef HAVE_BROKEN_RTC
|
||||
#define HAVE_ISC_READER
|
||||
|
||||
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
|
||||
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
|
||||
#endif
|
||||
|
||||
/* platform dependent options. */
|
||||
|
||||
/* Must preceed __linux__ since uClinux defines __linux__ too. */
|
||||
#if defined(__uClinux__) || defined(__UCLIBC__)
|
||||
#undef HAVE_LINUX_IPV6_PROC
|
||||
@@ -255,6 +264,7 @@ 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_
|
||||
/* The two below are not defined in Mac OS X arpa/nameserv.h */
|
||||
@@ -287,3 +297,4 @@ typedef unsigned long in_addr_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
278
src/dhcp.c
278
src/dhcp.c
@@ -14,12 +14,13 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
void dhcp_init(int *fdp, int* rfdp)
|
||||
void dhcp_init(int *fdp, int* rfdp, struct dhcp_config *configs)
|
||||
{
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
struct sockaddr_in saddr;
|
||||
int opt = 1;
|
||||
|
||||
struct dhcp_config *cp;
|
||||
|
||||
if (fd == -1)
|
||||
die ("cannot create DHCP socket : %s", NULL);
|
||||
|
||||
@@ -57,10 +58,19 @@ void dhcp_init(int *fdp, int* rfdp)
|
||||
#endif
|
||||
|
||||
*rfdp = fd;
|
||||
|
||||
/* If the same IP appears in more than one host config, then DISCOVER
|
||||
for one of the hosts will get the address, but REQUEST will be NAKed,
|
||||
since the address is reserved by the other one -> protocol loop. */
|
||||
for (; configs; configs = configs->next)
|
||||
for (cp = configs->next; cp; cp = cp->next)
|
||||
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
|
||||
die("Duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
|
||||
}
|
||||
|
||||
void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
struct dhcp_vendor *vendors,
|
||||
time_t now, char *namebuff, char *domain_suffix,
|
||||
char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
|
||||
@@ -75,7 +85,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
struct iovec iov[2];
|
||||
struct cmsghdr *cmptr;
|
||||
int sz, newlen, iface_index = 0;
|
||||
struct in_addr source, real_netmask, iface_addr, netmask_save, broadcast_save;
|
||||
struct in_addr source, iface_netmask, iface_addr, iface_broadcast;
|
||||
struct in_addr netmask_save, broadcast_save, router;
|
||||
#ifdef HAVE_BPF
|
||||
unsigned char iface_hwaddr[ETHER_ADDR_LEN];
|
||||
#endif
|
||||
@@ -112,7 +123,8 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
|
||||
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
|
||||
|
||||
if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
|
||||
if (!(ifr.ifr_ifindex = iface_index) ||
|
||||
ioctl(dhcp_fd, SIOCGIFNAME, &ifr) == -1)
|
||||
return;
|
||||
|
||||
#elif defined(IP_RECVIF)
|
||||
@@ -126,13 +138,9 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
return;
|
||||
|
||||
#else
|
||||
if (!names || !names->name || names->next)
|
||||
{
|
||||
syslog(LOG_ERR, "must set exactly one interface on broken systems without IP_RECVIF");
|
||||
return;
|
||||
}
|
||||
else
|
||||
strcpy(ifr.ifr_name, names->name);
|
||||
while (names->isloop)
|
||||
names = names->next;
|
||||
strcpy(ifr.ifr_name, names->name);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BPF
|
||||
@@ -169,17 +177,30 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
/* If the packet came via a relay, use that address to look up the context,
|
||||
else use the address of the interface is arrived on. */
|
||||
source = mess->giaddr.s_addr ? mess->giaddr : iface_addr;
|
||||
|
||||
iface_netmask.s_addr = 0;
|
||||
iface_broadcast.s_addr = 0;
|
||||
|
||||
if (ioctl(dhcp_fd, SIOCGIFNETMASK, &ifr) != -1)
|
||||
{
|
||||
iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
/* we can use the interface netmask if either the packet came direct,
|
||||
or it came via a relay listening on the same network. This sounds unlikely,
|
||||
but it happens with win4lin. */
|
||||
if (!is_same_net(source, iface_addr, iface_netmask))
|
||||
iface_netmask.s_addr = 0;
|
||||
else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
|
||||
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
|
||||
}
|
||||
|
||||
for (context = contexts; context; context = context->next)
|
||||
{
|
||||
if (!context->netmask.s_addr && !mess->giaddr.s_addr && ioctl(dhcp_fd, SIOCGIFNETMASK, &ifr) != -1)
|
||||
real_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
else
|
||||
real_netmask = context->netmask;
|
||||
|
||||
if (real_netmask.s_addr &&
|
||||
(source.s_addr & real_netmask.s_addr) == (context->start.s_addr & real_netmask.s_addr) &&
|
||||
(source.s_addr & real_netmask.s_addr) == (context->end.s_addr & real_netmask.s_addr))
|
||||
struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask;
|
||||
|
||||
if (netmask.s_addr &&
|
||||
is_same_net(source, context->start, netmask) &&
|
||||
is_same_net(source, context->end, netmask))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -192,26 +213,34 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
netmask_save = context->netmask;
|
||||
broadcast_save = context->broadcast;
|
||||
|
||||
context->netmask = real_netmask;
|
||||
if (!context->netmask.s_addr)
|
||||
context->netmask = iface_netmask;
|
||||
|
||||
if (!context->broadcast.s_addr)
|
||||
{
|
||||
if (mess->giaddr.s_addr)
|
||||
context->broadcast.s_addr = (mess->giaddr.s_addr & real_netmask.s_addr) | ~real_netmask.s_addr;
|
||||
else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
|
||||
context->broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
if (iface_broadcast.s_addr)
|
||||
context->broadcast = iface_broadcast;
|
||||
else
|
||||
context->broadcast.s_addr = (iface_addr.s_addr & real_netmask.s_addr) | ~real_netmask.s_addr;
|
||||
context->broadcast.s_addr = (source.s_addr & context->netmask.s_addr) | ~context->netmask.s_addr;
|
||||
}
|
||||
|
||||
if (ioctl(dhcp_fd, SIOCGIFMTU, &ifr) == -1)
|
||||
ifr.ifr_mtu = ETHERMTU;
|
||||
|
||||
/* Normally, we set the default route to point to the machine which is getting the
|
||||
DHCP broadcast, either this machine or a relay. In the special case that the relay
|
||||
is on the same network as us, we set the default route to us, not the relay.
|
||||
This is the win4lin scenario again. */
|
||||
if (is_same_net(source, iface_addr, context->netmask))
|
||||
router = iface_addr;
|
||||
else
|
||||
router = source;
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu,
|
||||
rawpacket, sz, now, namebuff,
|
||||
dhcp_opts, dhcp_configs, domain_suffix, dhcp_file,
|
||||
dhcp_sname, dhcp_next_server);
|
||||
dhcp_opts, dhcp_configs, vendors, domain_suffix,
|
||||
dhcp_file, dhcp_sname, dhcp_next_server, router);
|
||||
lease_update_file(0, now);
|
||||
lease_update_dns();
|
||||
|
||||
@@ -327,7 +356,6 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
{
|
||||
/* Check is an address is OK for this network, ie
|
||||
@@ -339,47 +367,64 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
start = ntohl(context->start.s_addr);
|
||||
end = ntohl(context->end.s_addr);
|
||||
|
||||
/* static leases only. */
|
||||
if (start == end)
|
||||
return 0;
|
||||
|
||||
if (addr < start)
|
||||
return 0;
|
||||
|
||||
if (addr > end)
|
||||
return 0;
|
||||
|
||||
if (lease_find_by_addr(taddr))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
|
||||
{
|
||||
struct dhcp_config *config;
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
|
||||
return config;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
|
||||
struct in_addr *addrp)
|
||||
struct in_addr *addrp, unsigned char *hwaddr)
|
||||
{
|
||||
/* Find a free address: exlude anything in use and anything allocated to
|
||||
/* Find a free address: exclude anything in use and anything allocated to
|
||||
a particular hwaddr/clientid/hostname in our configuration */
|
||||
|
||||
struct dhcp_config *config;
|
||||
struct in_addr start = context->last;
|
||||
struct in_addr start, addr ;
|
||||
unsigned int i, j;
|
||||
|
||||
/* start == end means no dynamic leases. */
|
||||
if (context->end.s_addr == context->start.s_addr)
|
||||
return 0;
|
||||
|
||||
/* pick a seed based on hwaddr then iterate until we find a free address. */
|
||||
for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
|
||||
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
|
||||
|
||||
start.s_addr = addr.s_addr =
|
||||
htonl(ntohl(context->start.s_addr) +
|
||||
(j % (ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
|
||||
|
||||
do {
|
||||
if (context->last.s_addr == context->end.s_addr)
|
||||
context->last = context->start;
|
||||
if (addr.s_addr == context->end.s_addr)
|
||||
addr = context->start;
|
||||
else
|
||||
context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
|
||||
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
|
||||
|
||||
|
||||
if (!lease_find_by_addr(context->last))
|
||||
if (!lease_find_by_addr(addr) && !config_find_by_address(configs, addr))
|
||||
{
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->addr.s_addr == context->last.s_addr)
|
||||
break;
|
||||
|
||||
if (!config)
|
||||
{
|
||||
*addrp = context->last;
|
||||
return 1;
|
||||
}
|
||||
*addrp = addr;
|
||||
return 1;
|
||||
}
|
||||
} while (context->last.s_addr != start.s_addr);
|
||||
} while (addr.s_addr != start.s_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -388,9 +433,9 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
|
||||
{
|
||||
if (!context)
|
||||
return 1;
|
||||
if (config->addr.s_addr == 0)
|
||||
if (!(config->flags & CONFIG_ADDR))
|
||||
return 1;
|
||||
if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
|
||||
if (is_same_net(config->addr, context->start, context->netmask))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -405,28 +450,31 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
|
||||
if (clid_len)
|
||||
for (config = configs; config; config = config->next)
|
||||
{
|
||||
if (config->clid_len == clid_len &&
|
||||
memcmp(config->clid, clid, clid_len) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
/* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
|
||||
cope with that here */
|
||||
if (*clid == 0 && config->clid_len == clid_len-1 &&
|
||||
memcmp(config->clid, clid+1, clid_len-1) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
}
|
||||
|
||||
if (config->flags & CONFIG_CLID)
|
||||
{
|
||||
if (config->clid_len == clid_len &&
|
||||
memcmp(config->clid, clid, clid_len) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
/* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
|
||||
cope with that here */
|
||||
if (*clid == 0 && config->clid_len == clid_len-1 &&
|
||||
memcmp(config->clid, clid+1, clid_len-1) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
}
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
|
||||
if ((config->flags & CONFIG_HWADDR) &&
|
||||
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
if (hostname)
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->hostname && hostname_isequal(config->hostname, hostname) &&
|
||||
if ((config->flags & CONFIG_NAME) &&
|
||||
hostname_isequal(config->hostname, hostname) &&
|
||||
is_addr_in_context(context, config))
|
||||
return config;
|
||||
|
||||
@@ -436,28 +484,29 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
|
||||
{
|
||||
FILE *f = fopen(ETHERSFILE, "r");
|
||||
unsigned int e0, e1, e2, e3, e4, e5;
|
||||
char *ip, *cp, *name;
|
||||
unsigned int flags, e0, e1, e2, e3, e4, e5;
|
||||
char *ip, *cp;
|
||||
struct in_addr addr;
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
struct dhcp_config *config;
|
||||
int count = 0;
|
||||
|
||||
if (!f)
|
||||
die("failed to open " ETHERSFILE ":%s", NULL);
|
||||
|
||||
{
|
||||
syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
|
||||
return configs;
|
||||
}
|
||||
|
||||
while (fgets(buff, MAXDNAME, f))
|
||||
{
|
||||
while (strlen(buff) > 0 &&
|
||||
(buff[strlen(buff)-1] == '\n' ||
|
||||
buff[strlen(buff)-1] == ' ' ||
|
||||
buff[strlen(buff)-1] == '\r' ||
|
||||
buff[strlen(buff)-1] == '\t'))
|
||||
while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
|
||||
buff[strlen(buff)-1] = 0;
|
||||
|
||||
if ((*buff == '#') || (*buff == '+'))
|
||||
continue;
|
||||
|
||||
for (ip = buff; *ip && *ip != ' ' && *ip != '\t'; ip++);
|
||||
for(; *ip && (*ip == ' ' || *ip == '\t'); ip++)
|
||||
for (ip = buff; *ip && !isspace(*ip); ip++);
|
||||
for(; *ip && isspace(*ip); ip++)
|
||||
*ip = 0;
|
||||
if (!*ip)
|
||||
continue;
|
||||
@@ -465,6 +514,13 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
|
||||
if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
|
||||
continue;
|
||||
|
||||
hwaddr[0] = e0;
|
||||
hwaddr[1] = e1;
|
||||
hwaddr[2] = e2;
|
||||
hwaddr[3] = e3;
|
||||
hwaddr[4] = e4;
|
||||
hwaddr[5] = e5;
|
||||
|
||||
/* check for name or dotted-quad */
|
||||
for (cp = ip; *cp; cp++)
|
||||
if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
|
||||
@@ -472,47 +528,64 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
|
||||
|
||||
if (!*cp)
|
||||
{
|
||||
name = NULL;
|
||||
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
|
||||
continue;
|
||||
flags = CONFIG_ADDR;
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->addr.s_addr == addr.s_addr)
|
||||
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!canonicalise(ip))
|
||||
continue;
|
||||
name = ip;
|
||||
addr.s_addr = 0;
|
||||
flags = CONFIG_NAME;
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->hostname && hostname_isequal(config->hostname, name))
|
||||
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!config)
|
||||
{
|
||||
config = safe_malloc(sizeof(struct dhcp_config));
|
||||
config->clid_len = 0;
|
||||
config->clid = NULL;
|
||||
config->lease_time = 0;
|
||||
config->hostname = safe_string_alloc(name);
|
||||
config->addr = addr;
|
||||
config->next = configs;
|
||||
configs = config;
|
||||
for (config = configs; config; config = config->next)
|
||||
if ((config->flags & CONFIG_HWADDR) &&
|
||||
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
|
||||
break;
|
||||
|
||||
if (!config)
|
||||
{
|
||||
if (!(config = malloc(sizeof(struct dhcp_config))))
|
||||
continue;
|
||||
config->flags = 0;
|
||||
config->next = configs;
|
||||
configs = config;
|
||||
}
|
||||
|
||||
config->flags |= flags;
|
||||
|
||||
if (flags & CONFIG_NAME)
|
||||
{
|
||||
if ((config->hostname = malloc(strlen(ip)+1)))
|
||||
strcpy(config->hostname, ip);
|
||||
else
|
||||
config->flags &= ~CONFIG_NAME;
|
||||
}
|
||||
|
||||
if (flags & CONFIG_ADDR)
|
||||
config->addr = addr;
|
||||
}
|
||||
|
||||
config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
|
||||
memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
|
||||
|
||||
config->hwaddr[0] = e0;
|
||||
config->hwaddr[1] = e1;
|
||||
config->hwaddr[2] = e2;
|
||||
config->hwaddr[3] = e3;
|
||||
config->hwaddr[4] = e4;
|
||||
config->hwaddr[5] = e5;
|
||||
count++;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
|
||||
return configs;
|
||||
}
|
||||
|
||||
@@ -526,10 +599,13 @@ void dhcp_update_configs(struct dhcp_config *configs)
|
||||
struct crec *crec;
|
||||
|
||||
for (config = configs; config; config = config->next)
|
||||
if (config->addr.s_addr == 0 && config->hostname &&
|
||||
if (!(config->flags & CONFIG_ADDR) &&
|
||||
(config->flags & CONFIG_NAME) &&
|
||||
(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
|
||||
(crec->flags & F_HOSTS))
|
||||
config->addr = crec->addr.addr.addr4;
|
||||
|
||||
{
|
||||
config->addr = crec->addr.addr.addr4;
|
||||
config->flags |= CONFIG_ADDR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
342
src/dnsmasq.c
342
src/dnsmasq.c
@@ -10,13 +10,11 @@
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* See RFC1035 for details of the protocol this code talks. */
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static int sigterm, sighup, sigusr1, sigalarm;
|
||||
static int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
@@ -27,7 +25,22 @@ static void sig_handler(int sig)
|
||||
else if (sig == SIGUSR1)
|
||||
sigusr1 = 1;
|
||||
else if (sig == SIGALRM)
|
||||
sigalarm = 1;
|
||||
{
|
||||
/* alarm is used to kill children after a fixed time. */
|
||||
if (in_child)
|
||||
exit(0);
|
||||
else
|
||||
sigalarm = 1;
|
||||
}
|
||||
else if (sig == SIGCHLD)
|
||||
{
|
||||
/* See Stevens 5.10 */
|
||||
pid_t pid;
|
||||
int stat;
|
||||
|
||||
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
|
||||
num_kids--;
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
@@ -35,17 +48,19 @@ int main (int argc, char **argv)
|
||||
int cachesize = CACHESIZ;
|
||||
int port = NAMESERVER_PORT;
|
||||
int maxleases = MAXLEASES;
|
||||
unsigned short edns_pktsz = EDNS_PKTSZ;
|
||||
int query_port = 0;
|
||||
int first_loop = 1;
|
||||
int bind_fallback = 0;
|
||||
unsigned long local_ttl = 0;
|
||||
unsigned int options, min_leasetime;
|
||||
char *runfile = RUNFILE;
|
||||
time_t resolv_changed = 0;
|
||||
time_t now, last = 0;
|
||||
struct irec *interfaces = NULL;
|
||||
struct listener *listener, *listeners;
|
||||
struct listener *listener, *listeners = NULL;
|
||||
struct doctor *doctors = NULL;
|
||||
char *mxname = NULL;
|
||||
struct mx_record *mxnames = NULL;
|
||||
char *mxtarget = NULL;
|
||||
char *lease_file = NULL;
|
||||
char *addn_hosts = NULL;
|
||||
@@ -66,12 +81,13 @@ int main (int argc, char **argv)
|
||||
struct dhcp_context *dhcp_tmp, *dhcp = NULL;
|
||||
struct dhcp_config *dhcp_configs = NULL;
|
||||
struct dhcp_opt *dhcp_options = NULL;
|
||||
struct dhcp_vendor *dhcp_vendors = NULL;
|
||||
char *dhcp_file = NULL, *dhcp_sname = NULL;
|
||||
struct in_addr dhcp_next_server;
|
||||
int leasefd = -1, dhcpfd = -1, dhcp_raw_fd = -1;
|
||||
struct sigaction sigact;
|
||||
sigset_t sigmask;
|
||||
|
||||
|
||||
sighup = 1; /* init cache the first time through */
|
||||
sigusr1 = 0; /* but don't dump */
|
||||
sigterm = 0; /* or die */
|
||||
@@ -80,6 +96,8 @@ int main (int argc, char **argv)
|
||||
#else
|
||||
sigalarm = 0; /* or not */
|
||||
#endif
|
||||
num_kids = 0;
|
||||
in_child = 0;
|
||||
|
||||
sigact.sa_handler = sig_handler;
|
||||
sigact.sa_flags = 0;
|
||||
@@ -88,6 +106,11 @@ int main (int argc, char **argv)
|
||||
sigaction(SIGHUP, &sigact, NULL);
|
||||
sigaction(SIGTERM, &sigact, NULL);
|
||||
sigaction(SIGALRM, &sigact, NULL);
|
||||
sigaction(SIGCHLD, &sigact, NULL);
|
||||
|
||||
/* ignore SIGPIPE */
|
||||
sigact.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &sigact, NULL);
|
||||
|
||||
/* now block all the signals, they stay that way except
|
||||
during the call to pselect */
|
||||
@@ -95,6 +118,7 @@ int main (int argc, char **argv)
|
||||
sigaddset(&sigact.sa_mask, SIGTERM);
|
||||
sigaddset(&sigact.sa_mask, SIGHUP);
|
||||
sigaddset(&sigact.sa_mask, SIGALRM);
|
||||
sigaddset(&sigact.sa_mask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
|
||||
|
||||
/* These get allocated here to avoid overflowing the small stack
|
||||
@@ -102,42 +126,65 @@ int main (int argc, char **argv)
|
||||
maximal sixed domain name and gets passed into all the processing
|
||||
code. We manage to get away with one buffer. */
|
||||
dnamebuff = safe_malloc(MAXDNAME);
|
||||
packet = safe_malloc(DNSMASQ_PACKETSZ);
|
||||
|
||||
dhcp_next_server.s_addr = 0;
|
||||
options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
|
||||
options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file,
|
||||
&username, &groupname, &domain_suffix, &runfile,
|
||||
&if_names, &if_addrs, &if_except, &bogus_addr,
|
||||
&serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
|
||||
&dhcp, &dhcp_configs, &dhcp_options,
|
||||
&dhcp, &dhcp_configs, &dhcp_options, &dhcp_vendors,
|
||||
&dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
|
||||
&doctors);
|
||||
&doctors, &edns_pktsz);
|
||||
|
||||
/* if we cannot support binding the wildcard address, set the "bind only
|
||||
interfaces in use" option */
|
||||
#ifndef HAVE_UDP_SRC_DST
|
||||
options |= OPT_NOWILD;
|
||||
#endif
|
||||
if (edns_pktsz < PACKETSZ)
|
||||
edns_pktsz = PACKETSZ;
|
||||
packet = safe_malloc(edns_pktsz > DNSMASQ_PACKETSZ ? edns_pktsz : DNSMASQ_PACKETSZ);
|
||||
|
||||
if (!lease_file)
|
||||
lease_file = LEASEFILE;
|
||||
else
|
||||
{
|
||||
if (!dhcp)
|
||||
{
|
||||
complain("********* dhcp-lease option set, but not dhcp-range.", NULL);
|
||||
complain("********* Are you trying to use the obsolete ISC dhcpd integration?", NULL);
|
||||
complain("********* Please configure the dnsmasq integrated DHCP server by using", NULL);
|
||||
complain("********* the \"dhcp-range\" option, and remove any other DHCP server.", NULL);
|
||||
}
|
||||
if (dhcp)
|
||||
lease_file = LEASEFILE;
|
||||
}
|
||||
#ifndef HAVE_ISC_READER
|
||||
else if (!dhcp)
|
||||
die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
|
||||
#endif
|
||||
|
||||
interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port);
|
||||
|
||||
if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port)))
|
||||
{
|
||||
bind_fallback = 1;
|
||||
options |= OPT_NOWILD;
|
||||
}
|
||||
|
||||
if (options & OPT_NOWILD)
|
||||
{
|
||||
struct iname *if_tmp;
|
||||
listeners = create_bound_listeners(interfaces, port);
|
||||
|
||||
for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
|
||||
if (if_tmp->name && !if_tmp->used)
|
||||
die("unknown interface %s", if_tmp->name);
|
||||
|
||||
for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
|
||||
if (!if_tmp->used)
|
||||
{
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
#ifdef HAVE_IPV6
|
||||
if (if_tmp->addr.sa.sa_family == AF_INET)
|
||||
inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
|
||||
addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
|
||||
addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
|
||||
#endif
|
||||
die("no interface with address %s", addrbuff);
|
||||
}
|
||||
}
|
||||
|
||||
interfaces = enumerate_interfaces(if_names, if_addrs, if_except, port);
|
||||
if (options & OPT_NOWILD)
|
||||
listeners = create_bound_listeners(interfaces);
|
||||
else
|
||||
listeners = create_wildcard_listeners(port);
|
||||
|
||||
forward_init(1);
|
||||
cache_init(cachesize, options & OPT_LOG);
|
||||
|
||||
@@ -150,13 +197,42 @@ int main (int argc, char **argv)
|
||||
|
||||
if (dhcp)
|
||||
{
|
||||
dhcp_init(&dhcpfd, &dhcp_raw_fd);
|
||||
#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
|
||||
int c;
|
||||
struct iname *tmp;
|
||||
for (c = 0, tmp = if_names; tmp; tmp = tmp->next)
|
||||
if (!tmp->isloop)
|
||||
c++;
|
||||
if (c != 1)
|
||||
die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
|
||||
#endif
|
||||
dhcp_init(&dhcpfd, &dhcp_raw_fd, dhcp_configs);
|
||||
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
|
||||
if (options & OPT_ETHERS)
|
||||
dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
|
||||
lease_update_from_configs(dhcp_configs, domain_suffix); /* must follow cache_init and lease_init */
|
||||
lease_update_file(0, now);
|
||||
lease_update_dns();
|
||||
}
|
||||
|
||||
/* If query_port is set then create a socket now, before dumping root
|
||||
for use to access nameservers without more specific source addresses.
|
||||
This allows query_port to be a low port */
|
||||
if (query_port)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
addr.in.sin_family = AF_INET;
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = htons(query_port);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
allocate_sfd(&addr, &sfds);
|
||||
#ifdef HAVE_IPV6
|
||||
addr.in6.sin6_family = AF_INET6;
|
||||
addr.in6.sin6_addr = in6addr_any;
|
||||
addr.in6.sin6_port = htons(query_port);
|
||||
addr.in6.sin6_flowinfo = htonl(0);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
allocate_sfd(&addr, &sfds);
|
||||
#endif
|
||||
}
|
||||
|
||||
setbuf(stdout, NULL);
|
||||
@@ -195,8 +271,12 @@ int main (int argc, char **argv)
|
||||
for (i=0; i<64; i++)
|
||||
{
|
||||
for (listener = listeners; listener; listener = listener->next)
|
||||
if (listener->fd == i)
|
||||
break;
|
||||
{
|
||||
if (listener->fd == i)
|
||||
break;
|
||||
if (listener->tcpfd == i)
|
||||
break;
|
||||
}
|
||||
if (listener)
|
||||
continue;
|
||||
|
||||
@@ -206,6 +286,12 @@ int main (int argc, char **argv)
|
||||
i == dhcp_raw_fd)
|
||||
continue;
|
||||
|
||||
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (serverfdp->fd == i)
|
||||
break;
|
||||
if (serverfdp)
|
||||
continue;
|
||||
|
||||
close(i);
|
||||
}
|
||||
|
||||
@@ -230,25 +316,33 @@ int main (int argc, char **argv)
|
||||
DNSMASQ_LOG_OPT(options & OPT_DEBUG),
|
||||
DNSMASQ_LOG_FAC(options & OPT_DEBUG));
|
||||
|
||||
if (cachesize)
|
||||
if (cachesize != 0)
|
||||
syslog(LOG_INFO, "started, version %s cachesize %d", VERSION, cachesize);
|
||||
else
|
||||
syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
|
||||
|
||||
if (options & OPT_LOCALMX)
|
||||
syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
|
||||
else if (mxname)
|
||||
syslog(LOG_INFO, "serving MX record for mailhost %s target %s",
|
||||
mxname, mxtarget);
|
||||
|
||||
if (bind_fallback)
|
||||
syslog(LOG_WARNING, "setting --bind-interfaces option because of OS limitations");
|
||||
|
||||
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
|
||||
{
|
||||
strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
|
||||
if (dhcp_tmp->lease_time == 0)
|
||||
sprintf(packet, "infinite");
|
||||
else
|
||||
sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
|
||||
syslog(LOG_INFO, "DHCP, IP range %s -- %s, lease time %s",
|
||||
{
|
||||
unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
|
||||
if ((x = t/3600))
|
||||
p += sprintf(&packet[p], "%dh", x);
|
||||
if ((x = (t/60)%60))
|
||||
p += sprintf(&packet[p], "%dm", x);
|
||||
if ((x = t%60))
|
||||
p += sprintf(&packet[p], "%ds", x);
|
||||
}
|
||||
syslog(LOG_INFO,
|
||||
dhcp_tmp->start.s_addr == dhcp_tmp->end.s_addr ?
|
||||
"DHCP, static leases only on %.0s%s, lease time %s" :
|
||||
"DHCP, IP range %s -- %s, lease time %s",
|
||||
dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
|
||||
}
|
||||
|
||||
@@ -256,12 +350,13 @@ int main (int argc, char **argv)
|
||||
if (dhcp)
|
||||
syslog(LOG_INFO, "DHCP, %s will be written every %ds", lease_file, min_leasetime/3);
|
||||
#endif
|
||||
|
||||
if (!(options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
|
||||
syslog(LOG_WARNING, "running as root");
|
||||
|
||||
servers = check_servers(serv_addrs, interfaces, &sfds);
|
||||
last_server = NULL;
|
||||
|
||||
if (getuid() == 0 || geteuid() == 0)
|
||||
syslog(LOG_WARNING, "failed to drop root privs");
|
||||
|
||||
servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
|
||||
|
||||
while (sigterm == 0)
|
||||
{
|
||||
fd_set rset;
|
||||
@@ -271,15 +366,19 @@ int main (int argc, char **argv)
|
||||
cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
|
||||
if (dhcp)
|
||||
{
|
||||
if (options & OPT_ETHERS)
|
||||
dhcp_configs = dhcp_read_ethers(dhcp_configs, dnamebuff);
|
||||
dhcp_update_configs(dhcp_configs);
|
||||
lease_update_from_configs(dhcp_configs, domain_suffix);
|
||||
lease_update_file(0, now);
|
||||
lease_update_dns();
|
||||
}
|
||||
if (resolv && (options & OPT_NO_POLL))
|
||||
servers = last_server =
|
||||
check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
{
|
||||
servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
last_server = NULL;
|
||||
}
|
||||
sighup = 0;
|
||||
}
|
||||
|
||||
@@ -319,6 +418,9 @@ int main (int argc, char **argv)
|
||||
FD_SET(listener->fd, &rset);
|
||||
if (listener->fd > maxfd)
|
||||
maxfd = listener->fd;
|
||||
FD_SET(listener->tcpfd, &rset);
|
||||
if (listener->tcpfd > maxfd)
|
||||
maxfd = listener->tcpfd;
|
||||
}
|
||||
|
||||
if (dhcp)
|
||||
@@ -350,11 +452,17 @@ int main (int argc, char **argv)
|
||||
if (last == 0 || difftime(now, last) > 1.0)
|
||||
{
|
||||
last = now;
|
||||
|
||||
#ifdef HAVE_ISC_READER
|
||||
if (lease_file && !dhcp)
|
||||
load_dhcp(lease_file, domain_suffix, now, dnamebuff);
|
||||
#endif
|
||||
|
||||
if (!(options & OPT_NO_POLL))
|
||||
{
|
||||
struct resolvc *res = resolv, *latest = NULL;
|
||||
time_t last_change = 0;
|
||||
struct stat statbuf;
|
||||
time_t last_change = 0;
|
||||
/* There may be more than one possible file.
|
||||
Go through and find the one which changed _last_.
|
||||
Warn of any which can't be read. */
|
||||
@@ -369,7 +477,7 @@ int main (int argc, char **argv)
|
||||
else
|
||||
{
|
||||
res->logged = 0;
|
||||
if (statbuf.st_mtime > last_change)
|
||||
if (difftime(statbuf.st_mtime, last_change) > 0.0)
|
||||
{
|
||||
last_change = statbuf.st_mtime;
|
||||
latest = res;
|
||||
@@ -378,32 +486,128 @@ int main (int argc, char **argv)
|
||||
res = res->next;
|
||||
}
|
||||
|
||||
if (latest && last_change > resolv_changed)
|
||||
if (latest && difftime(last_change, resolv_changed) > 0.0)
|
||||
{
|
||||
resolv_changed = last_change;
|
||||
servers = last_server =
|
||||
check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
|
||||
interfaces, &sfds);
|
||||
last_server = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
if (FD_ISSET(serverfdp->fd, &rset))
|
||||
last_server = reply_query(serverfdp->fd, options, packet, now,
|
||||
dnamebuff, last_server, bogus_addr, doctors);
|
||||
last_server = reply_query(serverfdp, options, packet, now,
|
||||
dnamebuff, servers, last_server,
|
||||
bogus_addr, doctors, edns_pktsz);
|
||||
|
||||
if (dhcp && FD_ISSET(dhcpfd, &rset))
|
||||
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs,
|
||||
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors,
|
||||
now, dnamebuff, domain_suffix, dhcp_file,
|
||||
dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
|
||||
if_names, if_addrs, if_except);
|
||||
|
||||
for (listener = listeners; listener; listener = listener->next)
|
||||
if (FD_ISSET(listener->fd, &rset))
|
||||
last_server = receive_query(listener, packet,
|
||||
mxname, mxtarget, options, now, local_ttl, dnamebuff,
|
||||
if_names, if_addrs, if_except, last_server, servers);
|
||||
{
|
||||
if (FD_ISSET(listener->fd, &rset))
|
||||
last_server = receive_query(listener, packet,
|
||||
mxnames, mxtarget, options, now, local_ttl, dnamebuff,
|
||||
if_names, if_addrs, if_except, last_server, servers, edns_pktsz);
|
||||
|
||||
if (FD_ISSET(listener->tcpfd, &rset))
|
||||
{
|
||||
int confd;
|
||||
|
||||
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd != -1)
|
||||
{
|
||||
int match = 1;
|
||||
if (!(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 = if_except; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
|
||||
match = 0;
|
||||
|
||||
if (match && (if_names || if_addrs))
|
||||
{
|
||||
match = 0;
|
||||
for (tmp = if_names; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
|
||||
match = 1;
|
||||
for (tmp = if_addrs; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
|
||||
match = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!match || (num_kids >= MAX_PROCS))
|
||||
close(confd);
|
||||
else if (!(options & OPT_DEBUG) && fork())
|
||||
{
|
||||
num_kids++;
|
||||
close(confd);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *buff;
|
||||
struct server *s;
|
||||
int flags;
|
||||
|
||||
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
if (!(options & OPT_DEBUG))
|
||||
{
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigaddset(&sigact.sa_mask, SIGALRM);
|
||||
sigprocmask(SIG_UNBLOCK, &sigact.sa_mask, NULL);
|
||||
alarm(CHILD_LIFETIME);
|
||||
in_child = 1;
|
||||
}
|
||||
|
||||
/* start with no upstream connections. */
|
||||
for (s = servers; s; s = s->next)
|
||||
s->tcpfd = -1;
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
|
||||
|
||||
buff = tcp_request(confd, mxnames, mxtarget, options, now,
|
||||
local_ttl, dnamebuff, last_server, servers,
|
||||
bogus_addr, doctors, edns_pktsz);
|
||||
|
||||
|
||||
if (!(options & OPT_DEBUG))
|
||||
exit(0);
|
||||
|
||||
close(confd);
|
||||
if (buff)
|
||||
free(buff);
|
||||
for (s = servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
close(s->tcpfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "exiting on receipt of SIGTERM");
|
||||
|
||||
108
src/dnsmasq.h
108
src/dnsmasq.h
@@ -11,7 +11,8 @@
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
|
||||
#define COPYRIGHT "Copyright (C) 2000-2004 Simon Kelley"
|
||||
|
||||
#ifdef __linux__
|
||||
/* for pselect.... */
|
||||
@@ -36,10 +37,12 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#if defined(__sun) || defined(__sun__)
|
||||
# include <sys/sockio.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
@@ -55,7 +58,7 @@
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#if defined(__OpenBSD__)
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
# include <netinet/if_ether.h>
|
||||
#else
|
||||
# include <net/ethernet.h>
|
||||
@@ -90,6 +93,7 @@
|
||||
#define OPT_NODOTS_LOCAL 4096
|
||||
#define OPT_NOWILD 8192
|
||||
#define OPT_ETHERS 16384
|
||||
#define OPT_RESOLV_DOMAIN 32768
|
||||
|
||||
struct all_addr {
|
||||
union {
|
||||
@@ -111,6 +115,11 @@ struct doctor {
|
||||
struct doctor *next;
|
||||
};
|
||||
|
||||
struct mx_record {
|
||||
char *mxname, *mxtarget;
|
||||
struct mx_record *next;
|
||||
};
|
||||
|
||||
union bigname {
|
||||
char name[MAXDNAME];
|
||||
union bigname *next; /* freelist */
|
||||
@@ -129,7 +138,7 @@ struct crec {
|
||||
};
|
||||
|
||||
#define F_IMMORTAL 1
|
||||
#define F_CONFIG 2
|
||||
#define F_CONFIG 2
|
||||
#define F_REVERSE 4
|
||||
#define F_FORWARD 8
|
||||
#define F_DHCP 16
|
||||
@@ -187,7 +196,7 @@ struct server {
|
||||
struct serverfd *sfd; /* non-NULL if this server has its own fd bound to
|
||||
a source port */
|
||||
char *domain; /* set if this server only handles a domain. */
|
||||
int flags;
|
||||
int flags, tcpfd;
|
||||
struct server *next;
|
||||
};
|
||||
|
||||
@@ -197,7 +206,7 @@ struct irec {
|
||||
};
|
||||
|
||||
struct listener {
|
||||
int fd, family;
|
||||
int fd, tcpfd, family;
|
||||
struct listener *next;
|
||||
};
|
||||
|
||||
@@ -205,6 +214,7 @@ struct listener {
|
||||
struct iname {
|
||||
char *name;
|
||||
union mysockaddr addr;
|
||||
int isloop, used;
|
||||
struct iname *next;
|
||||
};
|
||||
|
||||
@@ -220,6 +230,7 @@ struct frec {
|
||||
union mysockaddr source;
|
||||
struct all_addr dest;
|
||||
struct server *sentto;
|
||||
unsigned int iface;
|
||||
unsigned short orig_id, new_id;
|
||||
int fd;
|
||||
time_t time;
|
||||
@@ -236,28 +247,51 @@ struct dhcp_lease {
|
||||
struct dhcp_lease *next;
|
||||
};
|
||||
|
||||
struct dhcp_netid {
|
||||
char *net;
|
||||
struct dhcp_netid *next;
|
||||
};
|
||||
|
||||
struct dhcp_config {
|
||||
unsigned int flags;
|
||||
int clid_len; /* length of client identifier */
|
||||
unsigned char *clid; /* clientid */
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
char *hostname;
|
||||
struct dhcp_netid netid;
|
||||
struct in_addr addr;
|
||||
unsigned int lease_time;
|
||||
struct dhcp_config *next;
|
||||
};
|
||||
|
||||
#define CONFIG_DISABLE 1
|
||||
#define CONFIG_CLID 2
|
||||
#define CONFIG_HWADDR 4
|
||||
#define CONFIG_TIME 8
|
||||
#define CONFIG_NAME 16
|
||||
#define CONFIG_ADDR 32
|
||||
#define CONFIG_NETID 64
|
||||
#define CONFIG_NOCLID 128
|
||||
|
||||
struct dhcp_opt {
|
||||
int opt, len, is_addr;
|
||||
unsigned char *val;
|
||||
char *netid;
|
||||
struct dhcp_opt *next;
|
||||
};
|
||||
};
|
||||
|
||||
struct dhcp_vendor {
|
||||
int len, is_vendor, used;
|
||||
char *data;
|
||||
struct dhcp_netid netid;
|
||||
struct dhcp_vendor *next;
|
||||
};
|
||||
|
||||
struct dhcp_context {
|
||||
unsigned int lease_time;
|
||||
unsigned int lease_time, addr_epoch;
|
||||
struct in_addr netmask, broadcast;
|
||||
struct in_addr start, end, last; /* range of available addresses */
|
||||
char *netid;
|
||||
struct in_addr start, end; /* range of available addresses */
|
||||
struct dhcp_netid netid;
|
||||
struct dhcp_context *next;
|
||||
};
|
||||
|
||||
@@ -313,16 +347,18 @@ int setup_reply(HEADER *header, unsigned int qlen,
|
||||
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
|
||||
time_t now, struct doctor *doctors);
|
||||
void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now);
|
||||
int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
|
||||
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now, unsigned long local_ttl,
|
||||
char *namebuff);
|
||||
char *namebuff, unsigned short edns_pcktsz);
|
||||
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);
|
||||
|
||||
/* util.c */
|
||||
unsigned short rand16(void);
|
||||
int legal_char(char c);
|
||||
int canonicalise(char *s);
|
||||
int atoi_check(char *a, int *res);
|
||||
void die(char *message, char *arg1);
|
||||
void complain(char *message, char *arg1);
|
||||
void *safe_malloc(int size);
|
||||
@@ -331,49 +367,63 @@ int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int hostname_isequal(unsigned char *a, unsigned char *b);
|
||||
time_t dnsmasq_time(int fd);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
|
||||
/* option.c */
|
||||
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
|
||||
char **mxname, char **mxtarget, char **lease_file,
|
||||
struct mx_record **mxnames, char **mxtarget, char **lease_file,
|
||||
char **username, char **groupname,
|
||||
char **domain_suffix, char **runfile,
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize,
|
||||
int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts,
|
||||
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, struct dhcp_opt **opts,
|
||||
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf,
|
||||
struct dhcp_opt **opts, struct dhcp_vendor **dhcp_vendors,
|
||||
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server,
|
||||
int *maxleases, unsigned int *min_leasetime, struct doctor **doctors);
|
||||
int *maxleases, unsigned int *min_leasetime, struct doctor **doctors,
|
||||
unsigned short *edns_pktsz);
|
||||
|
||||
/* forward.c */
|
||||
void forward_init(int first);
|
||||
struct server *reply_query(int fd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *last_server,
|
||||
struct bogus_addr *bogus_nxdomain, struct doctor *doctors);
|
||||
struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *servers, struct server *last_server,
|
||||
struct bogus_addr *bogus_nxdomain,
|
||||
struct doctor *doctors, unsigned short edns_pcktsz);
|
||||
|
||||
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
|
||||
struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now,
|
||||
unsigned long local_ttl, char *namebuff,
|
||||
struct iname *names, struct iname *addrs, struct iname *except,
|
||||
struct server *last_server, struct server *servers);
|
||||
struct server *last_server, struct server *servers, unsigned short edns_pcktsz);
|
||||
|
||||
char *tcp_request(int confd, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now,
|
||||
unsigned long local_ttl, char *namebuff,
|
||||
struct server *last_server, struct server *servers,
|
||||
struct bogus_addr *bogus_nxdomain, struct doctor *doctors,
|
||||
unsigned short edns_pcktsz);
|
||||
/* network.c */
|
||||
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
|
||||
struct server *reload_servers(char *fname, char *buff, struct server *servers, int query_port);
|
||||
struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds);
|
||||
struct irec *enumerate_interfaces(struct iname *names,
|
||||
struct iname *addrs,
|
||||
struct irec *enumerate_interfaces(struct iname **names,
|
||||
struct iname **addrs,
|
||||
struct iname *except,
|
||||
int port);
|
||||
struct listener *create_wildcard_listeners(int port);
|
||||
struct listener *create_bound_listeners(struct irec *interfaces);
|
||||
struct listener *create_bound_listeners(struct irec *interfaces, int port);
|
||||
/* dhcp.c */
|
||||
void dhcp_init(int *fdp, int* rfdp);
|
||||
void dhcp_init(int *fdp, int* rfdp, struct dhcp_config *configs);
|
||||
void dhcp_packet(struct dhcp_context *contexts, char *packet,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
struct dhcp_vendor *vendors,
|
||||
time_t now, char *namebuff, char *domain_suffix,
|
||||
char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
|
||||
struct iname *names, struct iname *addrs, struct iname *except);
|
||||
int address_available(struct dhcp_context *context, struct in_addr addr);
|
||||
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
|
||||
struct in_addr *addrp);
|
||||
struct in_addr *addrp, unsigned char *hwaddr);
|
||||
struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
struct dhcp_context *context,
|
||||
unsigned char *clid, int clid_len,
|
||||
@@ -381,6 +431,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
struct dhcp_config *read_ethers(struct dhcp_config *configs, char *buff);
|
||||
void dhcp_update_configs(struct dhcp_config *configs);
|
||||
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff);
|
||||
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
|
||||
/* lease.c */
|
||||
void lease_update_file(int force, time_t now);
|
||||
void lease_update_dns(void);
|
||||
@@ -394,6 +445,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len);
|
||||
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
||||
void lease_prune(struct dhcp_lease *target, time_t now);
|
||||
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain);
|
||||
|
||||
/* rfc2131.c */
|
||||
int dhcp_reply(struct dhcp_context *context,
|
||||
struct in_addr iface_addr,
|
||||
@@ -402,6 +454,12 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
struct udp_dhcp_packet *rawpacket,
|
||||
unsigned int sz, time_t now, char *namebuff,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
struct dhcp_vendor *vendors,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server);
|
||||
struct in_addr dhcp_next_server, struct in_addr router);
|
||||
|
||||
/* isc.c */
|
||||
#ifdef HAVE_ISC_READER
|
||||
void load_dhcp(char *file, char *suffix, time_t now, char *hostname);
|
||||
#endif
|
||||
|
||||
|
||||
721
src/forward.c
721
src/forward.c
@@ -26,7 +26,7 @@ static unsigned short get_id(void);
|
||||
void forward_init(int first)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
|
||||
if (first)
|
||||
frec_list = NULL;
|
||||
for (f = frec_list; f; f = f->next)
|
||||
@@ -36,11 +36,11 @@ void forward_init(int first)
|
||||
/* Send a UDP packet with it's source address set as "source"
|
||||
unless nowild is true, when we just send it with the kernel default */
|
||||
static void send_from(int fd, int nowild, char *packet, int len,
|
||||
union mysockaddr *to, struct all_addr *source)
|
||||
union mysockaddr *to, struct all_addr *source,
|
||||
unsigned int iface)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct iovec iov[1];
|
||||
struct cmsghdr *cmptr;
|
||||
union {
|
||||
struct cmsghdr align; /* this ensures alignment */
|
||||
#if defined(IP_PKTINFO)
|
||||
@@ -52,178 +52,184 @@ static void send_from(int fd, int nowild, char *packet, int len,
|
||||
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
|
||||
#endif
|
||||
} control_u;
|
||||
|
||||
|
||||
iov[0].iov_base = packet;
|
||||
iov[0].iov_len = len;
|
||||
|
||||
if (nowild)
|
||||
{
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.msg_control = &control_u;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
}
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_name = to;
|
||||
msg.msg_namelen = sa_len(to);
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
cmptr = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
|
||||
if (!nowild && to->sa.sa_family == AF_INET)
|
||||
{
|
||||
msg.msg_control = &control_u;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
{
|
||||
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
|
||||
#if defined(IP_PKTINFO)
|
||||
if (!nowild && to->sa.sa_family == AF_INET)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
if (!nowild && to->sa.sa_family == AF_INET)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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
|
||||
if (!nowild && to->sa.sa_family == AF_INET6)
|
||||
if (to->sa.sa_family == AF_INET6)
|
||||
{
|
||||
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
|
||||
pkt->ipi6_ifindex = 0;
|
||||
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;
|
||||
cmptr->cmsg_level = IPPROTO_IPV6;
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sendmsg(fd, &msg, 0);
|
||||
|
||||
/* certain Linux kernels seem to object to setting the source address in the IPv6 stack
|
||||
by returning EINVAL from sendmsg. In that case, try again without setting the
|
||||
source address, since it will nearly alway be correct anyway. IPv6 stinks. */
|
||||
if (sendmsg(fd, &msg, 0) == -1 && errno == EINVAL)
|
||||
{
|
||||
msg.msg_controllen = 0;
|
||||
sendmsg(fd, &msg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short search_servers(struct server *servers, unsigned int options, struct all_addr **addrpp,
|
||||
unsigned short qtype, char *qdomain, int *type, char **domain)
|
||||
|
||||
{
|
||||
/* If the query ends in the domain in one of our servers, set
|
||||
domain to point to that name. We find the largest match to allow both
|
||||
domain.org and sub.domain.org to exist. */
|
||||
|
||||
unsigned int namelen = strlen(qdomain);
|
||||
unsigned int matchlen = 0;
|
||||
struct server *serv;
|
||||
unsigned short flags = 0;
|
||||
|
||||
for (serv=servers; serv; serv=serv->next)
|
||||
/* domain matches take priority over NODOTS matches */
|
||||
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.'))
|
||||
{
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
*type = SERV_FOR_NODOTS;
|
||||
flags = 0;
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
flags = F_NOERR;
|
||||
else if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & qtype))
|
||||
{
|
||||
flags = sflag;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (serv->flags & SERV_HAS_DOMAIN)
|
||||
{
|
||||
unsigned int domainlen = strlen(serv->domain);
|
||||
if (namelen >= domainlen &&
|
||||
hostname_isequal(qdomain + namelen - domainlen, serv->domain) &&
|
||||
domainlen >= matchlen)
|
||||
{
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
*type = SERV_HAS_DOMAIN;
|
||||
*domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
flags = 0;
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
flags = F_NOERR;
|
||||
else if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & qtype))
|
||||
{
|
||||
flags = qtype;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & ~F_NOERR) /* flags set here means a literal found */
|
||||
{
|
||||
if (flags & F_QUERY)
|
||||
log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL);
|
||||
else
|
||||
log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp);
|
||||
}
|
||||
else if (qtype && (options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
|
||||
flags = F_NXDOMAIN;
|
||||
|
||||
if (flags & (F_NOERR | F_NXDOMAIN))
|
||||
log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL);
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* returns new last_server */
|
||||
static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
struct all_addr *dst_addr, HEADER *header,
|
||||
int plen, unsigned int options, char *dnamebuff,
|
||||
struct all_addr *dst_addr, unsigned int dst_iface,
|
||||
HEADER *header, int plen, unsigned int options, char *dnamebuff,
|
||||
struct server *servers, struct server *last_server,
|
||||
time_t now, unsigned long local_ttl)
|
||||
{
|
||||
struct frec *forward;
|
||||
char *domain = NULL;
|
||||
int type = 0;
|
||||
struct server *serv;
|
||||
int forwardall = 0, type = 0;
|
||||
struct all_addr *addrp = NULL;
|
||||
unsigned short flags = 0;
|
||||
unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff);
|
||||
|
||||
struct server *start = NULL;
|
||||
|
||||
/* may be recursion not speced or no servers available. */
|
||||
if (!header->rd || !servers)
|
||||
forward = NULL;
|
||||
else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
|
||||
{
|
||||
/* retry on existing query, send to next server */
|
||||
/* retry on existing query, send to all available servers */
|
||||
domain = forward->sentto->domain;
|
||||
if (!(options & OPT_ORDER))
|
||||
{
|
||||
forwardall = 1;
|
||||
last_server = NULL;
|
||||
}
|
||||
type = forward->sentto->flags & SERV_TYPE;
|
||||
if (!(forward->sentto = forward->sentto->next))
|
||||
forward->sentto = servers; /* at end of list, recycle */
|
||||
if (!(start = forward->sentto->next))
|
||||
start = servers; /* at end of list, recycle */
|
||||
header->id = htons(forward->new_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gotname)
|
||||
{
|
||||
/* If the query ends in the domain in one of our servers, set
|
||||
domain to point to that name. We find the largest match to allow both
|
||||
domain.org and sub.domain.org to exist. */
|
||||
|
||||
unsigned int namelen = strlen(dnamebuff);
|
||||
unsigned int matchlen = 0;
|
||||
|
||||
for (serv=servers; serv; serv=serv->next)
|
||||
/* domain matches take priority over NODOTS matches */
|
||||
if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.'))
|
||||
{
|
||||
if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
/* flags gets set if server is in fact an answer */
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
if (sflag & gotname) /* only OK if addrfamily == query */
|
||||
{
|
||||
type = SERV_FOR_NODOTS;
|
||||
flags = sflag;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
flags = 0;
|
||||
}
|
||||
else if (serv->flags & SERV_HAS_DOMAIN)
|
||||
{
|
||||
unsigned int domainlen = strlen(serv->domain);
|
||||
if (namelen >= domainlen &&
|
||||
hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) &&
|
||||
domainlen > matchlen)
|
||||
{
|
||||
if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{ /* flags gets set if server is in fact an answer */
|
||||
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
|
||||
if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */
|
||||
{
|
||||
type = SERV_HAS_DOMAIN;
|
||||
flags = gotname;
|
||||
domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
addrp = (struct all_addr *)&serv->addr.in.sin_addr;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flags = 0; /* may be better match from previous literal */
|
||||
domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
type = SERV_HAS_DOMAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flags = search_servers(servers, options, &addrp, gotname, dnamebuff, &type, &domain);
|
||||
|
||||
if (flags) /* flags set here means a literal found */
|
||||
{
|
||||
if (flags & F_QUERY)
|
||||
log_query(F_CONFIG | F_FORWARD | F_NEG, dnamebuff, NULL);
|
||||
else
|
||||
log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we may by policy not forward names without a domain part */
|
||||
if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.'))
|
||||
flags = F_NXDOMAIN;
|
||||
else if (!(forward = get_new_frec(now)))
|
||||
/* table full - server failure. */
|
||||
flags = F_NEG;
|
||||
}
|
||||
if (!flags && !(forward = get_new_frec(now)))
|
||||
/* table full - server failure. */
|
||||
flags = F_NEG;
|
||||
|
||||
if (forward)
|
||||
{
|
||||
@@ -232,71 +238,73 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
otherwise, use the one last known to work. */
|
||||
|
||||
if (type != 0 || (options & OPT_ORDER))
|
||||
forward->sentto = servers;
|
||||
else
|
||||
forward->sentto = last_server;
|
||||
start = servers;
|
||||
else if (!(start = last_server))
|
||||
{
|
||||
start = servers;
|
||||
forwardall = 1;
|
||||
}
|
||||
|
||||
forward->source = *udpaddr;
|
||||
forward->dest = *dst_addr;
|
||||
forward->iface = dst_iface;
|
||||
forward->new_id = get_id();
|
||||
forward->fd = udpfd;
|
||||
forward->orig_id = ntohs(header->id);
|
||||
header->id = htons(forward->new_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* check for send errors here (no route to host)
|
||||
if we fail to send to all nameservers, send back an error
|
||||
packet straight away (helps modem users when offline) */
|
||||
|
||||
if (!flags && forward)
|
||||
{
|
||||
struct server *firstsentto = forward->sentto;
|
||||
|
||||
struct server *firstsentto = start;
|
||||
int forwarded = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int logflags = 0;
|
||||
|
||||
if (forward->sentto->addr.sa.sa_family == AF_INET)
|
||||
{
|
||||
logflags = F_SERVER | F_IPV4 | F_FORWARD;
|
||||
addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
|
||||
}
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
{
|
||||
logflags = F_SERVER | F_IPV6 | F_FORWARD;
|
||||
addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
|
||||
}
|
||||
#endif
|
||||
/* only send to servers dealing with our domain.
|
||||
domain may be NULL, in which case server->domain
|
||||
must be NULL also. */
|
||||
|
||||
if (type == (forward->sentto->flags & SERV_TYPE) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
|
||||
if (type == (start->flags & SERV_TYPE) &&
|
||||
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
|
||||
{
|
||||
if (forward->sentto->flags & SERV_NO_ADDR)
|
||||
flags = F_NOERR; /* NULL servers are OK. */
|
||||
else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
|
||||
sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
|
||||
&forward->sentto->addr.sa,
|
||||
sa_len(&forward->sentto->addr)) != -1)
|
||||
if (!(start->flags & SERV_LITERAL_ADDRESS) &&
|
||||
sendto(start->sfd->fd, (char *)header, plen, 0,
|
||||
&start->addr.sa,
|
||||
sa_len(&start->addr)) != -1)
|
||||
{
|
||||
log_query(logflags, gotname ? dnamebuff : "query", addrp);
|
||||
/* for no-domain, don't update last_server */
|
||||
return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
|
||||
if (!gotname)
|
||||
strcpy(dnamebuff, "query");
|
||||
if (start->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff,
|
||||
(struct all_addr *)&start->addr.in.sin_addr);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff,
|
||||
(struct all_addr *)&start->addr.in6.sin6_addr);
|
||||
#endif
|
||||
forwarded = 1;
|
||||
forward->sentto = start;
|
||||
if (!forwardall)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(forward->sentto = forward->sentto->next))
|
||||
forward->sentto = servers;
|
||||
if (!(start = start->next))
|
||||
start = servers;
|
||||
|
||||
/* check if we tried all without success */
|
||||
if (forward->sentto == firstsentto)
|
||||
if (start == firstsentto)
|
||||
break;
|
||||
}
|
||||
|
||||
if (forwarded)
|
||||
return last_server;
|
||||
|
||||
/* could not send on, prepare to return */
|
||||
header->id = htons(forward->orig_id);
|
||||
forward->new_id = 0; /* cancel */
|
||||
@@ -304,75 +312,123 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
/* could not send on, return empty answer or address if known for whole domain */
|
||||
plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl);
|
||||
send_from(udpfd, options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr);
|
||||
|
||||
if (flags & (F_NOERR | F_NXDOMAIN))
|
||||
log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL);
|
||||
send_from(udpfd, options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
|
||||
return last_server;
|
||||
}
|
||||
|
||||
static int process_reply(HEADER *header, time_t now, char *dnamebuff, struct bogus_addr *bogus_nxdomain,
|
||||
struct doctor *doctors, union mysockaddr *serveraddr,
|
||||
int n, int options, unsigned short edns_pcktsz)
|
||||
{
|
||||
unsigned char *pheader;
|
||||
|
||||
/* If upstream is advertising a larger UDP packet size
|
||||
than we allow, trim it so that we don't get overlarge
|
||||
requests for the client. */
|
||||
|
||||
if ((pheader = find_pseudoheader(header, n)))
|
||||
{
|
||||
unsigned short udpsz;
|
||||
unsigned char *psave = pheader;
|
||||
|
||||
GETSHORT(udpsz, pheader);
|
||||
if (udpsz > edns_pcktsz)
|
||||
PUTSHORT(edns_pcktsz, psave);
|
||||
}
|
||||
|
||||
/* Complain loudly if the upstream server is non-recursive. */
|
||||
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
|
||||
{
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
#ifdef HAVE_IPV6
|
||||
if (serveraddr->sa.sa_family == AF_INET)
|
||||
inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
|
||||
else if (serveraddr->sa.sa_family == AF_INET6)
|
||||
inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
|
||||
#else
|
||||
strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
|
||||
#endif
|
||||
syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY)
|
||||
{
|
||||
if (!(bogus_nxdomain &&
|
||||
header->rcode == NOERROR &&
|
||||
check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
|
||||
{
|
||||
if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
|
||||
extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
|
||||
else if (!(options & OPT_NO_NEG))
|
||||
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* returns new last_server */
|
||||
struct server *reply_query(int fd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *last_server,
|
||||
struct bogus_addr *bogus_nxdomain, struct doctor *doctors)
|
||||
struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
|
||||
char *dnamebuff, struct server *servers, struct server *last_server,
|
||||
struct bogus_addr *bogus_nxdomain, struct doctor *doctors, unsigned short edns_pcktsz)
|
||||
{
|
||||
/* packet from peer server, extract data for cache, and send to
|
||||
original requester */
|
||||
struct frec *forward;
|
||||
HEADER *header;
|
||||
int n = recv(fd, packet, PACKETSZ, 0);
|
||||
union mysockaddr serveraddr;
|
||||
socklen_t addrlen = sizeof(serveraddr);
|
||||
int n = recvfrom(sfd->fd, packet, edns_pcktsz, 0, &serveraddr.sa, &addrlen);
|
||||
|
||||
/* Determine the address of the server replying so that we can mark that as good */
|
||||
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
|
||||
#ifdef HAVE_IPV6
|
||||
if (serveraddr.sa.sa_family == AF_INET6)
|
||||
serveraddr.in6.sin6_flowinfo = htonl(0);
|
||||
#endif
|
||||
|
||||
header = (HEADER *)packet;
|
||||
if (n >= (int)sizeof(HEADER) && header->qr)
|
||||
if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
|
||||
{
|
||||
if ((forward = lookup_frec(ntohs(header->id))))
|
||||
/* find good server by address if possible, otherwise assume the last one we sent to */
|
||||
if ((forward->sentto->flags & SERV_TYPE) == 0)
|
||||
{
|
||||
if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
|
||||
{
|
||||
if (!forward->sentto->domain)
|
||||
last_server = forward->sentto; /* known good */
|
||||
if (header->opcode == QUERY)
|
||||
{
|
||||
if (!(bogus_nxdomain &&
|
||||
header->rcode == NOERROR &&
|
||||
check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
|
||||
{
|
||||
if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
|
||||
extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
|
||||
else if (!(options & OPT_NO_NEG))
|
||||
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
header->id = htons(forward->orig_id);
|
||||
/* There's no point returning an upstream reply marked as truncated,
|
||||
since that will prod the resolver into moving to TCP - which we
|
||||
don't support. */
|
||||
header->tc = 0; /* goodbye truncate */
|
||||
send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
|
||||
forward->new_id = 0; /* cancel */
|
||||
for (last_server = servers; last_server; last_server = last_server->next)
|
||||
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
|
||||
sockaddr_isequal(&last_server->addr, &serveraddr))
|
||||
break;
|
||||
if (!last_server)
|
||||
last_server = forward->sentto;
|
||||
}
|
||||
|
||||
if (!process_reply(header, now, dnamebuff, bogus_nxdomain, doctors, &serveraddr, n, options, edns_pcktsz))
|
||||
return NULL;
|
||||
|
||||
header->id = htons(forward->orig_id);
|
||||
send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest, forward->iface);
|
||||
forward->new_id = 0; /* cancel */
|
||||
}
|
||||
|
||||
|
||||
return last_server;
|
||||
}
|
||||
|
||||
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
|
||||
struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now,
|
||||
unsigned long local_ttl, char *namebuff,
|
||||
struct iname *names, struct iname *addrs, struct iname *except,
|
||||
struct server *last_server, struct server *servers)
|
||||
struct server *last_server, struct server *servers, unsigned short edns_pcktsz)
|
||||
{
|
||||
HEADER *header = (HEADER *)packet;
|
||||
union mysockaddr source_addr;
|
||||
struct iname *tmp;
|
||||
struct all_addr dst_addr;
|
||||
int m, n, gotit = 0;
|
||||
int check_dst = !(options & OPT_NOWILD);
|
||||
int m, n, if_index = 0;
|
||||
struct iovec iov[1];
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmptr;
|
||||
char if_name[IF_NAMESIZE];
|
||||
union {
|
||||
struct cmsghdr align; /* this ensures alignment */
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -387,7 +443,7 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
|
||||
} control_u;
|
||||
|
||||
iov[0].iov_base = packet;
|
||||
iov[0].iov_len = PACKETSZ;
|
||||
iov[0].iov_len = edns_pcktsz;
|
||||
|
||||
msg.msg_control = control_u.control;
|
||||
msg.msg_controllen = sizeof(control_u);
|
||||
@@ -397,49 +453,48 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
n = recvmsg(listen->fd, &msg, 0);
|
||||
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
|
||||
return last_server;
|
||||
|
||||
source_addr.sa.sa_family = listen->family;
|
||||
#ifdef HAVE_IPV6
|
||||
if (listen->family == AF_INET6)
|
||||
source_addr.in6.sin6_flowinfo = htonl(0);
|
||||
{
|
||||
check_dst = 1;
|
||||
source_addr.in6.sin6_flowinfo = htonl(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(options & OPT_NOWILD) && msg.msg_controllen < sizeof(struct cmsghdr))
|
||||
if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
|
||||
return last_server;
|
||||
|
||||
#if defined(IP_PKTINFO)
|
||||
if (!(options & OPT_NOWILD) && listen->family == AF_INET)
|
||||
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_indextoname(((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex, if_name);
|
||||
gotit = 1;
|
||||
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
|
||||
}
|
||||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
|
||||
if (!(options & OPT_NOWILD) && listen->family == AF_INET)
|
||||
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));
|
||||
gotit = 1;
|
||||
}
|
||||
dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
|
||||
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
|
||||
if_indextoname(((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index, if_name);
|
||||
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (!(options & OPT_NOWILD) && listen->family == AF_INET6)
|
||||
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_indextoname(((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex, if_name);
|
||||
gotit = 1;
|
||||
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -448,19 +503,33 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
|
||||
return last_server;
|
||||
|
||||
/* enforce available interface configuration */
|
||||
if (!(options & OPT_NOWILD))
|
||||
if (check_dst)
|
||||
{
|
||||
if (!gotit)
|
||||
struct ifreq ifr;
|
||||
|
||||
if (if_index == 0)
|
||||
return last_server;
|
||||
|
||||
if (except || names)
|
||||
{
|
||||
#ifdef SIOCGIFNAME
|
||||
ifr.ifr_ifindex = if_index;
|
||||
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
|
||||
return last_server;
|
||||
#else
|
||||
if (!if_indextoname(if_index, ifr.ifr_name))
|
||||
return last_server;
|
||||
#endif
|
||||
}
|
||||
|
||||
for (tmp = except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, if_name) == 0))
|
||||
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
|
||||
return last_server;
|
||||
|
||||
if (names || addrs)
|
||||
{
|
||||
for (tmp = names; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, if_name) == 0))
|
||||
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
|
||||
break;
|
||||
if (!tmp)
|
||||
for (tmp = addrs; tmp; tmp = tmp->next)
|
||||
@@ -495,16 +564,204 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
|
||||
}
|
||||
|
||||
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
|
||||
mxname, mxtarget, options, now, local_ttl, namebuff);
|
||||
mxnames, mxtarget, options, now, local_ttl, namebuff, edns_pcktsz);
|
||||
if (m >= 1)
|
||||
send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr);
|
||||
send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
|
||||
else
|
||||
last_server = forward_query(listen->fd, &source_addr, &dst_addr,
|
||||
last_server = forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, n, options, namebuff, servers,
|
||||
last_server, now, local_ttl);
|
||||
return last_server;
|
||||
}
|
||||
|
||||
static int read_write(int fd, char *packet, int size, int rw)
|
||||
{
|
||||
int n, done;
|
||||
|
||||
for (done = 0; done < size; done += n)
|
||||
{
|
||||
retry:
|
||||
if (rw)
|
||||
n = read(fd, &packet[done], (size_t)(size - done));
|
||||
else
|
||||
n = write(fd, &packet[done], (size_t)(size - done));
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
else if (n == -1)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
goto retry;
|
||||
else if (errno == EAGAIN)
|
||||
{
|
||||
struct timespec waiter;
|
||||
waiter.tv_sec = 0;
|
||||
waiter.tv_nsec = 10000;
|
||||
nanosleep(&waiter, NULL);
|
||||
goto retry;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
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(int confd, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now,
|
||||
unsigned long local_ttl, char *namebuff,
|
||||
struct server *last_server, struct server *servers,
|
||||
struct bogus_addr *bogus_nxdomain, struct doctor *doctors,
|
||||
unsigned short edns_pktsz)
|
||||
{
|
||||
int size = 0, m;
|
||||
unsigned char c1, c2;
|
||||
/* Max TCP packet + slop */
|
||||
char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
|
||||
HEADER *header;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!packet ||
|
||||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
|
||||
!(size = c1 << 8 | c2) ||
|
||||
!read_write(confd, packet, size, 1))
|
||||
return packet;
|
||||
|
||||
if (size < (int)sizeof(HEADER))
|
||||
continue;
|
||||
|
||||
header = (HEADER *)packet;
|
||||
|
||||
if (extract_request(header, (unsigned int)size, namebuff))
|
||||
{
|
||||
union mysockaddr peer_addr;
|
||||
socklen_t peer_len = sizeof(union mysockaddr);
|
||||
|
||||
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
|
||||
{
|
||||
if (peer_addr.sa.sa_family == AF_INET)
|
||||
log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff,
|
||||
(struct all_addr *)&peer_addr.in.sin_addr);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff,
|
||||
(struct all_addr *)&peer_addr.in6.sin6_addr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* m > 0 if answered from cache */
|
||||
m = answer_request (header, ((char *) header) + 65536, (unsigned int)size,
|
||||
mxnames, mxtarget, options, now, local_ttl, namebuff, edns_pktsz);
|
||||
|
||||
if (m == 0)
|
||||
{
|
||||
unsigned short flags = 0;
|
||||
unsigned short gotname = extract_request(header, (unsigned int)size, namebuff);
|
||||
struct all_addr *addrp = NULL;
|
||||
int type = 0;
|
||||
char *domain = NULL;
|
||||
|
||||
if (gotname)
|
||||
flags = search_servers(servers, options, &addrp, gotname, namebuff, &type, &domain);
|
||||
|
||||
if (type != 0 || (options & OPT_ORDER) || !last_server)
|
||||
last_server = servers;
|
||||
|
||||
if (!flags && last_server)
|
||||
{
|
||||
struct server *firstsendto = NULL;
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one.
|
||||
Note that this code subtley ensures that consecutive queries on this connection
|
||||
which can go to the same server, do so. */
|
||||
while (1)
|
||||
{
|
||||
if (!firstsendto)
|
||||
firstsendto = last_server;
|
||||
else
|
||||
{
|
||||
if (!(last_server = last_server->next))
|
||||
last_server = servers;
|
||||
|
||||
if (last_server == firstsendto)
|
||||
break;
|
||||
}
|
||||
|
||||
/* server for wrong domain */
|
||||
if (type != (last_server->flags & SERV_TYPE) ||
|
||||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
|
||||
continue;
|
||||
|
||||
if ((last_server->tcpfd == -1) &&
|
||||
(last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
|
||||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
}
|
||||
|
||||
if (last_server->tcpfd == -1)
|
||||
continue;
|
||||
|
||||
c1 = size >> 8;
|
||||
c2 = size;
|
||||
|
||||
if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 0) ||
|
||||
!read_write(last_server->tcpfd, packet, size, 0) ||
|
||||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
|
||||
!read_write(last_server->tcpfd, &c2, 1, 1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
last_server->tcpfd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
m = (c1 << 8) | c2;
|
||||
if (!read_write(last_server->tcpfd, packet, m, 1))
|
||||
return packet;
|
||||
|
||||
if (!gotname)
|
||||
strcpy(namebuff, "query");
|
||||
if (last_server->addr.sa.sa_family == AF_INET)
|
||||
log_query(F_SERVER | F_IPV4 | F_FORWARD, namebuff,
|
||||
(struct all_addr *)&last_server->addr.in.sin_addr);
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
log_query(F_SERVER | F_IPV6 | F_FORWARD, namebuff,
|
||||
(struct all_addr *)&last_server->addr.in6.sin6_addr);
|
||||
#endif
|
||||
|
||||
/* There's no point in updating the cache, since this process will exit and
|
||||
lose the information after one query. We make this call for the alias and
|
||||
bogus-nxdomain side-effects. */
|
||||
process_reply(header, now, namebuff, bogus_nxdomain, doctors,
|
||||
&last_server->addr, m, options, edns_pktsz);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
m = setup_reply(header, (unsigned int)size, addrp, flags, local_ttl);
|
||||
}
|
||||
|
||||
c1 = m>>8;
|
||||
c2 = m;
|
||||
if (!read_write(confd, &c1, 1, 0) ||
|
||||
!read_write(confd, &c2, 1, 0) ||
|
||||
!read_write(confd, packet, m, 0))
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
static struct frec *get_new_frec(time_t now)
|
||||
{
|
||||
struct frec *f = frec_list, *oldest = NULL;
|
||||
@@ -571,8 +828,8 @@ static struct frec *lookup_frec(unsigned short id)
|
||||
static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
union mysockaddr *addr)
|
||||
{
|
||||
struct frec *f;
|
||||
|
||||
struct frec *f;
|
||||
|
||||
for(f = frec_list; f; f = f->next)
|
||||
if (f->new_id &&
|
||||
f->orig_id == id &&
|
||||
|
||||
249
src/isc.c
Normal file
249
src/isc.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2004 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
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
|
||||
/* Code in this file is based on contributions by John Volpe. */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_ISC_READER
|
||||
|
||||
struct isc_lease {
|
||||
char *name, *fqdn;
|
||||
time_t expires;
|
||||
struct in_addr addr;
|
||||
struct isc_lease *next;
|
||||
};
|
||||
|
||||
static struct isc_lease *leases = NULL;
|
||||
static off_t lease_file_size = (off_t)0;
|
||||
static ino_t lease_file_inode = (ino_t)0;
|
||||
static int logged_lease = 0;
|
||||
|
||||
static int next_token (char *token, int buffsize, FILE * fp)
|
||||
{
|
||||
int c, count = 0;
|
||||
char *cp = token;
|
||||
|
||||
while((c = getc(fp)) != EOF)
|
||||
{
|
||||
if (c == '#')
|
||||
do { c = getc(fp); } while (c != '\n' && c != EOF);
|
||||
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == ';')
|
||||
{
|
||||
if (count)
|
||||
break;
|
||||
}
|
||||
else if ((c != '"') && (count<buffsize-1))
|
||||
{
|
||||
*cp++ = c;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
*cp = 0;
|
||||
return count ? 1 : 0;
|
||||
}
|
||||
|
||||
void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
|
||||
{
|
||||
char token[MAXTOK], *dot;
|
||||
struct in_addr host_address;
|
||||
time_t ttd, tts;
|
||||
FILE *fp;
|
||||
struct isc_lease *lease, *tmp, **up;
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(file, &statbuf) == -1)
|
||||
{
|
||||
if (!logged_lease)
|
||||
syslog(LOG_WARNING, "failed to access %s: %m", file);
|
||||
logged_lease = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
logged_lease = 0;
|
||||
|
||||
if ((statbuf.st_size <= lease_file_size) &&
|
||||
(statbuf.st_ino == lease_file_inode))
|
||||
return;
|
||||
|
||||
lease_file_size = statbuf.st_size;
|
||||
lease_file_inode = statbuf.st_ino;
|
||||
|
||||
if (!(fp = fopen (file, "r")))
|
||||
{
|
||||
syslog (LOG_ERR, "failed to load %s: %m", file);
|
||||
return;
|
||||
}
|
||||
|
||||
syslog (LOG_INFO, "reading %s", file);
|
||||
|
||||
while ((next_token(token, MAXTOK, fp)))
|
||||
{
|
||||
if (strcmp(token, "lease") == 0)
|
||||
{
|
||||
hostname[0] = '\0';
|
||||
ttd = tts = (time_t)(-1);
|
||||
if (next_token(token, MAXTOK, fp) &&
|
||||
(host_address.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
{
|
||||
if (next_token(token, MAXTOK, fp) && *token == '{')
|
||||
{
|
||||
while (next_token(token, MAXTOK, fp) && *token != '}')
|
||||
{
|
||||
if ((strcmp(token, "client-hostname") == 0) ||
|
||||
(strcmp(token, "hostname") == 0))
|
||||
{
|
||||
if (next_token(hostname, MAXDNAME, fp))
|
||||
if (!canonicalise(hostname))
|
||||
{
|
||||
*hostname = 0;
|
||||
syslog(LOG_ERR, "bad name in %s", file);
|
||||
}
|
||||
}
|
||||
else if ((strcmp(token, "ends") == 0) ||
|
||||
(strcmp(token, "starts") == 0))
|
||||
{
|
||||
struct tm lease_time;
|
||||
int is_ends = (strcmp(token, "ends") == 0);
|
||||
if (next_token(token, MAXTOK, fp) && /* skip weekday */
|
||||
next_token(token, MAXTOK, fp) && /* Get date from lease file */
|
||||
sscanf (token, "%d/%d/%d",
|
||||
&lease_time.tm_year,
|
||||
&lease_time.tm_mon,
|
||||
&lease_time.tm_mday) == 3 &&
|
||||
next_token(token, MAXTOK, fp) &&
|
||||
sscanf (token, "%d:%d:%d:",
|
||||
&lease_time.tm_hour,
|
||||
&lease_time.tm_min,
|
||||
&lease_time.tm_sec) == 3)
|
||||
{
|
||||
/* There doesn't seem to be a universally available library function
|
||||
which converts broken-down _GMT_ time to seconds-in-epoch.
|
||||
The following was borrowed from ISC dhcpd sources, where
|
||||
it is noted that it might not be entirely accurate for odd seconds.
|
||||
Since we're trying to get the same answer as dhcpd, that's just
|
||||
fine here. */
|
||||
static int months [11] = { 31, 59, 90, 120, 151, 181,
|
||||
212, 243, 273, 304, 334 };
|
||||
time_t time = ((((((365 * (lease_time.tm_year - 1970) + /* Days in years since '70 */
|
||||
(lease_time.tm_year - 1969) / 4 + /* Leap days since '70 */
|
||||
(lease_time.tm_mon > 1 /* Days in months this year */
|
||||
? months [lease_time.tm_mon - 2]
|
||||
: 0) +
|
||||
(lease_time.tm_mon > 2 && /* Leap day this year */
|
||||
!((lease_time.tm_year - 1972) & 3)) +
|
||||
lease_time.tm_mday - 1) * 24) + /* Day of month */
|
||||
lease_time.tm_hour) * 60) +
|
||||
lease_time.tm_min) * 60) + lease_time.tm_sec;
|
||||
if (is_ends)
|
||||
ttd = time;
|
||||
else
|
||||
tts = time; }
|
||||
}
|
||||
}
|
||||
|
||||
/* missing info? */
|
||||
if (!*hostname)
|
||||
continue;
|
||||
if (ttd == (time_t)(-1))
|
||||
continue;
|
||||
|
||||
/* We use 0 as infinite in ttd */
|
||||
if ((tts != -1) && (ttd == tts - 1))
|
||||
ttd = (time_t)0;
|
||||
else if (difftime(now, ttd) > 0)
|
||||
continue;
|
||||
|
||||
if ((dot = strchr(hostname, '.')))
|
||||
{
|
||||
if (!suffix || hostname_isequal(dot+1, suffix))
|
||||
{
|
||||
syslog(LOG_WARNING,
|
||||
"Ignoring DHCP lease for %s because it has an illegal domain part",
|
||||
hostname);
|
||||
continue;
|
||||
}
|
||||
*dot = 0;
|
||||
}
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (hostname_isequal(lease->name, hostname))
|
||||
{
|
||||
lease->expires = ttd;
|
||||
lease->addr = host_address;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lease && (lease = malloc(sizeof(struct isc_lease))))
|
||||
{
|
||||
lease->expires = ttd;
|
||||
lease->addr = host_address;
|
||||
lease->fqdn = NULL;
|
||||
lease->next = leases;
|
||||
if (!(lease->name = malloc(strlen(hostname)+1)))
|
||||
free(lease);
|
||||
else
|
||||
{
|
||||
leases = lease;
|
||||
strcpy(lease->name, hostname);
|
||||
if (suffix && (lease->fqdn = malloc(strlen(hostname) + strlen(suffix) + 2)))
|
||||
{
|
||||
strcpy(lease->fqdn, hostname);
|
||||
strcat(lease->fqdn, ".");
|
||||
strcat(lease->fqdn, suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* prune expired leases */
|
||||
for (lease = leases, up = &leases; lease; lease = tmp)
|
||||
{
|
||||
tmp = lease->next;
|
||||
if (lease->expires != (time_t)0 && difftime(now, lease->expires) > 0)
|
||||
{
|
||||
*up = lease->next; /* unlink */
|
||||
free(lease->name);
|
||||
if (lease->fqdn)
|
||||
free(lease->fqdn);
|
||||
free(lease);
|
||||
}
|
||||
else
|
||||
up = &lease->next;
|
||||
}
|
||||
|
||||
|
||||
/* remove all existing DHCP cache entries */
|
||||
cache_unhash_dhcp();
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
if (lease->fqdn)
|
||||
{
|
||||
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires, F_REVERSE);
|
||||
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, 0);
|
||||
}
|
||||
else
|
||||
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, F_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
12
src/lease.c
12
src/lease.c
@@ -109,7 +109,7 @@ void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) &&
|
||||
(config->hostname))
|
||||
(config->flags & CONFIG_NAME))
|
||||
lease_set_hostname(lease, config->hostname, domain);
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ void lease_update_file(int force, time_t now)
|
||||
expires, lease->hwaddr[0], lease->hwaddr[1],
|
||||
lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
|
||||
lease->hwaddr[5], inet_ntoa(lease->addr),
|
||||
lease->hostname ? lease->hostname : "*");
|
||||
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
|
||||
|
||||
if (lease->clid_len)
|
||||
{
|
||||
@@ -220,9 +220,6 @@ void lease_prune(struct dhcp_lease *target, time_t now)
|
||||
|
||||
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
|
||||
{
|
||||
/* zero length means clid from hwaddr: never match am option clid to
|
||||
a hardware-address derived clid */
|
||||
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
if (clid_len)
|
||||
@@ -235,8 +232,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
|
||||
else
|
||||
{
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (!lease->clid &&
|
||||
memcmp(clid, lease->hwaddr, ETHER_ADDR_LEN) == 0)
|
||||
if (memcmp(clid, lease->hwaddr, ETHER_ADDR_LEN) == 0)
|
||||
return lease;
|
||||
}
|
||||
|
||||
@@ -311,7 +307,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
|
||||
struct dhcp_lease *lease_tmp;
|
||||
char *new_name = NULL, *new_fqdn = NULL;
|
||||
|
||||
if (lease->hostname && name && strcmp(lease->hostname, name) == 0)
|
||||
if (lease->hostname && name && hostname_isequal(lease->hostname, name))
|
||||
return;
|
||||
|
||||
if (!name && !lease->hostname)
|
||||
|
||||
283
src/network.c
283
src/network.c
@@ -25,44 +25,54 @@ static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *a
|
||||
if (except)
|
||||
for (tmp = except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && strcmp(tmp->name, name) == 0)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
/* record address of named interfaces, for TCP access control */
|
||||
tmp->addr = *addr;
|
||||
return list;
|
||||
}
|
||||
|
||||
/* we may need to check the whitelist */
|
||||
if (names || addrs)
|
||||
{
|
||||
int found = 0;
|
||||
|
||||
for (tmp = names; tmp; tmp = tmp->next)
|
||||
if (tmp->name && (strcmp(tmp->name, name) == 0))
|
||||
break;
|
||||
if (!tmp)
|
||||
for (tmp = addrs; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, addr))
|
||||
break;
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
{
|
||||
tmp->addr = *addr;
|
||||
found = tmp->used = 1;
|
||||
}
|
||||
|
||||
for (tmp = addrs; tmp; tmp = tmp->next)
|
||||
if (sockaddr_isequal(&tmp->addr, addr))
|
||||
found = tmp->used = 1;
|
||||
|
||||
if (!found)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* check whether the interface IP has been added already
|
||||
it is possible to have multiple interfaces with the same address */
|
||||
for (; list; list = list->next)
|
||||
if (sockaddr_isequal(&list->addr, addr))
|
||||
for (iface = list; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->addr, addr))
|
||||
break;
|
||||
if (list)
|
||||
return NULL;
|
||||
if (iface)
|
||||
return list;
|
||||
|
||||
/* If OK, add it to the head of the list */
|
||||
iface = safe_malloc(sizeof(struct irec));
|
||||
iface->addr = *addr;
|
||||
|
||||
iface->next = list;
|
||||
return iface;
|
||||
}
|
||||
|
||||
|
||||
struct irec *enumerate_interfaces(struct iname *names,
|
||||
struct iname *addrs,
|
||||
struct irec *enumerate_interfaces(struct iname **names,
|
||||
struct iname **addrs,
|
||||
struct iname *except,
|
||||
int port)
|
||||
{
|
||||
struct irec *iface = NULL, *new;
|
||||
struct irec *iface = NULL;
|
||||
char *buf, *ptr;
|
||||
struct ifreq *ifr = NULL;
|
||||
struct ifconf ifc;
|
||||
@@ -72,7 +82,7 @@ struct irec *enumerate_interfaces(struct iname *names,
|
||||
|
||||
if (fd == -1)
|
||||
die ("cannot create socket to enumerate interfaces: %s", NULL);
|
||||
|
||||
|
||||
while (1)
|
||||
{
|
||||
buf = safe_malloc(len);
|
||||
@@ -138,21 +148,27 @@ struct irec *enumerate_interfaces(struct iname *names,
|
||||
|
||||
/* If we are restricting the set of interfaces to use, make
|
||||
sure that loopback interfaces are in that set. */
|
||||
if (names && (ifr->ifr_flags & IFF_LOOPBACK))
|
||||
if (*names && (ifr->ifr_flags & IFF_LOOPBACK))
|
||||
{
|
||||
struct iname *lo = safe_malloc(sizeof(struct iname));
|
||||
lo->name = safe_string_alloc(ifr->ifr_name);
|
||||
lo->next = names->next;
|
||||
names->next = lo;
|
||||
}
|
||||
|
||||
if ((new = add_iface(iface, ifr->ifr_name,
|
||||
&addr, names, addrs, except)))
|
||||
{
|
||||
new->next = iface;
|
||||
iface = new;
|
||||
struct iname *lo;
|
||||
for (lo = *names; lo; lo = lo->next)
|
||||
if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0)
|
||||
{
|
||||
lo->isloop = 1;
|
||||
break;
|
||||
}
|
||||
if (!lo)
|
||||
{
|
||||
lo = safe_malloc(sizeof(struct iname));
|
||||
lo->name = safe_string_alloc(ifr->ifr_name);
|
||||
lo->isloop = lo->used = 1;
|
||||
lo->next = *names;
|
||||
*names = lo;
|
||||
}
|
||||
}
|
||||
|
||||
iface = add_iface(iface, ifr->ifr_name, &addr, *names, *addrs, except);
|
||||
|
||||
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
@@ -194,12 +210,8 @@ struct irec *enumerate_interfaces(struct iname *names,
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (found && (new = add_iface(iface, ifr->ifr_name,
|
||||
&addr6, names, addrs, except)))
|
||||
{
|
||||
new->next = iface;
|
||||
iface = new;
|
||||
}
|
||||
if (found)
|
||||
iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except);
|
||||
}
|
||||
#endif /* LINUX */
|
||||
}
|
||||
@@ -215,36 +227,14 @@ struct irec *enumerate_interfaces(struct iname *names,
|
||||
return iface;
|
||||
}
|
||||
|
||||
struct listener *create_wildcard_listeners(int port)
|
||||
#ifdef HAVE_IPV6
|
||||
static int create_ipv6_listener(struct listener **link, int port)
|
||||
{
|
||||
union mysockaddr addr;
|
||||
int tcpfd, fd, flags, save;
|
||||
struct listener *l;
|
||||
int opt = 1;
|
||||
struct listener *listen;
|
||||
#ifdef HAVE_IPV6
|
||||
int fd;
|
||||
#endif
|
||||
|
||||
addr.in.sin_family = AF_INET;
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = htons(port);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
listen = safe_malloc(sizeof(struct listener));
|
||||
if ((listen->fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
die("failed to create socket: %s", NULL);
|
||||
if (setsockopt(listen->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
#if defined(IP_PKTINFO)
|
||||
setsockopt(listen->fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
|
||||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
|
||||
setsockopt(listen->fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(listen->fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
|
||||
#endif
|
||||
bind(listen->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
|
||||
die("failed to bind socket: %s", NULL);
|
||||
listen->next = NULL;
|
||||
listen->family = AF_INET;
|
||||
#ifdef HAVE_IPV6
|
||||
addr.in6.sin6_family = AF_INET6;
|
||||
addr.in6.sin6_addr = in6addr_any;
|
||||
addr.in6.sin6_port = htons(port);
|
||||
@@ -252,53 +242,152 @@ struct listener *create_wildcard_listeners(int port)
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
|
||||
/* No error of the kernel doesn't support IPv6 */
|
||||
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
|
||||
{
|
||||
if (errno != EPROTONOSUPPORT &&
|
||||
errno != EAFNOSUPPORT &&
|
||||
errno != EINVAL)
|
||||
die("failed to create IPv6 socket: %s", NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
listen->next = safe_malloc(sizeof(struct listener));
|
||||
listen->next->fd = fd;
|
||||
listen->next->family = AF_INET6;
|
||||
listen->next->next = NULL;
|
||||
if (setsockopt(listen->next->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(listen->next->fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
|
||||
bind(listen->next->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
|
||||
die("failed to bind IPv6 socket: %s", NULL);
|
||||
}
|
||||
#endif
|
||||
return (errno == EPROTONOSUPPORT ||
|
||||
errno == EAFNOSUPPORT ||
|
||||
errno == EINVAL);
|
||||
|
||||
return listen;
|
||||
if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
save = errno;
|
||||
close(fd);
|
||||
errno = save;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
|
||||
(flags = fcntl(tcpfd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1 ||
|
||||
#else
|
||||
setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
|
||||
#endif
|
||||
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
|
||||
listen(tcpfd, 5) == -1 ||
|
||||
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
|
||||
{
|
||||
save = errno;
|
||||
close(fd);
|
||||
close(tcpfd);
|
||||
errno = save;
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = safe_malloc(sizeof(struct listener));
|
||||
l->fd = fd;
|
||||
l->tcpfd = tcpfd;
|
||||
l->family = AF_INET6;
|
||||
l->next = NULL;
|
||||
*link = l;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct listener *create_wildcard_listeners(int port)
|
||||
{
|
||||
#if !(defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
|
||||
return NULL;
|
||||
#else
|
||||
union mysockaddr addr;
|
||||
int opt = 1;
|
||||
struct listener *l, *l6 = NULL;
|
||||
int flags;
|
||||
int tcpfd, fd;
|
||||
|
||||
addr.in.sin_family = AF_INET;
|
||||
addr.in.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.in.sin_port = htons(port);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
return NULL;
|
||||
|
||||
if ((tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
|
||||
{
|
||||
close (fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
|
||||
listen(tcpfd, 5) == -1 ||
|
||||
(flags = fcntl(tcpfd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
|
||||
#ifdef HAVE_IPV6
|
||||
!create_ipv6_listener(&l6, port) ||
|
||||
#endif
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
#if defined(IP_PKTINFO)
|
||||
setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
|
||||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
|
||||
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
|
||||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
|
||||
#endif
|
||||
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
|
||||
{
|
||||
close(fd);
|
||||
close(tcpfd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l = safe_malloc(sizeof(struct listener));
|
||||
l->family = AF_INET;
|
||||
l->fd = fd;
|
||||
l->tcpfd = tcpfd;
|
||||
l->next = l6;
|
||||
|
||||
return l;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
struct listener *create_bound_listeners(struct irec *interfaces)
|
||||
struct listener *create_bound_listeners(struct irec *interfaces, int port)
|
||||
{
|
||||
|
||||
struct listener *listeners = NULL;
|
||||
struct irec *iface;
|
||||
int opt = 1;
|
||||
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)
|
||||
{
|
||||
struct listener *new = safe_malloc(sizeof(struct listener));
|
||||
new->family = iface->addr.sa.sa_family;
|
||||
new->next = listeners;
|
||||
listeners = new;
|
||||
if ((new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1)
|
||||
die("failed to create socket: %s", NULL);
|
||||
if (setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
|
||||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
|
||||
die("failed to bind socket: %s", NULL);
|
||||
}
|
||||
|
||||
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 ||
|
||||
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);
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
|
||||
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
|
||||
{
|
||||
struct serverfd *sfd;
|
||||
|
||||
@@ -452,7 +541,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
|
||||
|
||||
if (!token || strcmp(token, "nameserver") != 0)
|
||||
continue;
|
||||
if (!(token = strtok(NULL, " \t\n")))
|
||||
if (!(token = strtok(NULL, " \t\n\r")))
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
|
||||
529
src/option.c
529
src/option.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000 - 2004 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@ struct myoption {
|
||||
int val;
|
||||
};
|
||||
|
||||
#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:"
|
||||
#define OPTSTRING "ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:"
|
||||
|
||||
static struct myoption opts[] = {
|
||||
{"version", 0, 0, 'v'},
|
||||
@@ -70,6 +70,9 @@ static struct myoption opts[] = {
|
||||
{"bind-interfaces", 0, 0, 'z'},
|
||||
{"read-ethers", 0, 0, 'Z' },
|
||||
{"alias", 1, 0, 'V' },
|
||||
{"dhcp-vendorclass", 1, 0, 'U'},
|
||||
{"dhcp-userclass", 1, 0, 'j'},
|
||||
{"edns-packet-max", 1, 0, 'P'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -120,6 +123,7 @@ static char *usage =
|
||||
"-H, --addn-hosts=path Specify a hosts file to be read in addition to " HOSTSFILE ".\n"
|
||||
"-i, --interface=interface Specify interface(s) to listen on.\n"
|
||||
"-I, --except-interface=int Specify interface(s) NOT to listen on.\n"
|
||||
"-j, --dhcp-userclass=<id>,<class> Map DHCP user class to option set.\n"
|
||||
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
|
||||
"-L, --localmx Return MX records for local hosts.\n"
|
||||
"-m, --mx-host=host_name Specify the MX name to reply to.\n"
|
||||
@@ -129,6 +133,7 @@ static char *usage =
|
||||
"-o, --strict-order Use nameservers strictly in the order given in " RESOLVFILE ".\n"
|
||||
"-O, --dhcp-option=<optspec> Set extra options to be set to DHCP clients.\n"
|
||||
"-p, --port=number Specify port to listen for DNS requests on (defaults to 53).\n"
|
||||
"-P, --edns-packet-max=<size> Maximum supported UDP packet size for EDNS.0 (defaults to %d).\n"
|
||||
"-q, --log-queries Log queries.\n"
|
||||
"-Q, --query-port=number Force the originating port for upstream queries.\n"
|
||||
"-R, --no-resolv Do NOT read resolv.conf.\n"
|
||||
@@ -139,7 +144,8 @@ static char *usage =
|
||||
"-t, --mx-target=host_name Specify the host in an MX reply.\n"
|
||||
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
|
||||
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
|
||||
"-v, --version Display dnsmasq version.\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, --help Display this message.\n"
|
||||
"-x, --pid-file=path Specify path of PID file. (defaults to " RUNFILE ").\n"
|
||||
@@ -150,22 +156,21 @@ static char *usage =
|
||||
|
||||
|
||||
unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **resolv_files,
|
||||
char **mxname, char **mxtarget, char **lease_file,
|
||||
struct mx_record **mxnames, char **mxtarget, char **lease_file,
|
||||
char **username, char **groupname, char **domain_suffix, char **runfile,
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port,
|
||||
int *query_port, unsigned long *local_ttl, char **addn_hosts, struct dhcp_context **dhcp,
|
||||
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, char **dhcp_file,
|
||||
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, struct dhcp_vendor **dhcp_vendors, char **dhcp_file,
|
||||
char **dhcp_sname, struct in_addr *dhcp_next_server, int *dhcp_max,
|
||||
unsigned int *min_leasetime, struct doctor **doctors)
|
||||
unsigned int *min_leasetime, struct doctor **doctors, unsigned short *edns_pktsz)
|
||||
{
|
||||
int option = 0, i;
|
||||
unsigned int flags = 0;
|
||||
FILE *f = NULL;
|
||||
char *conffile = CONFFILE;
|
||||
FILE *file_save = NULL, *f = NULL;
|
||||
char *file_name_save = NULL, *conffile = CONFFILE;
|
||||
int conffile_set = 0;
|
||||
int lineno = 0;
|
||||
|
||||
int line_save = 0, lineno = 0;
|
||||
opterr = 0;
|
||||
|
||||
*min_leasetime = UINT_MAX;
|
||||
@@ -180,26 +185,38 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
#endif
|
||||
else
|
||||
{ /* f non-NULL, reading from conffile. */
|
||||
reread:
|
||||
if (!fgets(buff, MAXDNAME, f))
|
||||
{
|
||||
/* At end of file, all done */
|
||||
fclose(f);
|
||||
if (file_save)
|
||||
{
|
||||
/* may be nested */
|
||||
conffile = file_name_save;
|
||||
f = file_save;
|
||||
file_save = NULL;
|
||||
lineno = line_save;
|
||||
goto reread;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *p;
|
||||
int white;
|
||||
lineno++;
|
||||
/* dump comments */
|
||||
for (p = buff; *p; p++)
|
||||
if (*p == '#')
|
||||
*p = 0;
|
||||
for (white = 1, p = buff; *p; p++)
|
||||
if (white && *p == '#')
|
||||
{
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
white = isspace(*p);
|
||||
/* fgets gets end of line char too. */
|
||||
while (strlen(buff) > 0 &&
|
||||
(buff[strlen(buff)-1] == '\n' ||
|
||||
buff[strlen(buff)-1] == ' ' ||
|
||||
buff[strlen(buff)-1] == '\r' ||
|
||||
buff[strlen(buff)-1] == '\t'))
|
||||
while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
|
||||
buff[strlen(buff)-1] = 0;
|
||||
if (*buff == 0)
|
||||
continue;
|
||||
@@ -228,6 +245,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
{ /* end of command line args, start reading conffile. */
|
||||
if (!conffile)
|
||||
break; /* "confile=" option disables */
|
||||
fileopen:
|
||||
option = 0;
|
||||
if (!(f = fopen(conffile, "r")))
|
||||
{
|
||||
@@ -240,13 +258,16 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
|
||||
if (!f && option == 'w')
|
||||
{
|
||||
fprintf (stderr, usage, CACHESIZ, MAXLEASES);
|
||||
fprintf (stderr, usage, CACHESIZ, EDNS_PKTSZ, MAXLEASES);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!f && option == 'v')
|
||||
{
|
||||
fprintf(stderr, "dnsmasq version %s\n", VERSION);
|
||||
fprintf(stderr, "Dnsmasq version %s %s\n\n", VERSION, COPYRIGHT);
|
||||
fprintf(stderr, "This software comes with ABSOLUTELY NO WARRANTY.\n");
|
||||
fprintf(stderr, "Dnsmasq is free software, and you are welcome to redistribute it\n");
|
||||
fprintf(stderr, "under the terms of the GNU General Public License, version 2.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -275,9 +296,27 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
switch (option)
|
||||
{
|
||||
case 'C':
|
||||
if (!f)
|
||||
{
|
||||
conffile = safe_string_alloc(optarg);
|
||||
conffile_set = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* nest conffiles one deep */
|
||||
if (file_save)
|
||||
{
|
||||
sprintf(buff, "nested includes not allowed at line %d of %s ", lineno, conffile);
|
||||
complain(buff, NULL);
|
||||
continue;
|
||||
}
|
||||
file_name_save = conffile;
|
||||
file_save = f;
|
||||
line_save = lineno;
|
||||
conffile = safe_string_alloc(optarg);
|
||||
conffile_set = 1;
|
||||
break;
|
||||
lineno = 0;
|
||||
goto fileopen;
|
||||
|
||||
case 'x':
|
||||
*runfile = safe_string_alloc(optarg);
|
||||
@@ -312,11 +351,22 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
}
|
||||
|
||||
case 'm':
|
||||
if (!canonicalise(optarg))
|
||||
option = '?';
|
||||
else
|
||||
*mxname = safe_string_alloc(optarg);
|
||||
break;
|
||||
{
|
||||
char *comma = strchr(optarg, ',');
|
||||
if (comma)
|
||||
*(comma++) = 0;
|
||||
if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
|
||||
option = '?';
|
||||
else
|
||||
{
|
||||
struct mx_record *new = safe_malloc(sizeof(struct mx_record));
|
||||
new->next = *mxnames;
|
||||
*mxnames = new;
|
||||
new->mxname = safe_string_alloc(optarg);
|
||||
new->mxtarget = safe_string_alloc(comma); /* may be NULL */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 't':
|
||||
if (!canonicalise(optarg))
|
||||
@@ -337,7 +387,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (!canonicalise(optarg))
|
||||
if (strcmp (optarg, "#") == 0)
|
||||
flags |= OPT_RESOLV_DOMAIN;
|
||||
else if (!canonicalise(optarg))
|
||||
option = '?';
|
||||
else
|
||||
*domain_suffix = safe_string_alloc(optarg);
|
||||
@@ -359,6 +411,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
/* new->name may be NULL if someone does
|
||||
"interface=" to disable all interfaces except loop. */
|
||||
new->name = safe_string_alloc(optarg);
|
||||
new->isloop = new->used = 0;
|
||||
if (strchr(optarg, ':'))
|
||||
flags |= OPT_NOWILD;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -368,6 +423,8 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
new->next = *if_except;
|
||||
*if_except = new;
|
||||
new->name = safe_string_alloc(optarg);
|
||||
if (strchr(optarg, ':'))
|
||||
flags |= OPT_NOWILD;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -438,11 +495,15 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
optarg++;
|
||||
while ((end = strchr(optarg, '/')))
|
||||
{
|
||||
char *domain;
|
||||
char *domain = NULL;
|
||||
*end = 0;
|
||||
if (!canonicalise(optarg))
|
||||
/* # matches everything and becomes a zero length domain string */
|
||||
if (strcmp(optarg, "#") == 0)
|
||||
domain = "";
|
||||
else if (!canonicalise(optarg) && strlen(optarg) != 0)
|
||||
option = '?';
|
||||
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
|
||||
else
|
||||
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
|
||||
serv = safe_malloc(sizeof(struct server));
|
||||
serv->next = newlist;
|
||||
newlist = serv;
|
||||
@@ -491,14 +552,16 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
if ((portno = strchr(source+1, '#')))
|
||||
{
|
||||
*portno = 0;
|
||||
source_port = atoi(portno+1);
|
||||
if (!atoi_check(portno+1, &source_port))
|
||||
option = '?';
|
||||
}
|
||||
}
|
||||
|
||||
if ((portno = strchr(optarg, '#'))) /* is there a port no. */
|
||||
{
|
||||
*portno = 0;
|
||||
serv_port = atoi(portno+1);
|
||||
if (!atoi_check(portno+1, &serv_port))
|
||||
option = '?';
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
@@ -578,32 +641,55 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
|
||||
case 'c':
|
||||
{
|
||||
int size = atoi(optarg);
|
||||
/* zero is OK, and means no caching. */
|
||||
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
else if (size > 10000)
|
||||
size = 10000;
|
||||
|
||||
*cachesize = size;
|
||||
int size;
|
||||
if (!atoi_check(optarg, &size))
|
||||
option = '?';
|
||||
else
|
||||
{
|
||||
/* zero is OK, and means no caching. */
|
||||
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
else if (size > 10000)
|
||||
size = 10000;
|
||||
|
||||
*cachesize = size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
*port = atoi(optarg);
|
||||
if (!atoi_check(optarg, port))
|
||||
option = '?';
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
{
|
||||
int i;
|
||||
if (!atoi_check(optarg, &i))
|
||||
option = '?';
|
||||
*edns_pktsz = (unsigned short)i;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Q':
|
||||
*query_port = atoi(optarg);
|
||||
if (!atoi_check(optarg, query_port))
|
||||
option = '?';
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
*local_ttl = (unsigned long)atoi(optarg);
|
||||
break;
|
||||
{
|
||||
int ttl;
|
||||
if (!atoi_check(optarg, &ttl))
|
||||
option = '?';
|
||||
else
|
||||
*local_ttl = (unsigned long)ttl;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'X':
|
||||
*dhcp_max = atoi(optarg);
|
||||
if (!atoi_check(optarg, dhcp_max))
|
||||
option = '?';
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
@@ -613,10 +699,11 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
|
||||
|
||||
new->next = *dhcp;
|
||||
new->lease_time = DEFLEASE;
|
||||
new->lease_time = DEFLEASE;
|
||||
new->addr_epoch = 0;
|
||||
new->netmask.s_addr = 0;
|
||||
new->broadcast.s_addr = 0;
|
||||
new->netid = NULL;
|
||||
new->netid.net = NULL;
|
||||
|
||||
|
||||
for (cp = optarg; *cp; cp++)
|
||||
@@ -626,7 +713,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
if (*cp != ',' && (comma = strchr(optarg, ',')))
|
||||
{
|
||||
*comma = 0;
|
||||
new->netid = safe_string_alloc(optarg);
|
||||
new->netid.net = safe_string_alloc(optarg);
|
||||
a[0] = comma + 1;
|
||||
}
|
||||
else
|
||||
@@ -640,11 +727,15 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
*(a[k]++) = 0;
|
||||
}
|
||||
|
||||
if ((k < 2) ||
|
||||
((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
|
||||
((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
|
||||
if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1))
|
||||
option = '?';
|
||||
else if (strcmp(a[1], "static") == 0)
|
||||
new->end = new->start;
|
||||
else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
|
||||
if (option == '?')
|
||||
{
|
||||
option = '?';
|
||||
free(new);
|
||||
break;
|
||||
}
|
||||
@@ -684,36 +775,29 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
}
|
||||
|
||||
new->lease_time = atoi(a[leasepos]) * fac;
|
||||
if (new->lease_time < *min_leasetime)
|
||||
*min_leasetime = new->lease_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new->last = new->start;
|
||||
|
||||
if (new->lease_time < *min_leasetime)
|
||||
*min_leasetime = new->lease_time;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'G':
|
||||
{
|
||||
int j, k;
|
||||
char *a[4] = { NULL, NULL, NULL, NULL };
|
||||
char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
unsigned int e0, e1, e2, e3, e4, e5;
|
||||
struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
|
||||
struct in_addr in;
|
||||
|
||||
new->next = *dhcp_conf;
|
||||
|
||||
memset(new->hwaddr, 0, ETHER_ADDR_LEN);
|
||||
new->clid_len = 0;
|
||||
new->clid = NULL;
|
||||
new->hostname = NULL;
|
||||
new->addr.s_addr = 0;
|
||||
new->lease_time = 0;
|
||||
new->flags = 0;
|
||||
|
||||
|
||||
a[0] = optarg;
|
||||
for (k = 1; k < 4; k++)
|
||||
for (k = 1; k < 6; k++)
|
||||
{
|
||||
if (!(a[k] = strchr(a[k-1], ',')))
|
||||
break;
|
||||
@@ -721,37 +805,65 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
}
|
||||
|
||||
for(j = 0; j < k; j++)
|
||||
if (strchr(a[j], ':')) /* ethernet address or binary CLID */
|
||||
if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
|
||||
{
|
||||
char *arg = a[j];
|
||||
if ((arg[0] == 'i' || arg[0] == 'I') &&
|
||||
(arg[1] == 'd' || arg[1] == 'D') &&
|
||||
arg[2] == ':')
|
||||
{
|
||||
int s, len;
|
||||
arg += 3; /* dump id: */
|
||||
if (strchr(arg, ':'))
|
||||
{
|
||||
s = (strlen(arg)/3) + 1;
|
||||
/* decode in place */
|
||||
for (len = 0; len < s; len++)
|
||||
{
|
||||
if (arg[(len*3)+2] != ':')
|
||||
option = '?';
|
||||
arg[(len*3)+2] = 0;
|
||||
arg[len] = strtol(&arg[len*3], NULL, 16);
|
||||
}
|
||||
}
|
||||
if (arg[3] == '*')
|
||||
new->flags |= CONFIG_NOCLID;
|
||||
else
|
||||
len = strlen(arg);
|
||||
|
||||
new->clid_len = len;
|
||||
new->clid = safe_malloc(len);
|
||||
memcpy(new->clid, arg, len);
|
||||
{
|
||||
int len;
|
||||
arg += 3; /* dump id: */
|
||||
if (strchr(arg, ':'))
|
||||
{
|
||||
/* decode hex in place */
|
||||
char *p = arg, *q = arg, *r;
|
||||
while (*p)
|
||||
{
|
||||
for (r = p; *r && *r != ':'; r++);
|
||||
if (*r)
|
||||
{
|
||||
if (r != p)
|
||||
{
|
||||
*r = 0;
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
}
|
||||
p = r+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p)
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
len = q - arg;
|
||||
}
|
||||
else
|
||||
len = strlen(arg);
|
||||
|
||||
new->flags |= CONFIG_CLID;
|
||||
new->clid_len = len;
|
||||
new->clid = safe_malloc(len);
|
||||
memcpy(new->clid, arg, len);
|
||||
}
|
||||
}
|
||||
else if ((arg[0] == 'n' || arg[0] == 'N') &&
|
||||
(arg[1] == 'e' || arg[1] == 'E') &&
|
||||
(arg[2] == 't' || arg[3] == 'T') &&
|
||||
arg[3] == ':')
|
||||
{
|
||||
new->flags |= CONFIG_NETID;
|
||||
new->netid.net = safe_string_alloc(arg+4);
|
||||
}
|
||||
else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
|
||||
&e0, &e1, &e2, &e3, &e4, &e5) == 6)
|
||||
{
|
||||
new->flags |= CONFIG_HWADDR;
|
||||
new->hwaddr[0] = e0;
|
||||
new->hwaddr[1] = e1;
|
||||
new->hwaddr[2] = e2;
|
||||
@@ -763,7 +875,10 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
option = '?';
|
||||
}
|
||||
else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
|
||||
new->addr = in;
|
||||
{
|
||||
new->addr = in;
|
||||
new->flags |= CONFIG_ADDR;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *cp, *lastp = NULL, last = 0;
|
||||
@@ -798,22 +913,41 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
if (lastp)
|
||||
*lastp = last;
|
||||
if (strcmp(a[j], "infinite") == 0)
|
||||
new->lease_time = 0xffffffff;
|
||||
{
|
||||
new->lease_time = 0xffffffff;
|
||||
new->flags |= CONFIG_TIME;
|
||||
}
|
||||
else if (strcmp(a[j], "ignore") == 0)
|
||||
new->flags |= CONFIG_DISABLE;
|
||||
else
|
||||
new->hostname = safe_string_alloc(a[j]);
|
||||
{
|
||||
new->hostname = safe_string_alloc(a[j]);
|
||||
new->flags |= CONFIG_NAME;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new->lease_time = atoi(a[j]) * fac;
|
||||
if (new->lease_time < *min_leasetime)
|
||||
*min_leasetime = new->lease_time;
|
||||
new->lease_time = atoi(a[j]) * fac;
|
||||
new->flags |= CONFIG_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
if (option == '?')
|
||||
free(new);
|
||||
{
|
||||
if (new->flags & CONFIG_NAME)
|
||||
free(new->hostname);
|
||||
if (new->flags & CONFIG_CLID)
|
||||
free(new->clid);
|
||||
if (new->flags & CONFIG_NETID)
|
||||
free(new->netid.net);
|
||||
free(new);
|
||||
}
|
||||
else
|
||||
*dhcp_conf = new;
|
||||
{
|
||||
if ((new->flags & CONFIG_TIME) && new->lease_time < *min_leasetime)
|
||||
*min_leasetime = new->lease_time;
|
||||
*dhcp_conf = new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -821,7 +955,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
{
|
||||
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
|
||||
char *cp, *comma;
|
||||
int addrs, is_addr;
|
||||
int addrs, digs, is_addr, is_hex, is_dec;
|
||||
|
||||
new->next = *dhcp_opts;
|
||||
new->len = 0;
|
||||
@@ -848,61 +982,118 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
if ((new->opt = atoi(optarg)) == 0)
|
||||
{
|
||||
option = '?';
|
||||
if (new->netid)
|
||||
free(new->netid);
|
||||
free(new);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
*dhcp_opts = new;
|
||||
|
||||
if (!comma)
|
||||
{
|
||||
*dhcp_opts = new;
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for non-address list characters */
|
||||
for (addrs = 1, is_addr = 0, cp = comma+1; *cp; cp++)
|
||||
break;
|
||||
|
||||
/* characterise the value */
|
||||
is_addr = is_hex = is_dec = 1;
|
||||
addrs = digs = 1;
|
||||
for (cp = comma+1; *cp; cp++)
|
||||
if (*cp == ',')
|
||||
addrs++;
|
||||
else if (!(*cp == '.' || *cp == ' ' || (*cp >='0' && *cp <= '9')))
|
||||
break;
|
||||
{
|
||||
addrs++;
|
||||
is_dec = is_hex = 0;
|
||||
}
|
||||
else if (*cp == ':')
|
||||
{
|
||||
digs++;
|
||||
is_dec = is_addr = 0;
|
||||
}
|
||||
else if (*cp == '.')
|
||||
is_addr = 1;
|
||||
|
||||
if (*cp)
|
||||
is_dec = is_hex = 0;
|
||||
else if (!(*cp >='0' && *cp <= '9'))
|
||||
{
|
||||
is_dec = is_addr = 0;
|
||||
if (!((*cp >='A' && *cp <= 'F') ||
|
||||
(*cp >='a' && *cp <= 'f')))
|
||||
is_hex = 0;
|
||||
}
|
||||
|
||||
if (is_hex && digs > 1)
|
||||
{
|
||||
char *p = comma+1, *q, *r;
|
||||
new->len = digs;
|
||||
q = new->val = safe_malloc(new->len);
|
||||
while (*p)
|
||||
{
|
||||
for (r = p; *r && *r != ':'; r++);
|
||||
if (*r)
|
||||
{
|
||||
if (r != p)
|
||||
{
|
||||
*r = 0;
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
}
|
||||
p = r+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p)
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (is_dec)
|
||||
{
|
||||
/* Given that we don't know the length,
|
||||
this applaing hack is the best available */
|
||||
unsigned int val = atoi(comma+1);
|
||||
if (val < 256)
|
||||
{
|
||||
new->len = 1;
|
||||
new->val = safe_malloc(1);
|
||||
*(new->val) = val;
|
||||
}
|
||||
else if (val < 65536)
|
||||
{
|
||||
new->len = 2;
|
||||
new->val = safe_malloc(2);
|
||||
*(new->val) = val>>8;
|
||||
*(new->val+1) = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (is_addr)
|
||||
{
|
||||
struct in_addr in;
|
||||
unsigned char *op;
|
||||
new->len = INADDRSZ * addrs;
|
||||
new->val = op = safe_malloc(new->len);
|
||||
new->is_addr = 1;
|
||||
while (addrs--)
|
||||
{
|
||||
cp = comma;
|
||||
if ((comma = strchr(cp+1, ',')))
|
||||
*comma = 0;
|
||||
in.s_addr = inet_addr(cp+1);
|
||||
memcpy(op, &in, INADDRSZ);
|
||||
op += INADDRSZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* text arg */
|
||||
new->len = strlen(comma+1);
|
||||
new->val = safe_malloc(new->len);
|
||||
memcpy(new->val, comma+1, new->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in_addr in;
|
||||
unsigned char *op;
|
||||
|
||||
if (addrs == 1 && !is_addr)
|
||||
{
|
||||
new->len = 1;
|
||||
new->val = safe_malloc(1);
|
||||
*(new->val) = atoi(comma+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
new->len = INADDRSZ * addrs;
|
||||
new->val = op = safe_malloc(new->len);
|
||||
new->is_addr = 1;
|
||||
while (addrs--)
|
||||
{
|
||||
cp = comma;
|
||||
if (cp && (comma = strchr(cp+1, ',')))
|
||||
*comma = 0;
|
||||
if (cp && (in.s_addr = inet_addr(cp+1)) == (in_addr_t)-1)
|
||||
option = '?';
|
||||
memcpy(op, &in, INADDRSZ);
|
||||
op += INADDRSZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
*dhcp_opts = new;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -925,6 +1116,28 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
break;
|
||||
}
|
||||
|
||||
case 'U':
|
||||
case 'j':
|
||||
{
|
||||
char *comma;
|
||||
|
||||
if (!(comma = strchr(optarg, ',')))
|
||||
option = '?';
|
||||
else
|
||||
{
|
||||
struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor));
|
||||
*comma = 0;
|
||||
new->netid.net = safe_string_alloc(optarg);
|
||||
new->len = strlen(comma+1);
|
||||
new->data = safe_malloc(new->len);
|
||||
memcpy(new->data, comma+1, new->len);
|
||||
new->is_vendor = (option == 'U');
|
||||
new->next = *dhcp_vendors;
|
||||
*dhcp_vendors = new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'V':
|
||||
{
|
||||
char *a[3] = { NULL, NULL, NULL };
|
||||
@@ -1007,13 +1220,18 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
|
||||
/* only one of these need be specified: the other defaults to the
|
||||
host-name */
|
||||
if ((flags & OPT_LOCALMX) || *mxname || *mxtarget)
|
||||
if ((flags & OPT_LOCALMX) || *mxnames || *mxtarget)
|
||||
{
|
||||
if (gethostname(buff, MAXDNAME) == -1)
|
||||
die("cannot get host-name: %s", NULL);
|
||||
|
||||
if (!*mxname)
|
||||
*mxname = safe_string_alloc(buff);
|
||||
if (!*mxnames)
|
||||
{
|
||||
*mxnames = safe_malloc(sizeof(struct mx_record));
|
||||
(*mxnames)->next = NULL;
|
||||
(*mxnames)->mxtarget = NULL;
|
||||
(*mxnames)->mxname = safe_string_alloc(buff);
|
||||
}
|
||||
|
||||
if (!*mxtarget)
|
||||
*mxtarget = safe_string_alloc(buff);
|
||||
@@ -1024,6 +1242,35 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
else if (*resolv_files && (*resolv_files)->next && (flags & OPT_NO_POLL))
|
||||
die("only one resolv.conf file allowed in no-poll mode.", NULL);
|
||||
|
||||
if (flags & OPT_RESOLV_DOMAIN)
|
||||
{
|
||||
char *line;
|
||||
|
||||
if (!*resolv_files || (*resolv_files)->next)
|
||||
die("must have exactly one resolv.conf to read domain from.", NULL);
|
||||
|
||||
if (!(f = fopen((*resolv_files)->name, "r")))
|
||||
die("failed to read %s: %m", (*resolv_files)->name);
|
||||
|
||||
while ((line = fgets(buff, MAXDNAME, f)))
|
||||
{
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
|
||||
if (!token || strcmp(token, "search") != 0)
|
||||
continue;
|
||||
|
||||
if ((token = strtok(NULL, " \t\n\r")) &&
|
||||
canonicalise(token) &&
|
||||
(*domain_suffix = safe_string_alloc(token)))
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
if (!*domain_suffix)
|
||||
die("no search directive found in %s", (*resolv_files)->name);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
554
src/rfc1035.c
554
src/rfc1035.c
@@ -244,52 +244,60 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char *skip_name(unsigned char *ansp, HEADER *header, unsigned int plen)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
unsigned int label_type = (*ansp) & 0xc0;
|
||||
|
||||
if ((unsigned int)(ansp - (unsigned char *)header) >= plen)
|
||||
return NULL;
|
||||
|
||||
if (label_type == 0xc0)
|
||||
{
|
||||
/* pointer for compression. */
|
||||
ansp += 2;
|
||||
break;
|
||||
}
|
||||
else if (label_type == 0x80)
|
||||
return NULL; /* reserved */
|
||||
else if (label_type == 0x40)
|
||||
{
|
||||
/* Extended label type */
|
||||
unsigned int count;
|
||||
|
||||
if (((*ansp++) & 0x3f) != 1)
|
||||
return NULL; /* we only understand bitstrings */
|
||||
|
||||
count = *(ansp++); /* Bits in bitstring */
|
||||
|
||||
if (count == 0) /* count == 0 means 256 bits */
|
||||
ansp += 32;
|
||||
else
|
||||
ansp += ((count-1)>>3)+1;
|
||||
}
|
||||
else
|
||||
{ /* label type == 0 Bottom six bits is length */
|
||||
unsigned int len = (*ansp++) & 0x3f;
|
||||
if (len == 0)
|
||||
break; /* zero length label marks the end. */
|
||||
|
||||
ansp += len;
|
||||
}
|
||||
}
|
||||
|
||||
return ansp;
|
||||
}
|
||||
|
||||
static unsigned char *skip_questions(HEADER *header, unsigned int plen)
|
||||
{
|
||||
int q, qdcount = ntohs(header->qdcount);
|
||||
unsigned char *ansp = (unsigned char *)(header+1);
|
||||
|
||||
for (q=0; q<qdcount; q++)
|
||||
for (q = 0; q<qdcount; q++)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
unsigned int label_type = (*ansp) & 0xc0;
|
||||
|
||||
if ((unsigned int)(ansp - (unsigned char *)header) >= plen)
|
||||
return NULL;
|
||||
|
||||
if (label_type == 0xc0)
|
||||
{
|
||||
/* pointer for compression. */
|
||||
ansp += 2;
|
||||
break;
|
||||
}
|
||||
else if (label_type == 0x80)
|
||||
return NULL; /* reserved */
|
||||
else if (label_type == 0x40)
|
||||
{
|
||||
/* Extended label type */
|
||||
unsigned int count;
|
||||
|
||||
if (((*ansp++) & 0x3f) != 1)
|
||||
return NULL; /* we only understand bitstrings */
|
||||
|
||||
count = *(ansp++); /* Bits in bitstring */
|
||||
|
||||
if (count == 0) /* count == 0 means 256 bits */
|
||||
ansp += 32;
|
||||
else
|
||||
ansp += ((count-1)>>3)+1;
|
||||
}
|
||||
else
|
||||
{ /* label type == 0 Bottom six bits is length */
|
||||
unsigned int len = (*ansp++) & 0x3f;
|
||||
if (len == 0)
|
||||
break; /* zero length label marks the end. */
|
||||
|
||||
ansp += len;
|
||||
}
|
||||
}
|
||||
if (!(ansp = skip_name(ansp, header, plen)))
|
||||
return NULL;
|
||||
ansp += 4; /* class and type */
|
||||
}
|
||||
if ((unsigned int)(ansp - (unsigned char *)header) > plen)
|
||||
@@ -298,6 +306,49 @@ static unsigned char *skip_questions(HEADER *header, unsigned int plen)
|
||||
return ansp;
|
||||
}
|
||||
|
||||
unsigned char *find_pseudoheader(HEADER *header, unsigned int plen)
|
||||
{
|
||||
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. */
|
||||
|
||||
int i, arcount = ntohs(header->arcount);
|
||||
unsigned char *ansp;
|
||||
unsigned short rdlen, type;
|
||||
|
||||
if (arcount == 0 || !(ansp = skip_questions(header, plen)))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < (ntohs(header->ancount) + ntohs(header->nscount)); i++)
|
||||
{
|
||||
if (!(ansp = skip_name(ansp, header, plen)))
|
||||
return NULL;
|
||||
ansp += 8; /* type, class, TTL */
|
||||
GETSHORT(rdlen, ansp);
|
||||
if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen)
|
||||
return NULL;
|
||||
ansp += rdlen;
|
||||
}
|
||||
|
||||
for (i = 0; i < arcount; i++)
|
||||
{
|
||||
unsigned char *save;
|
||||
if (!(ansp = skip_name(ansp, header, plen)))
|
||||
return NULL;
|
||||
|
||||
GETSHORT(type, ansp);
|
||||
save = ansp;
|
||||
ansp += 6; /* class, TTL */
|
||||
GETSHORT(rdlen, ansp);
|
||||
if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen)
|
||||
return NULL;
|
||||
if (type == ns_t_opt)
|
||||
return save;
|
||||
ansp += rdlen;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* is addr in the non-globally-routed IP space? */
|
||||
static int private_net(struct all_addr *addrp)
|
||||
{
|
||||
@@ -440,13 +491,16 @@ void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now
|
||||
cache_end_insert();
|
||||
}
|
||||
|
||||
static void dns_doctor(struct doctor *doctor, struct in_addr *addr)
|
||||
static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
|
||||
{
|
||||
for (; doctor; doctor = doctor->next)
|
||||
if ((doctor->in.s_addr & doctor->mask.s_addr) == (addr->s_addr & doctor->mask.s_addr))
|
||||
if (is_same_net(doctor->in, *addr, doctor->mask))
|
||||
{
|
||||
addr->s_addr &= ~doctor->mask.s_addr;
|
||||
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
|
||||
/* Since we munged the data, the server it came from is no longer authoritative */
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -490,7 +544,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name,
|
||||
|
||||
if (qtype == T_A) /* A record. */
|
||||
{
|
||||
dns_doctor(doctors, (struct in_addr *)p);
|
||||
dns_doctor(header, doctors, (struct in_addr *)p);
|
||||
cache_insert(name, (struct all_addr *)p, now,
|
||||
ttl, F_IPV4 | F_FORWARD);
|
||||
}
|
||||
@@ -562,7 +616,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name,
|
||||
|
||||
if (qtype == T_A) /* A record. */
|
||||
{
|
||||
dns_doctor(doctors, (struct in_addr *)p);
|
||||
dns_doctor(header, doctors, (struct in_addr *)p);
|
||||
cache_insert(name, (struct all_addr *)p, now,
|
||||
cttl, F_IPV4 | F_FORWARD);
|
||||
}
|
||||
@@ -635,7 +689,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
|
||||
header->tc = 0; /* not truncated */
|
||||
header->nscount = htons(0);
|
||||
header->arcount = htons(0);
|
||||
header->ancount = htons(0); /* no answers unless changed below*/
|
||||
header->ancount = htons(0); /* no answers unless changed below */
|
||||
if (flags == F_NEG)
|
||||
header->rcode = SERVFAIL; /* couldn't get memory */
|
||||
else if (flags == F_NOERR || flags == F_QUERY)
|
||||
@@ -728,29 +782,60 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
|
||||
}
|
||||
|
||||
/* return zero if we can't answer from cache, or packet size if we can */
|
||||
int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
|
||||
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
|
||||
char *mxtarget, unsigned int options, time_t now,
|
||||
unsigned long local_ttl, char *name)
|
||||
unsigned long local_ttl, char *name, unsigned short edns_pcktsz)
|
||||
{
|
||||
unsigned char *p, *ansp;
|
||||
unsigned char *p, *ansp, *pheader;
|
||||
int qtype, qclass, is_arpa;
|
||||
struct all_addr addr;
|
||||
unsigned int nameoffset;
|
||||
int q, qdcount = ntohs(header->qdcount);
|
||||
int ans, anscount = 0;
|
||||
unsigned short flag;
|
||||
int qdcount = ntohs(header->qdcount);
|
||||
int q, ans, anscount;
|
||||
int dryrun = 0, sec_reqd = 0;
|
||||
struct crec *crecp;
|
||||
int nxdomain = 0, auth = 1;
|
||||
int nxdomain, auth;
|
||||
|
||||
if (!qdcount || header->opcode != QUERY )
|
||||
return 0;
|
||||
|
||||
/* If there is an RFC2671 pseudoheader then it will be overwritten by
|
||||
partial replies, so we have to do a dry run to see if we can answer
|
||||
the query. We check to see if the do bit is set, if so we always
|
||||
forward rather than answering from the cache, which doesn't include
|
||||
security information. */
|
||||
|
||||
if ((pheader = find_pseudoheader(header, qlen)))
|
||||
{
|
||||
unsigned short udpsz, ext_rcode, flags;
|
||||
unsigned char *psave = pheader;
|
||||
|
||||
GETSHORT(udpsz, pheader);
|
||||
GETSHORT(ext_rcode, pheader);
|
||||
GETSHORT(flags, pheader);
|
||||
|
||||
sec_reqd = flags & 0x8000; /* do bit */
|
||||
|
||||
/* If our client is advertising a larger UDP packet size
|
||||
than we allow, trim it so that we don't get an overlarge
|
||||
response from upstream */
|
||||
|
||||
if (udpsz > edns_pcktsz)
|
||||
PUTSHORT(edns_pcktsz, psave);
|
||||
|
||||
dryrun = 1;
|
||||
}
|
||||
|
||||
rerun:
|
||||
/* determine end of question section (we put answers there) */
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
/* 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++)
|
||||
{
|
||||
/* save pointer to name for copying into answers */
|
||||
@@ -769,16 +854,19 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
|
||||
|
||||
ans = 0; /* have we answered this question */
|
||||
|
||||
if (qclass == C_CHAOS)
|
||||
if (qclass == C_CHAOS && qtype == T_TXT)
|
||||
/* special query to get version. */
|
||||
{
|
||||
if (qtype == T_TXT)
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
int len;
|
||||
if (hostname_isequal(name, "version.bind"))
|
||||
sprintf(name, "dnsmasq-%s", VERSION);
|
||||
else if (hostname_isequal(name, "authors.bind"))
|
||||
sprintf(name, "Simon Kelley");
|
||||
else if (hostname_isequal(name, "copyright.bind"))
|
||||
sprintf(name, COPYRIGHT);
|
||||
else
|
||||
*name = 0;
|
||||
len = strlen(name);
|
||||
@@ -790,230 +878,192 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
|
||||
*ansp++ = len;
|
||||
memcpy(ansp, name, len);
|
||||
ansp += len;
|
||||
ans = 1;
|
||||
anscount++;
|
||||
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else if (qclass != C_IN)
|
||||
return 0; /* we can't answer non-inet queries */
|
||||
else
|
||||
}
|
||||
else if (qclass == C_IN)
|
||||
{
|
||||
|
||||
if ((options & OPT_FILTER) && (qtype == T_SOA || qtype == T_SRV))
|
||||
if ((options & OPT_FILTER) &&
|
||||
(qtype == T_SOA || qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
|
||||
ans = 1;
|
||||
|
||||
if (qtype == T_PTR || qtype == T_ANY)
|
||||
else
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)))
|
||||
{
|
||||
unsigned long ttl;
|
||||
/* Return 0 ttl for DHCP entries, which might change
|
||||
before the lease expires. */
|
||||
if (crecp->flags & (F_IMMORTAL | F_DHCP))
|
||||
ttl = local_ttl;
|
||||
else
|
||||
ttl = crecp->ttd - now;
|
||||
if (qtype == T_PTR || qtype == T_ANY)
|
||||
{
|
||||
if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
|
||||
{
|
||||
if (is_arpa == F_IPV4 && (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);
|
||||
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);
|
||||
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 = local_ttl;
|
||||
else
|
||||
ttl = crecp->ttd - now;
|
||||
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
|
||||
ansp = add_text_record(nameoffset, ansp, ttl, 0, T_PTR,
|
||||
cache_get_name(crecp));
|
||||
|
||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr);
|
||||
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;
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or dhcp leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
return 0;
|
||||
|
||||
ans = 1;
|
||||
if (crecp->flags & F_NEG)
|
||||
if (flag == F_IPV6)
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr);
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
ansp = add_text_record(nameoffset, ansp, ttl, 0, T_PTR,
|
||||
cache_get_name(crecp));
|
||||
|
||||
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr);
|
||||
anscount++;
|
||||
|
||||
/* if last answer exceeded packet size, give up */
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* if not in cache, enabled and private IPV4 address, fake up answer */
|
||||
if (ans == 0 && is_arpa == F_IPV4 &&
|
||||
(options & OPT_BOGUSPRIV) &&
|
||||
private_net(&addr))
|
||||
{
|
||||
struct in_addr addr4 = *((struct in_addr *)&addr);
|
||||
ansp = add_text_record(nameoffset, ansp, local_ttl, 0, T_PTR, inet_ntoa(addr4));
|
||||
log_query(F_CONFIG | F_REVERSE | F_IPV4, inet_ntoa(addr4), &addr);
|
||||
anscount++;
|
||||
ans = 1;
|
||||
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (qtype == T_A || qtype == T_ANY)
|
||||
{
|
||||
/* T_ANY queries for hostnames with underscores are spam
|
||||
from win2k - don't forward them. */
|
||||
if ((options & OPT_FILTER) &&
|
||||
qtype == T_ANY &&
|
||||
(strchr(name, '_') != NULL))
|
||||
ans = 1;
|
||||
else
|
||||
{
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
|
||||
{
|
||||
unsigned long ttl;
|
||||
if (crecp->flags & (F_IMMORTAL | F_DHCP))
|
||||
ttl = local_ttl;
|
||||
else
|
||||
ttl = crecp->ttd - now;
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
return 0;
|
||||
|
||||
/* If we have negative cache entry, it's OK
|
||||
to return no answer. */
|
||||
ans = 1;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL);
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr);
|
||||
|
||||
/* copy question as first part of answer (use compression) */
|
||||
PUTSHORT(nameoffset | 0xc000, ansp);
|
||||
PUTSHORT(T_A, ansp);
|
||||
PUTSHORT(C_IN, ansp);
|
||||
PUTLONG(ttl, ansp); /* TTL */
|
||||
|
||||
PUTSHORT(INADDRSZ, ansp);
|
||||
memcpy(ansp, &crecp->addr, INADDRSZ);
|
||||
ansp += INADDRSZ;
|
||||
anscount++;
|
||||
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPV6
|
||||
if (qtype == T_AAAA || qtype == T_ANY)
|
||||
{
|
||||
/* T_ANY queries for hostnames with underscores are spam
|
||||
from win2k - don't forward them. */
|
||||
if ((options & OPT_FILTER) &&
|
||||
qtype == T_ANY
|
||||
&& (strchr(name, '_') != NULL))
|
||||
ans = 1;
|
||||
else
|
||||
{
|
||||
type = T_AAAA;
|
||||
addrsz = IN6ADDRSZ;
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (qtype != type && qtype != T_ANY)
|
||||
continue;
|
||||
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV6)))
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, flag)))
|
||||
{
|
||||
unsigned long ttl;
|
||||
if (crecp->flags & (F_IMMORTAL | F_DHCP))
|
||||
ttl = local_ttl;
|
||||
else
|
||||
ttl = crecp->ttd - now;
|
||||
|
||||
/* don't answer wildcard queries with data not from /etc/hosts
|
||||
or DHCP leases */
|
||||
if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
return 0;
|
||||
|
||||
/* If we have negative cache entry, it's OK
|
||||
to return no answer. */
|
||||
ans = 1;
|
||||
continue;
|
||||
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL);
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(crecp->flags, name, NULL);
|
||||
auth = 0;
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
|
||||
{
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr);
|
||||
|
||||
/* copy question as first part of answer (use compression) */
|
||||
PUTSHORT(nameoffset | 0xc000, ansp);
|
||||
PUTSHORT(T_AAAA, ansp);
|
||||
PUTSHORT(C_IN, ansp);
|
||||
PUTLONG(ttl, ansp); /* TTL */
|
||||
|
||||
PUTSHORT(IN6ADDRSZ, ansp);
|
||||
memcpy(ansp, &crecp->addr, IN6ADDRSZ);
|
||||
ansp += IN6ADDRSZ;
|
||||
anscount++;
|
||||
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
unsigned long ttl;
|
||||
|
||||
if (crecp->flags & (F_IMMORTAL | F_DHCP))
|
||||
ttl = local_ttl;
|
||||
else
|
||||
ttl = crecp->ttd - now;
|
||||
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
auth = 0;
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qtype == T_MX || qtype == T_ANY)
|
||||
{
|
||||
if (mxname && hostname_isequal(name, mxname))
|
||||
{
|
||||
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX, mxtarget);
|
||||
anscount++;
|
||||
ans = 1;
|
||||
}
|
||||
else if ((options & (OPT_SELFMX | OPT_LOCALMX)) &&
|
||||
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
|
||||
{
|
||||
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX,
|
||||
(options & OPT_SELFMX) ? name : mxtarget);
|
||||
anscount++;
|
||||
ans = 1;
|
||||
}
|
||||
if (((unsigned char *)limit - ansp) < 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (qtype == T_MAILB)
|
||||
ans = 1, nxdomain = 1;
|
||||
|
||||
if (qtype == T_MX || qtype == T_ANY)
|
||||
{
|
||||
struct mx_record *mx;
|
||||
for (mx = mxnames; mx; mx = mx->next)
|
||||
if (hostname_isequal(name, mx->mxname))
|
||||
break;
|
||||
if (mx)
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX,
|
||||
mx->mxtarget ? mx->mxtarget : mxtarget);
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
else if ((options & (OPT_SELFMX | OPT_LOCALMX)) &&
|
||||
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
|
||||
{
|
||||
ans = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX,
|
||||
(options & OPT_SELFMX) ? name : mxtarget);
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (qtype == T_MAILB)
|
||||
ans = 1, nxdomain = 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!ans)
|
||||
if (!ans || ((unsigned char *)limit - ansp) < 0)
|
||||
return 0; /* failed to answer a question */
|
||||
}
|
||||
|
||||
if (dryrun)
|
||||
{
|
||||
dryrun = 0;
|
||||
goto rerun;
|
||||
}
|
||||
|
||||
/* done all questions, set up header and return length of result */
|
||||
|
||||
428
src/rfc2131.c
428
src/rfc2131.c
@@ -18,6 +18,10 @@
|
||||
#define BOOTREPLY 2
|
||||
#define DHCP_COOKIE 0x63825363
|
||||
|
||||
/* The Linux in-kernel DHCP client silently ignores any packet
|
||||
smaller than this. Sigh........... */
|
||||
#define MIN_PACKETSZ 300
|
||||
|
||||
#define OPTION_PAD 0
|
||||
#define OPTION_NETMASK 1
|
||||
#define OPTION_ROUTER 3
|
||||
@@ -35,7 +39,9 @@
|
||||
#define OPTION_MAXMESSAGE 57
|
||||
#define OPTION_T1 58
|
||||
#define OPTION_T2 59
|
||||
#define OPTION_VENDOR_ID 60
|
||||
#define OPTION_CLIENT_ID 61
|
||||
#define OPTION_USER_CLASS 77
|
||||
#define OPTION_END 255
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
@@ -48,6 +54,7 @@
|
||||
#define DHCPINFORM 8
|
||||
|
||||
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
|
||||
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
|
||||
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
|
||||
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname);
|
||||
static int option_len(unsigned char *opt);
|
||||
@@ -61,10 +68,14 @@ static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
unsigned char *req_options,
|
||||
struct dhcp_opt *config_opts,
|
||||
char *domainname, char *hostname,
|
||||
struct in_addr relay,
|
||||
struct in_addr router,
|
||||
struct in_addr iface_addr,
|
||||
int iface_mtu);
|
||||
int iface_mtu, struct dhcp_netid *netid);
|
||||
|
||||
static int have_config(struct dhcp_config *config, unsigned int mask)
|
||||
{
|
||||
return config && (config->flags & mask);
|
||||
}
|
||||
|
||||
int dhcp_reply(struct dhcp_context *context,
|
||||
struct in_addr iface_addr,
|
||||
@@ -73,11 +84,13 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
struct udp_dhcp_packet *rawpacket,
|
||||
unsigned int sz, time_t now, char *namebuff,
|
||||
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
|
||||
struct dhcp_vendor *vendors,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server)
|
||||
struct in_addr dhcp_next_server, struct in_addr router)
|
||||
{
|
||||
unsigned char *opt, *clid;
|
||||
struct dhcp_lease *lease;
|
||||
struct dhcp_vendor *vendor;
|
||||
int clid_len;
|
||||
struct dhcp_packet *mess = &rawpacket->data;
|
||||
unsigned char *p = mess->options;
|
||||
@@ -88,13 +101,32 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
char *message = NULL;
|
||||
unsigned int renewal_time, expires_time, def_time;
|
||||
struct dhcp_config *config;
|
||||
|
||||
if (mess->op != BOOTREQUEST ||
|
||||
mess->htype != ARPHRD_ETHER ||
|
||||
mess->hlen != ETHER_ADDR_LEN ||
|
||||
mess->cookie != htonl(DHCP_COOKIE))
|
||||
return 0;
|
||||
struct dhcp_netid *netid = NULL;
|
||||
struct in_addr addr;
|
||||
unsigned short fuzz = 0;
|
||||
|
||||
if (mess->op != BOOTREQUEST || mess->cookie != htonl(DHCP_COOKIE))
|
||||
return 0;
|
||||
|
||||
/* Token ring is supported when we have packet sockets
|
||||
to make the HW headers for us. We don't have the code to build
|
||||
token ring headers when using BPF. We rely on the fact that
|
||||
token ring hwaddrs are the same size as ethernet hwaddrs. */
|
||||
|
||||
#ifdef HAVE_BPF
|
||||
if (mess->htype != ARPHRD_ETHER)
|
||||
#else
|
||||
if (mess->htype != ARPHRD_ETHER && mess->htype != ARPHRD_IEEE802)
|
||||
#endif
|
||||
{
|
||||
syslog(LOG_WARNING, "DHCP request for unsupported hardware type (%d) recieved on %s",
|
||||
mess->htype, iface_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mess->hlen != ETHER_ADDR_LEN)
|
||||
return 0;
|
||||
|
||||
mess->op = BOOTREPLY;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE)))
|
||||
@@ -119,20 +151,10 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = namebuff;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
}
|
||||
|
||||
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL);
|
||||
|
||||
if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
|
||||
config->hostname)
|
||||
if (have_config(config, CONFIG_NAME))
|
||||
hostname = config->hostname;
|
||||
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
|
||||
{
|
||||
@@ -157,14 +179,88 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
hostname = NULL;
|
||||
}
|
||||
else
|
||||
*dot = 0; /* truncate */
|
||||
{
|
||||
*dot = 0; /* truncate */
|
||||
if (strlen(hostname) == 0)
|
||||
hostname = NULL; /* nothing left */
|
||||
}
|
||||
}
|
||||
|
||||
/* Search again now we have a hostname.
|
||||
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(dhcp_configs, context, NULL, 0, mess->chaddr, hostname);
|
||||
if (!have_config(new, CONFIG_CLID) && !have_config(new, CONFIG_HWADDR))
|
||||
config = new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
|
||||
|
||||
if (context->netid.net)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
netid = &context->netid;
|
||||
}
|
||||
|
||||
if (have_config(config, CONFIG_NETID))
|
||||
{
|
||||
config->netid.next = netid;
|
||||
netid = &config->netid;
|
||||
}
|
||||
|
||||
/* Theres a chance that carefully chosen data could match the same
|
||||
vendor/user option twice and make a loop in the netid chain. */
|
||||
for (vendor = vendors; vendor; vendor = vendor->next)
|
||||
vendor->used = 0;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
|
||||
for (vendor = vendors; vendor; vendor = vendor->next)
|
||||
if (vendor->is_vendor && !vendor->used)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
|
||||
if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
|
||||
{
|
||||
vendor->used = 1;
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_USER_CLASS)))
|
||||
{
|
||||
unsigned char *ucp = option_ptr(opt);
|
||||
int j;
|
||||
for (j = 0; j < option_len(opt); j += ucp[j] + 1)
|
||||
for (vendor = vendors; vendor; vendor = vendor->next)
|
||||
if (!vendor->is_vendor && !vendor->used)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i <= (ucp[j] - vendor->len); i++)
|
||||
if (memcmp(vendor->data, &ucp[j+i+1], vendor->len) == 0)
|
||||
{
|
||||
vendor->used = 1;
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* search again now we have a hostname */
|
||||
config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname);
|
||||
def_time = config && config->lease_time ? config->lease_time : context->lease_time;
|
||||
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
|
||||
if (have_config(config, CONFIG_NOCLID))
|
||||
{
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
|
||||
{
|
||||
@@ -184,11 +280,19 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
else
|
||||
expires_time = def_time;
|
||||
}
|
||||
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = namebuff;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
}
|
||||
|
||||
if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
|
||||
return 0;
|
||||
|
||||
switch (opt[2])
|
||||
switch (option_uint(opt, 1))
|
||||
{
|
||||
case DHCPDECLINE:
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
|
||||
@@ -219,12 +323,15 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
|
||||
lease_prune(lease, now);
|
||||
|
||||
if (config && config->addr.s_addr &&
|
||||
if (have_config(config, CONFIG_ADDR) &&
|
||||
config->addr.s_addr == option_addr(opt).s_addr)
|
||||
{
|
||||
syslog(LOG_WARNING, "disabling DHCP static address %s", inet_ntoa(config->addr));
|
||||
config->addr.s_addr = 0;
|
||||
config->flags &= ~CONFIG_ADDR ;
|
||||
}
|
||||
else
|
||||
/* make sure this host gets a different address next time. */
|
||||
context->addr_epoch++;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -242,38 +349,47 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
|
||||
case DHCPDISCOVER:
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
mess->yiaddr = option_addr(opt);
|
||||
|
||||
log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, NULL);
|
||||
|
||||
if (config && config->addr.s_addr && !lease_find_by_addr(config->addr))
|
||||
addr = option_addr(opt);
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
message = "ignored";
|
||||
else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
|
||||
mess->yiaddr = config->addr;
|
||||
else if (lease &&
|
||||
((lease->addr.s_addr & context->netmask.s_addr) ==
|
||||
(context->start.s_addr & context->netmask.s_addr)))
|
||||
else if (lease && address_available(context, lease->addr))
|
||||
mess->yiaddr = lease->addr;
|
||||
else if ((!opt || !address_available(context, mess->yiaddr)) &&
|
||||
!address_allocate(context, dhcp_configs, &mess->yiaddr))
|
||||
{
|
||||
syslog(LOG_WARNING, "address pool exhausted");
|
||||
return 0;
|
||||
}
|
||||
|
||||
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
|
||||
!config_find_by_address(dhcp_configs, addr))
|
||||
mess->yiaddr = addr;
|
||||
else if (!address_allocate(context, dhcp_configs, &mess->yiaddr, mess->chaddr))
|
||||
message = "no address available";
|
||||
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
|
||||
|
||||
if (message)
|
||||
return 0;
|
||||
|
||||
/* ensure that we send the reply by steam even if a buggy client sets this. */
|
||||
mess->ciaddr.s_addr = 0;
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server;
|
||||
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
|
||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||
if (expires_time != 0xffffffff)
|
||||
{
|
||||
p = option_put(p, end, OPTION_T1, 4, (expires_time/2));
|
||||
p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8));
|
||||
}
|
||||
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
|
||||
NULL, mess->giaddr, iface_addr, iface_mtu);
|
||||
p = option_put(p, end, OPTION_END, 0, 0);
|
||||
NULL, router, iface_addr, iface_mtu, netid);
|
||||
p = option_end(p, end, mess);
|
||||
|
||||
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
|
||||
case DHCPREQUEST:
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
message = "disabled";
|
||||
else if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
|
||||
{
|
||||
/* SELECTING or INIT_REBOOT */
|
||||
mess->yiaddr = option_addr(opt);
|
||||
@@ -291,17 +407,14 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
lease_prune(lease, now);
|
||||
lease = NULL;
|
||||
}
|
||||
|
||||
/* accept addresses in the dynamic range or ones allocated statically to
|
||||
particular hosts or an address which the host already has. */
|
||||
|
||||
if (!lease)
|
||||
{
|
||||
if (!address_available(context, mess->yiaddr) &&
|
||||
(!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr))
|
||||
message = "address unavailable";
|
||||
if (lease_find_by_addr(mess->yiaddr))
|
||||
message = "address in use";
|
||||
else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
|
||||
message = "no leases left";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -313,23 +426,50 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
mess->yiaddr = mess->ciaddr;
|
||||
if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
|
||||
message = "lease not found";
|
||||
|
||||
/* desynchronise renewals */
|
||||
fuzz = rand16();
|
||||
while (fuzz > (renewal_time/16))
|
||||
fuzz = fuzz/2;
|
||||
}
|
||||
|
||||
/* If a machine moves networks whilst it has a lease, we catch that here. */
|
||||
if ((mess->yiaddr.s_addr & context->netmask.s_addr) != (context->start.s_addr & context->netmask.s_addr))
|
||||
message = "wrong network";
|
||||
|
||||
if (!message)
|
||||
{
|
||||
struct dhcp_config *addr_config;
|
||||
/* If a machine moves networks whilst it has a lease, we catch that here. */
|
||||
if (!is_same_net(mess->yiaddr, context->start, context->netmask))
|
||||
message = "wrong network";
|
||||
|
||||
/* Check for renewal of a lease which is now outside the allowed range. */
|
||||
else if (!address_available(context, mess->yiaddr) &&
|
||||
(!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
|
||||
message = "address no longer available";
|
||||
|
||||
/* Check if a new static address has been configured. Be very sure that
|
||||
when the client does DISCOVER, it will get the static address, otherwise
|
||||
an endless protocol loop will ensue. */
|
||||
|
||||
else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
|
||||
message = "static lease available";
|
||||
|
||||
/* Check to see if the address is reserved as a static address for another host */
|
||||
else if ((addr_config = config_find_by_address(dhcp_configs, mess->yiaddr)) && addr_config != config)
|
||||
message ="address reserved";
|
||||
}
|
||||
|
||||
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
|
||||
|
||||
if (message)
|
||||
{
|
||||
log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message);
|
||||
|
||||
lease_prune(lease, now);
|
||||
|
||||
mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
|
||||
bootp_option_put(mess, NULL, NULL);
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
|
||||
p = option_put_string(p, end, OPTION_MESSAGE, message);
|
||||
p = option_put(p, end, OPTION_END, 0, 0);
|
||||
p = option_end(p, end, mess);
|
||||
mess->flags |= htons(0x8000); /* broadcast */
|
||||
return p - (unsigned char *)mess;
|
||||
}
|
||||
@@ -337,35 +477,39 @@ int dhcp_reply(struct dhcp_context *context,
|
||||
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
|
||||
|
||||
lease_set_hwaddr(lease, mess->chaddr);
|
||||
lease_set_hostname(lease, hostname, domain_suffix);
|
||||
if (hostname)
|
||||
lease_set_hostname(lease, hostname, domain_suffix);
|
||||
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
|
||||
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server;
|
||||
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
|
||||
if (renewal_time != 0xffffffff)
|
||||
{
|
||||
unsigned short fuzz = rand16();
|
||||
while (fuzz > (renewal_time/16))
|
||||
fuzz = fuzz/2;
|
||||
p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
|
||||
p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
|
||||
}
|
||||
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
|
||||
hostname, mess->giaddr, iface_addr, iface_mtu);
|
||||
p = option_put(p, end, OPTION_END, 0, 0);
|
||||
hostname, router, iface_addr, iface_mtu, netid);
|
||||
p = option_end(p, end, mess);
|
||||
return p - (unsigned char *)mess;
|
||||
|
||||
case DHCPINFORM:
|
||||
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
|
||||
if (have_config(config, CONFIG_DISABLE))
|
||||
message = "ignored";
|
||||
|
||||
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, message);
|
||||
|
||||
if (message || mess->ciaddr.s_addr == 0)
|
||||
return 0;
|
||||
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
|
||||
hostname, mess->giaddr, iface_addr, iface_mtu);
|
||||
p = option_put(p, end, OPTION_END, 0, 0);
|
||||
hostname, router, iface_addr, iface_mtu, netid);
|
||||
p = option_end(p, end, mess);
|
||||
|
||||
log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
|
||||
return p - (unsigned char *)mess;
|
||||
@@ -435,20 +579,26 @@ static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt,
|
||||
int i;
|
||||
|
||||
/* always keep one octet space for the END option. */
|
||||
if ((opt == OPTION_END) || (p + len + 3 < end))
|
||||
if (p + len + 3 < end)
|
||||
{
|
||||
*(p++) = opt;
|
||||
if (opt != OPTION_END)
|
||||
{
|
||||
*(p++) = len;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
*(p++) = val >> (8 * (len - (i + 1)));
|
||||
}
|
||||
*(p++) = len;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
*(p++) = val >> (8 * (len - (i + 1)));
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start)
|
||||
{
|
||||
*(p++) = OPTION_END;
|
||||
while ((p < end) && (p - ((unsigned char *)start) < MIN_PACKETSZ))
|
||||
*p++ = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
|
||||
{
|
||||
if (p + strlen(string) + 3 < end)
|
||||
@@ -517,6 +667,10 @@ static int in_list(unsigned char *list, int opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* If no requested options, send everything, not nothing. */
|
||||
if (!list)
|
||||
return 1;
|
||||
|
||||
for (i = 0; list[i] != OPTION_END; i++)
|
||||
if (opt == list[i])
|
||||
return 1;
|
||||
@@ -524,13 +678,26 @@ static int in_list(unsigned char *list, int opt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dhcp_opt *option_find2(struct dhcp_context *context, struct dhcp_opt *opts, int opt)
|
||||
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
|
||||
{
|
||||
for (; opts; opts = opts->next)
|
||||
if (opts->opt == opt &&
|
||||
(!opts->netid || (context->netid && strcmp(opts->netid, context->netid) == 0)))
|
||||
return opts;
|
||||
return NULL;
|
||||
struct dhcp_opt *tmp;
|
||||
struct dhcp_netid *tmp1;
|
||||
|
||||
for (tmp = opts; tmp; tmp = tmp->next)
|
||||
if (tmp->opt == opt)
|
||||
{
|
||||
if (netid)
|
||||
{
|
||||
if (tmp->netid)
|
||||
for (tmp1 = netid; tmp1; tmp1 = tmp1->next)
|
||||
if (strcmp(tmp->netid, tmp1->net) == 0)
|
||||
return tmp;
|
||||
}
|
||||
else if (!tmp->netid)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return netid ? option_find2(NULL, opts, opt) : NULL;
|
||||
}
|
||||
|
||||
static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
@@ -538,39 +705,36 @@ static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
unsigned char *req_options,
|
||||
struct dhcp_opt *config_opts,
|
||||
char *domainname, char *hostname,
|
||||
struct in_addr relay,
|
||||
struct in_addr router,
|
||||
struct in_addr iface_addr,
|
||||
int iface_mtu)
|
||||
int iface_mtu, struct dhcp_netid *netid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!req_options)
|
||||
return p;
|
||||
|
||||
struct dhcp_opt *opt;
|
||||
|
||||
if (in_list(req_options, OPTION_MAXMESSAGE))
|
||||
p = option_put(p, end, OPTION_MAXMESSAGE, 2,
|
||||
DNSMASQ_PACKETSZ > iface_mtu ?
|
||||
iface_mtu : DNSMASQ_PACKETSZ);
|
||||
|
||||
if (in_list(req_options, OPTION_NETMASK) &&
|
||||
!option_find2(context, config_opts, OPTION_NETMASK))
|
||||
!option_find2(netid, config_opts, OPTION_NETMASK))
|
||||
p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_BROADCAST) &&
|
||||
!option_find2(context, config_opts, OPTION_BROADCAST))
|
||||
!option_find2(netid, config_opts, OPTION_BROADCAST))
|
||||
p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_ROUTER) &&
|
||||
!option_find2(context, config_opts, OPTION_ROUTER))
|
||||
!option_find2(netid, config_opts, OPTION_ROUTER))
|
||||
p = option_put(p, end, OPTION_ROUTER, INADDRSZ,
|
||||
ntohl(relay.s_addr ? relay.s_addr : iface_addr.s_addr ));
|
||||
ntohl(router.s_addr));
|
||||
|
||||
if (in_list(req_options, OPTION_DNSSERVER) &&
|
||||
!option_find2(context, config_opts, OPTION_DNSSERVER))
|
||||
!option_find2(netid, config_opts, OPTION_DNSSERVER))
|
||||
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
|
||||
if (domainname && in_list(req_options, OPTION_DOMAINNAME) &&
|
||||
!option_find2(context, config_opts, OPTION_DOMAINNAME))
|
||||
!option_find2(netid, config_opts, OPTION_DOMAINNAME))
|
||||
p = option_put_string(p, end, OPTION_DOMAINNAME, domainname);
|
||||
|
||||
/* Note that we ignore attempts to set the hostname using
|
||||
@@ -578,40 +742,50 @@ static unsigned char *do_req_options(struct dhcp_context *context,
|
||||
if (hostname && in_list(req_options, OPTION_HOSTNAME))
|
||||
p = option_put_string(p, end, OPTION_HOSTNAME, hostname);
|
||||
|
||||
for (i = 0; req_options[i] != OPTION_END; i++)
|
||||
for (opt=config_opts; opt; opt = opt->next)
|
||||
{
|
||||
struct dhcp_opt *opt = option_find2(context, config_opts, req_options[i]);
|
||||
if (req_options[i] != OPTION_HOSTNAME &&
|
||||
req_options[i] != OPTION_MAXMESSAGE &&
|
||||
opt && (p + opt->len + 3 < end))
|
||||
if (opt->opt == OPTION_HOSTNAME ||
|
||||
opt->opt == OPTION_MAXMESSAGE ||
|
||||
!in_list(req_options, opt->opt) ||
|
||||
opt != option_find2(netid, config_opts, opt->opt) ||
|
||||
p + opt->len + 3 >= end)
|
||||
continue;
|
||||
|
||||
/* For the options we have default values on
|
||||
dhc-option=<optionno> means "don't include this option"
|
||||
not "include a zero-length option" */
|
||||
if (opt->len == 0 &&
|
||||
(opt->opt == OPTION_NETMASK ||
|
||||
opt->opt == OPTION_BROADCAST ||
|
||||
opt->opt == OPTION_ROUTER ||
|
||||
opt->opt == OPTION_DNSSERVER))
|
||||
continue;
|
||||
|
||||
*(p++) = opt->opt;
|
||||
*(p++) = opt->len;
|
||||
if (opt->len == 0)
|
||||
continue;
|
||||
|
||||
if (opt->is_addr)
|
||||
{
|
||||
*(p++) = opt->opt;
|
||||
*(p++) = opt->len;
|
||||
if (opt->len != 0)
|
||||
int j;
|
||||
struct in_addr *a = (struct in_addr *)opt->val;
|
||||
for (j = 0; j < opt->len; j+=INADDRSZ, a++)
|
||||
{
|
||||
if (opt->is_addr)
|
||||
{
|
||||
int j;
|
||||
struct in_addr *a = (struct in_addr *)opt->val;
|
||||
for (j = 0; j < opt->len; j+=INADDRSZ, a++)
|
||||
{
|
||||
/* zero means "self" */
|
||||
if (a->s_addr == 0)
|
||||
memcpy(p, &iface_addr, INADDRSZ);
|
||||
else
|
||||
memcpy(p, a, INADDRSZ);
|
||||
p += INADDRSZ;
|
||||
}
|
||||
}
|
||||
/* zero means "self" */
|
||||
if (a->s_addr == 0)
|
||||
memcpy(p, &iface_addr, INADDRSZ);
|
||||
else
|
||||
{
|
||||
memcpy(p, opt->val, opt->len);
|
||||
p += opt->len;
|
||||
}
|
||||
memcpy(p, a, INADDRSZ);
|
||||
p += INADDRSZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
memcpy(p, opt->val, opt->len);
|
||||
p += opt->len;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
31
src/util.c
31
src/util.c
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
|
||||
/* Code in this file contributed by Rob Funk. */
|
||||
/* Some code in this file contributed by Rob Funk. */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
@@ -85,6 +85,18 @@ unsigned short rand16(void)
|
||||
return( (unsigned short) (rand() >> 15) );
|
||||
}
|
||||
|
||||
int atoi_check(char *a, int *res)
|
||||
{
|
||||
char *p;
|
||||
|
||||
for (p = a; *p; p++)
|
||||
if (*p < '0' || *p > '9')
|
||||
return 0;
|
||||
|
||||
*res = atoi(a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int legal_char(char c)
|
||||
{
|
||||
/* check for legal char a-z A-Z 0-9 -
|
||||
@@ -100,12 +112,18 @@ int legal_char(char c)
|
||||
|
||||
int canonicalise(char *s)
|
||||
{
|
||||
/* check for legal chars ans remove trailing . */
|
||||
/* check for legal chars and remove trailing .
|
||||
also fail empty string. */
|
||||
int l = strlen(s);
|
||||
char c;
|
||||
|
||||
if (l>0 && s[l-1] == '.')
|
||||
s[l-1] = 0;
|
||||
if (l == 0) return 0;
|
||||
|
||||
if (s[l-1] == '.')
|
||||
{
|
||||
if (l == 1) return 0;
|
||||
s[l-1] = 0;
|
||||
}
|
||||
|
||||
while ((c = *s++))
|
||||
if (c != '.' && !legal_char(c))
|
||||
@@ -228,3 +246,8 @@ time_t dnsmasq_time(int fd)
|
||||
return time(NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
|
||||
{
|
||||
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user