Compare commits

..

4 Commits
v2.2 ... v2.6

Author SHA1 Message Date
Simon Kelley
33820b7ed9 import of dnsmasq-2.6.tar.gz 2012-01-05 17:31:10 +00:00
Simon Kelley
8a911ccc75 import of dnsmasq-2.5.tar.gz 2012-01-05 17:31:10 +00:00
Simon Kelley
1cff166d37 import of dnsmasq-2.4.tar.gz 2012-01-05 17:31:10 +00:00
Simon Kelley
44a2a3165c import of dnsmasq-2.3.tar.gz 2012-01-05 17:31:10 +00:00
23 changed files with 2671 additions and 1221 deletions

216
CHANGELOG
View File

@@ -667,6 +667,9 @@ release 2.0
helping to track that one down.
release 2.1
Thanks to Matt Swift and Dag Wieers for many suggestions
which went into this release.
Tweak include files to allow compilation on FreeBSD 5
Fix unaligned access warnings on BSD/Alpha.
@@ -687,10 +690,11 @@ release 2.1
Fixed long-existing strangeness in Linux IPv6 interface
discovery code. The flags field in /proc/net/if_inet6 is
_not_ the interface flags.
_not_ the interface flags.
Fail gracefully when getting an ENODEV error when trying
to bind an IPv6 socket, rather than bailing out.
to bind an IPv6 socket, rather than bailing out. Thanks
to Jan Ischebeck for feedback on that.
Allow the name->address mapping for static DHCP leases to
be set by /etc/hosts. It's now possible to have
@@ -710,12 +714,214 @@ release 2.1
Fix lease time spec when specified in dhcp-range and not
in dhcp-host, previously this was always one hour.
Fix problem with setting domains as "local only".
Fix problem with setting domains as "local only". -
thanks to Chris Schank.
Added support for max message size DHCP option.
release 2.2
Fix total lack for DHCP functionality on
Linux systems with IPv6 enabled.
Linux systems with IPv6 enabled. - thanks to
Jonathon Hudson for spotting that.
Move default config file under FreeBSD.
Move default config file under FreeBSD - patch from
Steven Honson
release 2.3
Fix "install" makefile target. (reported by Rob Stevens)
Ensure that "local=/domain/" flag is obeyed for all
queries on a domain, not just A and AAAA. (Reported by
Peter Fichtner.)
Handle DHCPDECLINE messages and provide an error message
in DHCPNAK messages.
Add "domain" setting example to
dnsmasq.conf.example. Thanks to K P Kirchdorfer for
spotting that it was missing.
Subtle change to the DHCPREQUEST handling code to work
around a bug in the DHCP client in HP Jetdirect printers.
Thanks to Marko Stolle for finding this problem.
Return DHCP T1 and T2 times, with "fuzz" to desychronise lease
renewals, as specified in the RFC.
Ensure that the END option is always present in DHCP
packets , even if the packet is too small to fit all
the requested options.
Handle larger-than-default DHCP packets if required, up
to the ethernet MTU.
Fix a couple of places where the return code from
malloc() was not checked.
Cope with a machine taking a DHCP lease and then moving
network so that the lease address is no longer valid.
The DHCP server will now work via a BOOTP relay - remote
networks are configured with the dhcp-range option the
same as directly connected ones, but they need an
additional netmask parameter. Eg
--dhcp-range=192.168.4.10,192.168.4.50,255.255,255.0
will enable DHCP service via a BOOTP relay on the
192.168.4.0 network.
Add a limit on the number of available DHCP leases,
otherwise the daemon could be DOSed by a malicious
host. The default is 150, but it can be changed by the
dhcp-lease-max option.
Fixed compilation on OpenBSD (thanks to Frederic Brodbeck
for help with that.)
Reworked the DHCP network handling code for two good
effects: (1) The limit of one network only for DHCP on
FreeBSD is now gone, (2) The DHCP server copes with
dynamically created interfaces. The one-interface
limitation remains for OpenBSD, which is missing
extensions to the socket API which have been in Linux
since version 2.2 and FreeBSD since version 4.8.
Reworked the DNS network code to also cope with
dynamically created interfaces. dnsmasq will now listen
to the wildcard address and port 53 by default, so if no
--interface or --address options are given it will handle
dynamically created interfaces. The old behaviour can be
restored with --bind-interfaces for people running BIND
on one interface and dnsmasq on another. Note that
--interface and --address options still work, but the
filtering is done by dnsmasq, rather then the kernel.
This works on Linux, and FreeBSD>=5.0. On systems which
don't support the required API extensions, the old
behaviour is used, just as if --bind-interfaces had been set.
Allow IPv6 support to be disabled at compile time. To do
that, add -DNO_IPV6 to the CFLAGS. Thanks to Oleg
I. Vdovikin for the suggestion to do that.
Add ability to set DHCP options per network. This is done
by giving a network an identifier like this:
dhcp-range=red-net,192.168.0.10,192.168.0.50
and then labeling options intended for that network only
like this:
dhcp-option=red-net,6,1.1.1.1
Thanks to Oleg Vdovikin for arguing that one through.
Made errors in the configuration file non-fatal: dnsmasq
will now complain bitterly, but continue.
Added --read-ethers option, to allow dnsmasq to pull
static DHCP information from that file.
Thanks to Andi Cambeis for that suggestion.
Added HAVE_BROKEN_RTC compilation option to support
embedded systems without a stable RTC. Oleg Vdovikin
helped work out how to make that work.
release 2.4
Fixed inability to start when the lease file doesn't
already exist. Thanks to Dag Wieers for reporting that.
Fixed problem were dhcp-host configuration options did
not play well with entries in /etc/ethers for the same
host. Thanks again to Dag Wieers.
Tweaked DHCP code to favour moving to a newly-configured
static IP address rather than an old lease when doing
DHCP allocation.
Added --alias configuration option. This provides IPv4
rewrite facilities like Cisco "DNS doctoring". Suggested
by Chad Skeeters.
Fixed bug in /etc/ethers parsing code triggered by tab
characters. Qudos to Dag Wieers for hepling to nail that
one.
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.

35
FAQ
View File

@@ -181,7 +181,42 @@ 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 two 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"
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.

View File

@@ -12,7 +12,7 @@ all :
clean :
rm -f *~ */*~ $(SRC)/*.o $(SRC)/dnsmasq core build
install : $(SRC)/dnsmasq
install : all
install -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
install -m 644 dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
install -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)

View File

@@ -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

View File

@@ -1,130 +0,0 @@
###############################################################################
#
# General mumbojumbo
#
###############################################################################
Name: dnsmasq
Version: 2.2
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

View File

@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.2
Version: 2.6
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*

View File

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

119
dnsmasq.8
View File

@@ -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.
@@ -82,12 +85,19 @@ flags are given, dnsmasq listens on all available interfaces unless overridden b
.B \-a
or
.B \-I
flags.
flags. 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.
.TP
.B \-a, --listen-address
.B \-a, --listen-address=<ipaddr>
Listen only on the given IP address. As with
.B \-i
more than one address may be specified. Unlike
@@ -101,10 +111,29 @@ or
.B \-I
flags.
.TP
.B \-z, --bind-interfaces
On systems which support it, dnsmasq binds the wildcard address,
even when it is listening on only some interfaces. It then discards
requests that it shouldn't reply to. This has the advantage of
working even when interfaces come and go and change address. This
option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when
running another nameserver on the same machine or using IP
alias. Specifying interfaces with IP alias automatically turns this
option on.
.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.
.TP
.B \-V, --alias=<old-ip>,<new-ip>[,<mask>]
Modify IPv4 addresses returned from upstream nameservers; old-ip is
replaced by new-ip. If the optional mask is given then any address
which matches the masked old-ip will be re-written. So, for instance
.B --alias=1.2.3.0,6.7.8.0,255.255.255.0
will map 1.2.3.56 to 6.7.8.56 and 1.2.3.67 to 6.7.8.67. This is what
Cisco PIX routers call "DNS doctoring".
.TP
.B \-B, --bogus-nxdomain=<ipaddr>
Transform replies which contain the IP address given into "No such
domain" replies. This is intended to counteract a devious move made by
@@ -226,17 +255,35 @@ Disable negative caching. Negative caching allows dnsmasq to remember
identical queries without forwarding them again. This flag disables
negative caching.
.TP
.B \-F, --dhcp-range=<start-addr>,<end-addr>[,<default lease time>]
.B \-F, --dhcp-range=[network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr>, both of which must be on the network
attached to a local interface. If the lease time is given, then leases
<start-addr> to <end-addr> and from statically defined addresses given
in
.B dhcp-host
options. If the lease time is given, then leases
will be given for that length of time. The lease time is on seconds,
or minutes (eg 45m) or hours (eg 1h) or the literal "infinite". This
option may be repeated, with different addresses, to enable DHCP
service on more than one local interface. (Use of more than one
interface currently only works under Linux.)
service to more than one network. For directly connected networks (ie,
networks on which the machine running dnsmasq has an interface) the
netmask is optional. It is, however, required for networks which
recieve DHCP service via a relay agent. The broadcast address is
always optional. On some broken systems, dnsmasq can listen on only
one interface when using DHCP, and the name of that interface must be
given using the
.B interface
option. This limitation currently affects OpenBSD. The optional
network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis. The end address
may be replaced by the keyword
.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
@@ -262,9 +309,26 @@ allowed to specify the client ID as text, like this:
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
format of /etc/ethers is a hardware address, followed by either a
hostname or dotted-quad IP address. When read by dnsmasq these lines
have exactly the same effect as
.B --dhcp-host
options containing the same information.
.TP
.B \-O, --dhcp-option=<opt>,[<value>[,<value>]]
.B \-O, --dhcp-option=[network-id,]<opt>,[<value>[,<value>]]
Specfify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and
@@ -278,7 +342,11 @@ specfied in RFC2132. For example, to set the default route option to
and to set the time-server address to 192.168.0.4, do
.B dhcp-option=42,192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq".
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
option number is sent, and there are option numbers for which it is not
possible to generate the correct data type; it is quite possible to
@@ -290,8 +358,19 @@ Set BOOTP options to be returned by the DHCP server. These are needed
for machines which network boot, and tell the machine where to collect
its initial configuration.
.TP
.B \-X, --dhcp-lease-max=<number>
Limits dnsmasq to the specified maximum number of DHCP leases. The
default is 150. This limit is to prevent DoS attacks from hosts which
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;
@@ -308,12 +387,14 @@ 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
@@ -332,14 +413,6 @@ of names that have been inserted into the cache. In
.B --no-daemon
mode or when full logging is enabled (-q), a complete dump of the contents of the cache is made.
.PP
When it receives a SIGUSR2,
.B dnsmasq
re-scans network interfaces. This is required if it is to listen for
queries on newly created interfaces or interfaces which have changed IP
address. For this facility to work, dnsmasq must be told to continue
running as user root, using
.B --user=root
.PP
Dnsmasq is a DNS query forwarder: it it not capable of recursively
answering arbitrary queries starting from the root servers but
forwards such queries to a fully recursive upstream DNS server which is

View File

@@ -76,6 +76,15 @@ filterwin2k
# you use this.)
#listen-address=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
#no-hosts
@@ -87,13 +96,32 @@ filterwin2k
# automatically added to simple names in a hosts-file.
#expand-hosts
# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
# as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one interface, you will need to
# repeat this for each interface on which you want to supply DHCP
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
# This is an example of a DHCP range with a network-id, so that
# some DHCP options may be set only for this network.
#dhcp-range=red,192.168.0.50,192.168.0.150
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
@@ -129,6 +157,20 @@ 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
# 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
# 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
# MAC-address/host mappings there for other purposes.
#read-ethers
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Note that all the common settings, such as netmask and
@@ -159,6 +201,14 @@ 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
# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
@@ -177,6 +227,9 @@ filterwin2k
# boot machines over the network.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.
@@ -202,11 +255,20 @@ filterwin2k
# registries which have implemented wildcard A records.
#bogus-nxdomain=64.94.110.11
# If you want to fix up DNS results from upstream servers, use the
# alias option. This only works for IPv4.
# This alias makes a result of 1.2.3.4 appear as 5.6.7.8
#alias=1.2.3.4,5.6.7.8
# 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

View File

@@ -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

View File

@@ -641,7 +641,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
if (crec->flags & F_HOSTS)
{
if (crec->addr.addr.addr4.s_addr != host_address->s_addr)
syslog(LOG_WARNING, "Not naming DHCP lease for %s because it clashes with an /etc/hosts entry.", host_name);
syslog(LOG_WARNING, "not naming DHCP lease for %s because it clashes with an /etc/hosts entry.", host_name);
}
else if (!(crec->flags & F_DHCP))
{
@@ -653,7 +653,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
goto newrec;
}
else
syslog(LOG_WARNING, "Not naming DHCP lease for %s because it clashes with a cached name.", cache_get_name(crec));
syslog(LOG_WARNING, "not naming DHCP lease for %s because it clashes with a cached name.", cache_get_name(crec));
}
return;
}
@@ -686,7 +686,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
void dump_cache(int debug, int cache_size)
{
syslog(LOG_INFO, "Cache size %d, %d/%d cache insertions re-used unexpired cache entries.",
syslog(LOG_INFO, "cache size %d, %d/%d cache insertions re-used unexpired cache entries.",
cache_size, cache_live_freed, cache_inserted);
if (debug)
@@ -710,7 +710,12 @@ void dump_cache(int debug, int cache_size)
else
strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr4));
#endif
syslog(LOG_DEBUG, "%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
syslog(LOG_DEBUG,
#ifdef HAVE_BROKEN_RTC
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %ld\n",
#else
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
#endif
cache_get_name(cache), addrbuff,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
@@ -722,7 +727,11 @@ void dump_cache(int debug, int cache_size)
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_ADDN ? "A" : " ",
#ifdef HAVE_BROKEN_RTC
cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd) ;
#else
cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))) ;
#endif
}
}
@@ -749,14 +758,14 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr)
#endif
if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<NXDOMAIN>-");
strcpy(addrbuff, "<NXDOMAIN>");
else
strcpy(addrbuff, "<NODATA>-");
strcpy(addrbuff, "<NODATA>");
if (flags & F_IPV4)
strcat(addrbuff, "IPv4");
else
strcat(addrbuff, "IPv6");
strcat(addrbuff, "-IPv4");
else if (flags & F_IPV6)
strcat(addrbuff, "-IPv6");
}
else
#ifdef HAVE_IPV6

View File

@@ -12,21 +12,24 @@
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.2"
#define VERSION "2.6"
#define FTABSIZ 150 /* max number of outstanding requests */
#define TIMEOUT 40 /* drop queries after TIMEOUT seconds */
#define TIMEOUT 20 /* drop 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"
#define ETHERSFILE "/etc/ethers"
#ifdef __uClinux__
# define RESOLVFILE "/etc/config/resolv.conf"
#else
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#ifdef __FreeBSD__
#if defined(__FreeBSD__) || defined (__OpenBSD__)
# define LEASEFILE "/var/db/dnsmasq.leases"
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
#else
@@ -37,6 +40,7 @@
#define CHUSER "nobody"
#define CHGRP "dip"
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
@@ -55,13 +59,29 @@
#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)
#if defined(INET6_ADDRSTRLEN) && !defined(NO_IPV6)
# define HAVE_IPV6
# define ADDRSTRLEN INET6_ADDRSTRLEN
# if defined(SOL_IPV6)
# define IPV6_LEVEL SOL_IPV6
# else
# define IPV6_LEVEL IPPROTO_IPV6
# endif
#elif defined(INET_ADDRSTRLEN)
# undef HAVE_IPV6
# define ADDRSTRLEN INET_ADDRSTRLEN
@@ -85,6 +105,27 @@ HAVE_LINUX_IPV6_PROC
define this to do IPv6 interface discovery using
proc/net/if_inet6 ala LINUX.
HAVE_BROKEN_RTC
define this on embeded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime()
for timing, and keep relative time values in its leases file.
Also enables "Flash disk mode". Normally, dnsmasq tries very hard to
keep the on-disk leases file up-to-date: rewriting it after every change.
When HAVE_BROKEN_RTC is in effect, a different regime is used:
The leases file is written when dnsmasq terminates, when it receives
SIGALRM, when a brand new lease is allocated, or every n seconds,
where n is one third of the smallest time configured for leases
in a --dhcp-range or --dhcp-host option.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
This configuration currently only works on Linux, but could be made to
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.
@@ -111,9 +152,6 @@ HAVE_SOCKADDR_SA_LEN
HAVE_PSELECT
If your C library implements pselect, define this.
HAVE_PF_PACKET
If your OS implements packet sockets, define this.
HAVE_BPF
If your OS implements Berkeley PAcket filter, define this.
@@ -124,8 +162,7 @@ NOTES:
HAVE_RANDOM
HAVE_DEV_RANDOM
HAVE_DEV_URANDOM
HAVE_PF_PACKET
you should NOT define
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
@@ -143,6 +180,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
@@ -151,7 +198,6 @@ NOTES:
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#define HAVE_PF_PACKET
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Don't fork into background on uClinux */
@@ -175,7 +221,6 @@ NOTES:
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_PF_PACKET
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Fix various misfeatures of libc5 headers */
@@ -193,7 +238,6 @@ typedef size_t socklen_t;
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define HAVE_PSELECT
#define HAVE_PF_PACKET
/* glibc < 2.2 has broken Sockaddr_in6 so we have to use our own. */
/* glibc < 2.2 doesn't define in_addr_t */
#if defined(__GLIBC__) && (__GLIBC__ == 2) && \
@@ -204,6 +248,9 @@ typedef unsigned long in_addr_t;
#endif
#endif
/* #elif defined(__OpenBSD__)
#error The sockets API in OpenBSD does not provide facilities required by dnsmasq
*/
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_GETOPT_LONG
@@ -223,6 +270,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 */
@@ -255,3 +303,4 @@ typedef unsigned long in_addr_t;

View File

@@ -14,156 +14,343 @@
#include "dnsmasq.h"
void dhcp_packet(struct dhcp_context *context, char *packet,
void dhcp_init(int *fdp, int* rfdp)
{
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr;
int opt = 1;
if (fd == -1)
die ("cannot create DHCP socket : %s", NULL);
if (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_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
die("failed to set options on DHCP socket: %s", NULL);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
die("failed to bind DHCP server socket: %s", NULL);
*fdp = fd;
#ifdef HAVE_BPF
opt = 0;
while (1)
{
char filename[50];
sprintf(filename, "/dev/bpf%d", opt++);
if ((fd = open(filename, O_RDWR, 0)) != -1)
break;
if (errno != EBUSY)
die("cannot create DHCP BPF socket: %s", NULL);
}
#else
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
die("cannot create DHCP packet socket: %s", NULL);
#endif
*rfdp = fd;
}
void dhcp_packet(struct dhcp_context *contexts, char *packet,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
time_t now, char *namebuff, char *domain_suffix,
char *dhcp_file, char *dhcp_sname,
struct in_addr dhcp_next_server)
struct in_addr dhcp_next_server, int dhcp_fd, int raw_fd,
struct iname *names, struct iname *addrs, struct iname *except)
{
struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *) packet;
struct udp_dhcp_packet *rawpacket = (struct udp_dhcp_packet *)packet;
struct dhcp_packet *mess = (struct dhcp_packet *)&rawpacket->data;
int sz, newlen;
struct dhcp_context *context;
struct iname *tmp;
struct ifreq ifr;
struct msghdr msg;
struct iovec iov[2];
struct cmsghdr *cmptr;
int sz, newlen, iface_index = 0;
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
sz = recvfrom(context->fd, &rawpacket->data,
PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr)),
0, NULL, 0);
if ((unsigned int)sz > (sizeof(*mess) - sizeof(mess->options)))
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef IP_PKTINFO
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
iov[0].iov_base = (char *)&rawpacket->data;
iov[0].iov_len = DNSMASQ_PACKETSZ - (sizeof(struct ip) + sizeof(struct udphdr));
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
sz = recvmsg(dhcp_fd, &msg, 0);
if (sz < (int)(sizeof(*mess) - sizeof(mess->options)))
return;
#if defined (IP_PKTINFO)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
if (!(ifr.ifr_ifindex = iface_index) ||
ioctl(dhcp_fd, SIOCGIFNAME, &ifr) == -1)
return;
#elif defined(IP_RECVIF)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
return;
#else
if (!names || !names->name || names->next)
{
lease_prune(NULL, now); /* lose any expired leases */
newlen = dhcp_reply(context, mess, sz, now, namebuff, dhcp_opts,
dhcp_configs, domain_suffix, dhcp_file,
dhcp_sname, dhcp_next_server );
lease_update_dns(0);
if (newlen != 0)
{
int broadcast = ntohs(mess->flags) & 0x8000;
/* newlen -ve forces broadcast */
if (newlen < 0)
{
broadcast = 1;
newlen = -newlen;
}
if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
{
/* To send to BOOTP relay or configured client, use
the IP packet */
struct sockaddr_in dest;
dest.sin_family = AF_INET;
if (mess->giaddr.s_addr)
{
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_addr = mess->giaddr;
}
else
{
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_addr = mess->ciaddr;
}
sendto(context->fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
}
else
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
u32 i, sum;
#ifdef HAVE_PF_PACKET
struct sockaddr_ll dest;
dest.sll_family = AF_PACKET;
dest.sll_halen = ETHER_ADDR_LEN;
dest.sll_ifindex = context->ifindex;
dest.sll_protocol = htons(ETHERTYPE_IP);
if (broadcast)
{
memset(dest.sll_addr, 255, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(dest.sll_addr, mess->chaddr, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
#endif
#ifdef HAVE_BPF
struct ether_header header;
struct iovec iov [2];
header.ether_type = htons(ETHERTYPE_IP);
memcpy(header.ether_shost, context->hwaddr, ETHER_ADDR_LEN);
if (broadcast)
{
memset(header.ether_dhost, 255, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(header.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
#endif
rawpacket->ip.ip_p = IPPROTO_UDP;
rawpacket->ip.ip_src.s_addr = context->serv_addr.s_addr;
rawpacket->ip.ip_len = htons(sizeof(struct ip) +
sizeof(struct udphdr) +
newlen) ;
rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
rawpacket->ip.ip_v = IPVERSION;
rawpacket->ip.ip_tos = 0;
rawpacket->ip.ip_id = htons(0);
rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
rawpacket->ip.ip_ttl = IPDEFTTL;
rawpacket->ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&rawpacket->ip)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
rawpacket->udp.uh_sum = 0;
rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
sum += htons(IPPROTO_UDP);
for (i = 0; i < 4; i++)
sum += ((u16 *)&rawpacket->ip.ip_src)[i];
for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
sum += ((u16 *)&rawpacket->udp)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
#ifdef HAVE_PF_PACKET
sendto(context->rawfd, rawpacket, ntohs(rawpacket->ip.ip_len),
0, (struct sockaddr *)&dest, sizeof(dest));
syslog(LOG_ERR, "must set exactly one interface on broken systems without IP_RECVIF");
return;
}
else
strcpy(ifr.ifr_name, names->name);
#endif
#ifdef HAVE_BPF
iov[0].iov_base = (char *)&header;
iov[0].iov_len = sizeof(struct ether_header);
iov[1].iov_base = (char *)rawpacket;
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
writev(context->rawfd, iov, 2);
#endif
}
ifr.ifr_addr.sa_family = AF_LINK;
if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0)
return;
memcpy(iface_hwaddr, LLADDR((struct sockaddr_dl *)&ifr.ifr_addr), ETHER_ADDR_LEN);
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(dhcp_fd, SIOCGIFADDR, &ifr) < 0 )
return;
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
/* enforce available interface configuration */
for (tmp = except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
if (names || addrs)
{
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp)
for (tmp = addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == iface_addr.s_addr)
break;
if (!tmp)
return;
}
/* 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 ((source.s_addr & iface_netmask.s_addr) != (iface_addr.s_addr & iface_netmask.s_addr))
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)
{
struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask;
if (netmask.s_addr &&
(source.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr) &&
(source.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr))
break;
}
if (!context)
{
syslog(LOG_WARNING, "no address range available for DHCP request via %s", inet_ntoa(source));
return;
}
netmask_save = context->netmask;
broadcast_save = context->broadcast;
if (!context->netmask.s_addr)
context->netmask = iface_netmask;
if (!context->broadcast.s_addr)
{
if (iface_broadcast.s_addr)
context->broadcast = iface_broadcast;
else
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 ((source.s_addr & context->netmask.s_addr) == (iface_addr.s_addr & context->netmask.s_addr))
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, router);
lease_update_file(0, now);
lease_update_dns();
context->netmask = netmask_save;
context->broadcast = broadcast_save;
if (newlen == 0)
return;
if (mess->giaddr.s_addr || mess->ciaddr.s_addr)
{
/* To send to BOOTP relay or configured client, use
the IP packet */
struct sockaddr_in dest;
dest.sin_family = AF_INET;
if (mess->giaddr.s_addr)
{
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_addr = mess->giaddr;
}
else
{
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_addr = mess->ciaddr;
}
sendto(dhcp_fd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
}
else
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
u32 i, sum;
unsigned char hwdest[ETHER_ADDR_LEN];
if (ntohs(mess->flags) & 0x8000)
{
memset(hwdest, 255, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(hwdest, mess->chaddr, ETHER_ADDR_LEN);
rawpacket->ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
rawpacket->ip.ip_p = IPPROTO_UDP;
rawpacket->ip.ip_src.s_addr = iface_addr.s_addr;
rawpacket->ip.ip_len = htons(sizeof(struct ip) +
sizeof(struct udphdr) +
newlen) ;
rawpacket->ip.ip_hl = sizeof(struct ip) / 4;
rawpacket->ip.ip_v = IPVERSION;
rawpacket->ip.ip_tos = 0;
rawpacket->ip.ip_id = htons(0);
rawpacket->ip.ip_off = htons(0x4000); /* don't fragment */
rawpacket->ip.ip_ttl = IPDEFTTL;
rawpacket->ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&rawpacket->ip)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
rawpacket->udp.uh_sport = htons(DHCP_SERVER_PORT);
rawpacket->udp.uh_dport = htons(DHCP_CLIENT_PORT);
((u8 *)&rawpacket->data)[newlen] = 0; /* for checksum, in case length is odd. */
rawpacket->udp.uh_sum = 0;
rawpacket->udp.uh_ulen = sum = htons(sizeof(struct udphdr) + newlen);
sum += htons(IPPROTO_UDP);
for (i = 0; i < 4; i++)
sum += ((u16 *)&rawpacket->ip.ip_src)[i];
for (i = 0; i < (sizeof(struct udphdr) + newlen + 1) / 2; i++)
sum += ((u16 *)&rawpacket->udp)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
rawpacket->udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
{
#ifdef HAVE_BPF
struct ether_header header;
header.ether_type = htons(ETHERTYPE_IP);
memcpy(header.ether_shost, iface_hwaddr, ETHER_ADDR_LEN);
memcpy(header.ether_dhost, hwdest, ETHER_ADDR_LEN);
ioctl(raw_fd, BIOCSETIF, &ifr);
iov[0].iov_base = (char *)&header;
iov[0].iov_len = sizeof(struct ether_header);
iov[1].iov_base = (char *)rawpacket;
iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
writev(raw_fd, iov, 2);
#else
struct sockaddr_ll dest;
dest.sll_family = AF_PACKET;
dest.sll_halen = ETHER_ADDR_LEN;
dest.sll_ifindex = iface_index;
dest.sll_protocol = htons(ETHERTYPE_IP);
memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN);
sendto(raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len),
0, (struct sockaddr *)&dest, sizeof(dest));
#endif
}
}
}
int address_available(struct dhcp_context *context, struct in_addr taddr)
{
/* Check is an address is OK for this network, ie
@@ -175,6 +362,10 @@ 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;
@@ -195,7 +386,11 @@ int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
struct dhcp_config *config;
struct in_addr start = context->last;
/* start == end means no dynamic leases. */
if (context->end.s_addr == context->start.s_addr)
return 0;
do {
if (context->last.s_addr == context->end.s_addr)
context->last = context->start;
@@ -206,7 +401,7 @@ int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
if (!lease_find_by_addr(context->last))
{
for (config = configs; config; config = config->next)
if (config->addr.s_addr == context->last.s_addr)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == context->last.s_addr)
break;
if (!config)
@@ -224,7 +419,7 @@ 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))
return 1;
@@ -241,46 +436,162 @@ 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 && strcmp(config->hostname, hostname) == 0 &&
if ((config->flags & CONFIG_NAME) &&
hostname_isequal(config->hostname, hostname) &&
is_addr_in_context(context, config))
return config;
return NULL;
}
void set_configs_from_cache(struct dhcp_config *configs)
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches. */
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
{
FILE *f = fopen(ETHERSFILE, "r");
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)
{
syslog(LOG_ERR, "failed to read " ETHERSFILE ":%m");
return configs;
}
while (fgets(buff, MAXDNAME, f))
{
while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if ((*buff == '#') || (*buff == '+'))
continue;
for (ip = buff; *ip && !isspace(*ip); ip++);
for(; *ip && isspace(*ip); ip++)
*ip = 0;
if (!*ip)
continue;
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')))
break;
if (!*cp)
{
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
continue;
flags = CONFIG_ADDR;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
break;
}
else
{
if (!canonicalise(ip))
continue;
flags = CONFIG_NAME;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
break;
}
if (!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;
memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
count++;
}
fclose(f);
syslog(LOG_INFO, "read " ETHERSFILE " - %d addresses", count);
return configs;
}
void dhcp_update_configs(struct dhcp_config *configs)
{
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches. */
struct dhcp_config *config;
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;
}
}

View File

@@ -16,7 +16,7 @@
#include "dnsmasq.h"
static int sigterm, sighup, sigusr1, sigusr2;
static int sigterm, sighup, sigusr1, sigalarm;
static void sig_handler(int sig)
{
@@ -26,23 +26,25 @@ static void sig_handler(int sig)
sighup = 1;
else if (sig == SIGUSR1)
sigusr1 = 1;
else if (sig == SIGUSR2)
sigusr2 = 1;
else if (sig == SIGALRM)
sigalarm = 1;
}
int main (int argc, char **argv)
{
char *int_err_string;
int cachesize = CACHESIZ;
int port = NAMESERVER_PORT;
int maxleases = MAXLEASES;
int query_port = 0;
int first_loop = 1;
unsigned long local_ttl = 0;
unsigned int options;
unsigned int options, min_leasetime;
char *runfile = RUNFILE;
time_t resolv_changed = 0;
time_t now, last = 0;
struct irec *iface, *interfaces = NULL;
struct irec *interfaces = NULL;
struct listener *listener, *listeners;
struct doctor *doctors = NULL;
char *mxname = NULL;
char *mxtarget = NULL;
char *lease_file = NULL;
@@ -53,9 +55,9 @@ int main (int argc, char **argv)
struct iname *if_names = NULL;
struct iname *if_addrs = NULL;
struct iname *if_except = NULL;
struct iname *if_tmp;
struct server *serv_addrs = NULL;
char *dnamebuff, *packet;
int uptime_fd = -1;
struct server *servers, *last_server;
struct resolvc default_resolv = { NULL, 1, 0, RESOLVFILE };
struct resolvc *resolv = &default_resolv;
@@ -66,29 +68,33 @@ int main (int argc, char **argv)
struct dhcp_opt *dhcp_options = NULL;
char *dhcp_file = NULL, *dhcp_sname = NULL;
struct in_addr dhcp_next_server;
int leasefd = 0;
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 */
sigusr2 = 0; /* or rescan interfaces */
sigterm = 0; /* or die */
#ifdef HAVE_BROKEN_RTC
sigalarm = 1; /* need regular lease dumps */
#else
sigalarm = 0; /* or not */
#endif
sigact.sa_handler = sig_handler;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGUSR1, &sigact, NULL);
sigaction(SIGUSR2, &sigact, NULL);
sigaction(SIGHUP, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
/* now block all the signals, they stay that way except
during the call to pselect */
sigaddset(&sigact.sa_mask, SIGUSR1);
sigaddset(&sigact.sa_mask, SIGUSR2);
sigaddset(&sigact.sa_mask, SIGTERM);
sigaddset(&sigact.sa_mask, SIGHUP);
sigaddset(&sigact.sa_mask, SIGALRM);
sigprocmask(SIG_BLOCK, &sigact.sa_mask, &sigmask);
/* These get allocated here to avoid overflowing the small stack
@@ -96,71 +102,53 @@ 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);
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
packet = safe_malloc(PACKETSZ+MAXDNAME+RRFIXEDSZ);
packet = safe_malloc(DNSMASQ_PACKETSZ);
dhcp_next_server.s_addr = 0;
options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &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_file, &dhcp_sname, &dhcp_next_server);
&dhcp, &dhcp_configs, &dhcp_options,
&dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
&doctors);
/* if we cannot support binding the wildcard address, set the "bind only
interfaces in use" option */
#ifndef HAVE_UDP_SRC_DST
options |= OPT_NOWILD;
#endif
if (!lease_file)
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;
}
if ((int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, dhcp, port)))
die(int_err_string, NULL);
for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->found)
die("unknown interface %s", if_tmp->name);
for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->found)
{
#ifdef HAVE_IPV6
if (if_tmp->addr.sa.sa_family == AF_INET)
inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
dnamebuff, MAXDNAME);
else
inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
dnamebuff, MAXDNAME);
die("no interface with address %s", dnamebuff);
#else
die("no interface with address %s", inet_ntoa(if_tmp->addr.in.sin_addr));
#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_bound_listeners(interfaces);
else
listeners = create_wildcard_listeners(port);
forward_init(1);
cache_init(cachesize, options & OPT_LOG);
#ifdef HAVE_BROKEN_RTC
if ((uptime_fd = open(UPTIME, O_RDONLY)) == -1)
die("cannot open " UPTIME ":%s", NULL);
#endif
now = dnsmasq_time(uptime_fd);
if (dhcp)
{
#if !defined(HAVE_PF_PACKET) && !defined(HAVE_BPF)
die("no DHCP support available on this OS.", NULL);
#endif
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
if (!dhcp_tmp->iface)
die("No suitable interface for DHCP service at address %s", inet_ntoa(dhcp_tmp->start));
set_configs_from_cache(dhcp_configs);
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, time(NULL), dhcp_configs);
lease_update_dns(1); /* must follow cache_init and lease_init */
dhcp_init(&dhcpfd, &dhcp_raw_fd);
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
}
setbuf(stdout, NULL);
@@ -198,19 +186,16 @@ int main (int argc, char **argv)
for (i=0; i<64; i++)
{
for (iface = interfaces; iface; iface = iface->next)
if (iface->fd == i)
for (listener = listeners; listener; listener = listener->next)
if (listener->fd == i)
break;
if (iface)
if (listener)
continue;
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
if (dhcp_tmp->fd == i || dhcp_tmp->rawfd == i)
break;
if (dhcp_tmp)
continue;
if (dhcp && (i == leasefd))
if (i == leasefd ||
i == uptime_fd ||
i == dhcpfd ||
i == dhcp_raw_fd)
continue;
close(i);
@@ -255,10 +240,18 @@ int main (int argc, char **argv)
sprintf(packet, "infinite");
else
sprintf(packet, "%ds", (int)dhcp_tmp->lease_time);
syslog(LOG_INFO, "DHCP on %s, IP range %s -- %s, lease time %s",
dhcp_tmp->iface, dnamebuff, inet_ntoa(dhcp_tmp->end), packet);
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);
}
#ifdef HAVE_BROKEN_RTC
if (dhcp)
syslog(LOG_INFO, "DHCP, %s will be written every %ds", lease_file, min_leasetime/3);
#endif
if (getuid() == 0 || geteuid() == 0)
syslog(LOG_WARNING, "failed to drop root privs");
@@ -271,8 +264,15 @@ int main (int argc, char **argv)
if (sighup)
{
cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
set_configs_from_cache(dhcp_configs);
lease_update_dns(1);
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),
@@ -286,18 +286,16 @@ int main (int argc, char **argv)
sigusr1 = 0;
}
if (sigusr2)
if (sigalarm)
{
if (getuid() != 0 && port <= 1024)
syslog(LOG_ERR, "cannot re-scan interfaces unless --user=root");
else
{
syslog(LOG_INFO, "rescanning network interfaces");
int_err_string = enumerate_interfaces(&interfaces, if_names, if_addrs, if_except, NULL, port);
if (int_err_string)
syslog(LOG_ERR, int_err_string, strerror(errno));
}
sigusr2 = 0;
if (dhcp)
{
lease_update_file(1, now);
#ifdef HAVE_BROKEN_RTC
alarm(min_leasetime/3);
#endif
}
sigalarm = 0;
}
FD_ZERO(&rset);
@@ -313,19 +311,20 @@ int main (int argc, char **argv)
maxfd = serverfdp->fd;
}
for (iface = interfaces; iface; iface = iface->next)
for (listener = listeners; listener; listener = listener->next)
{
FD_SET(iface->fd, &rset);
if (iface->fd > maxfd)
maxfd = iface->fd;
FD_SET(listener->fd, &rset);
if (listener->fd > maxfd)
maxfd = listener->fd;
}
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
if (dhcp)
{
FD_SET(dhcp_tmp->fd, &rset);
if (dhcp_tmp->fd > maxfd)
maxfd = dhcp_tmp->fd;
FD_SET(dhcpfd, &rset);
if (dhcpfd > maxfd)
maxfd = dhcpfd;
}
#ifdef HAVE_PSELECT
if (pselect(maxfd+1, &rset, NULL, NULL, NULL, &sigmask) < 0)
FD_ZERO(&rset); /* rset otherwise undefined after error */
@@ -342,17 +341,23 @@ int main (int argc, char **argv)
}
first_loop = 0;
now = time(NULL);
now = dnsmasq_time(uptime_fd);
/* Check for changes to resolv files once per second max. */
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. */
@@ -389,64 +394,30 @@ int main (int argc, char **argv)
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);
dnamebuff, last_server, bogus_addr, doctors);
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
if (FD_ISSET(dhcp_tmp->fd, &rset))
dhcp_packet(dhcp_tmp, packet, dhcp_options, dhcp_configs,
now, dnamebuff, domain_suffix, dhcp_file,
dhcp_sname, dhcp_next_server);
if (dhcp && FD_ISSET(dhcpfd, &rset))
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs,
now, dnamebuff, domain_suffix, dhcp_file,
dhcp_sname, dhcp_next_server, dhcpfd, dhcp_raw_fd,
if_names, if_addrs, if_except);
for (iface = interfaces; iface; iface = iface->next)
{
if (FD_ISSET(iface->fd, &rset))
{
/* request packet, deal with query */
union mysockaddr udpaddr;
socklen_t udplen = sizeof(udpaddr);
HEADER *header = (HEADER *)packet;
int m, n = recvfrom(iface->fd, packet, PACKETSZ, 0, &udpaddr.sa, &udplen);
udpaddr.sa.sa_family = iface->addr.sa.sa_family;
#ifdef HAVE_IPV6
if (udpaddr.sa.sa_family == AF_INET6)
udpaddr.in6.sin6_flowinfo = htonl(0);
#endif
if (n >= (int)sizeof(HEADER) && !header->qr)
{
if (extract_request(header, (unsigned int)n, dnamebuff))
{
if (udpaddr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, dnamebuff,
(struct all_addr *)&udpaddr.in.sin_addr);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, dnamebuff,
(struct all_addr *)&udpaddr.in6.sin6_addr);
#endif
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
mxname, mxtarget, options, now, local_ttl, dnamebuff);
if (m >= 1)
{
/* answered from cache, send reply */
sendto(iface->fd, (char *)header, m, 0,
&udpaddr.sa, sa_len(&udpaddr));
}
else
{
/* cannot answer from cache, send on to real nameserver */
last_server = forward_query(iface->fd, &udpaddr, header, n,
options, dnamebuff, servers,
last_server, now, local_ttl);
}
}
}
}
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);
}
syslog(LOG_INFO, "exiting on receipt of SIGTERM");
#ifdef HAVE_BROKEN_RTC
if (dhcp)
lease_update_file(1, now);
#endif
if (leasefd != -1)
close(leasefd);
return 0;
}

View File

@@ -11,23 +11,24 @@
*/
/* Author's email: simon@thekelleys.org.uk */
#ifdef __linux__
/* for pselect.... */
#define _XOPEN_SOURCE 600
#define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */
#define _BSD_SOURCE
/* and also, on FreeBSD 5.0 ..... */
#define __BSD_VISIBLE 1
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/types.h>
#include <netinet/in.h>
/* get this before config.h too. */
#include <syslog.h>
#include "config.h"
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <sys/stat.h>
@@ -36,9 +37,10 @@
#include <sys/ioctl.h>
#include <sys/select.h>
#if defined(__sun) || defined(__sun__)
#include <sys/sockio.h>
# include <sys/sockio.h>
#endif
#include <sys/time.h>
#include <limits.h>
#include <net/if.h>
#include <unistd.h>
#include <stdio.h>
@@ -54,19 +56,26 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <net/ethernet.h>
#if defined(__OpenBSD__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
#endif
#include <net/if_arp.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef HAVE_PF_PACKET
#include <netpacket/packet.h>
#endif
#ifdef HAVE_BPF
#include <net/bpf.h>
#include <net/if_dl.h>
# include <net/bpf.h>
# include <net/if_dl.h>
#else
# include <netpacket/packet.h>
#endif
#include <sys/uio.h>
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV 1
#define OPT_FILTER 2
#define OPT_LOG 4
@@ -80,6 +89,8 @@
#define OPT_LOCALMX 1024
#define OPT_NO_NEG 2048
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
struct all_addr {
union {
@@ -95,6 +106,12 @@ struct bogus_addr {
struct bogus_addr *next;
};
/* dns doctor param */
struct doctor {
struct in_addr in, out, mask;
struct doctor *next;
};
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -175,20 +192,20 @@ struct server {
struct server *next;
};
/* linked list of all the interfaces in the system and
the sockets we have bound to each one. */
struct irec {
union mysockaddr addr;
int fd;
int valid;
struct irec *next;
};
struct listener {
int fd, family;
struct listener *next;
};
/* interface and address parms from command line. */
struct iname {
char *name;
union mysockaddr addr;
int found;
struct iname *next;
};
@@ -202,6 +219,7 @@ struct resolvc {
struct frec {
union mysockaddr source;
struct all_addr dest;
struct server *sentto;
unsigned short orig_id, new_id;
int fd;
@@ -220,28 +238,36 @@ struct dhcp_lease {
};
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;
char *hostname, *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
struct dhcp_opt {
int opt, len, is_addr;
unsigned char *val;
char *netid;
struct dhcp_opt *next;
};
struct dhcp_context {
int fd, rawfd, ifindex;
char *iface;
unsigned char hwaddr[ETHER_ADDR_LEN];
unsigned int lease_time;
struct in_addr serv_addr, netmask, broadcast;
struct in_addr netmask, broadcast;
struct in_addr start, end, last; /* range of available addresses */
char *netid;
struct dhcp_context *next;
};
@@ -294,7 +320,8 @@ unsigned short extract_request(HEADER *header, unsigned int qlen, char *name);
int setup_reply(HEADER *header, unsigned int qlen,
struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl);
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, time_t now);
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,
char *mxtarget, unsigned int options, time_t now, unsigned long local_ttl,
@@ -313,7 +340,7 @@ char *safe_string_alloc(char *cp);
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);
/* option.c */
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
char **mxname, char **mxtarget, char **lease_file,
@@ -323,35 +350,37 @@ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resol
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,
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server);
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server,
int *maxleases, unsigned int *min_leasetime, struct doctor **doctors);
/* forward.c */
void forward_init(int first);
void reap_forward(int fd);
struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *header,
int plen, unsigned int options, char *dnamebuff,
struct server *servers, struct server *last_server,
time_t now, unsigned long local_ttl);
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 bogus_addr *bogus_nxdomain, struct doctor *doctors);
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
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);
/* network.c */
struct server *reload_servers(char *fname, char *buff, struct server *servers, int query_port);
struct server *check_servers(struct server *new, struct irec *interfaces, struct serverfd **sfds);
char *enumerate_interfaces(struct irec **interfaces,
struct iname *names,
struct iname *addrs,
struct iname *except,
struct dhcp_context *dhcp,
int port);
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);
/* dhcp.c */
void dhcp_packet(struct dhcp_context *context, char *packet,
struct dhcp_opt *dhcp_opts,
struct dhcp_config *dhcp_configs,
void dhcp_init(int *fdp, int* rfdp);
void dhcp_packet(struct dhcp_context *contexts, char *packet,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
time_t now, char *namebuff, char *domain_suffix,
char *dhcp_file, char *dhcp_sname, struct in_addr dhcp_next_server);
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);
@@ -359,12 +388,14 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
unsigned char *hwaddr, char *hostname);
void set_configs_from_cache(struct dhcp_config *configs);
struct dhcp_config *read_ethers(struct dhcp_config *configs, char *buff);
void dhcp_update_configs(struct dhcp_config *configs);
struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff);
/* lease.c */
void lease_update_dns(int force_dns);
void lease_update_file(int force, time_t now);
void lease_update_dns(void);
int lease_init(char *lease_file, char *domain, char *buff,
char *buff2, time_t now, struct dhcp_config *dhcp_configs);
char *buff2, time_t now, int maxleases);
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr);
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix);
@@ -372,10 +403,19 @@ void lease_set_expires(struct dhcp_lease *lease, time_t exp);
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_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 dhcp_packet *mess,
int dhcp_reply(struct dhcp_context *context,
struct in_addr iface_addr,
char *iface_name,
int iface_mtu,
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_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
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

View File

@@ -33,21 +33,90 @@ void forward_init(int first)
f->new_id = 0;
}
/* delete all forward records recieved from socket fd */
void reap_forward(int fd)
/* 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)
{
struct frec *f;
for (f = frec_list; f; f = f->next)
if (f->fd == fd)
f->new_id = 0;
}
struct msghdr msg;
struct iovec iov[1];
struct cmsghdr *cmptr;
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(IP_PKTINFO)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_SENDSRCADDR)
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
#ifdef HAVE_IPV6
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_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 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;
}
#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;
}
#endif
#ifdef HAVE_IPV6
if (!nowild && 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;
}
#endif
sendmsg(fd, &msg, 0);
}
/* returns new last_server */
struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *header,
int plen, unsigned int options, char *dnamebuff,
struct server *servers, struct server *last_server,
time_t now, unsigned long local_ttl)
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 server *servers, struct server *last_server,
time_t now, unsigned long local_ttl)
{
struct frec *forward;
char *domain = NULL;
@@ -113,10 +182,10 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
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 */
if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */
{
type = SERV_HAS_DOMAIN;
flags = sflag;
flags = gotname;
domain = serv->domain;
matchlen = domainlen;
if (serv->addr.sa.sa_family == AF_INET)
@@ -139,7 +208,12 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
}
if (flags) /* flags set here means a literal found */
log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp);
{
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 */
@@ -162,6 +236,7 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
forward->sentto = last_server;
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
@@ -228,8 +303,8 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
/* 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);
sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr));
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);
@@ -238,7 +313,8 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
/* 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)
char *dnamebuff, struct server *last_server,
struct bogus_addr *bogus_nxdomain, struct doctor *doctors)
{
/* packet from peer server, extract data for cache, and send to
original requester */
@@ -262,7 +338,7 @@ struct server *reply_query(int fd, int options, char *packet, time_t now,
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);
extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
else if (!(options & OPT_NO_NEG))
extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
}
@@ -273,15 +349,169 @@ struct server *reply_query(int fd, int options, char *packet, time_t now,
since that will prod the resolver into moving to TCP - which we
don't support. */
header->tc = 0; /* goodbye truncate */
sendto(forward->fd, packet, n, 0,
&forward->source.sa, sa_len(&forward->source));
send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
forward->new_id = 0; /* cancel */
}
}
return last_server;
}
struct server *receive_query(struct listener *listen, char *packet, char *mxname,
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)
{
HEADER *header = (HEADER *)packet;
union mysockaddr source_addr;
struct iname *tmp;
struct all_addr dst_addr;
int m, n, if_index = 0;
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmptr;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(IP_PKTINFO)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_RECVDSTADDR)
char control[CMSG_SPACE(sizeof(struct in_addr)) +
CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
iov[0].iov_base = packet;
iov[0].iov_len = PACKETSZ;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &source_addr;
msg.msg_namelen = sizeof(source_addr);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
n = recvmsg(listen->fd, &msg, 0);
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
source_addr.in6.sin6_flowinfo = htonl(0);
#endif
if (!(options & OPT_NOWILD) && msg.msg_controllen < sizeof(struct cmsghdr))
return last_server;
#if defined(IP_PKTINFO)
if (!(options & OPT_NOWILD) && listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (!(options & OPT_NOWILD) && listen->family == AF_INET)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (!(options & OPT_NOWILD) && listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
}
}
#endif
if (n < (int)sizeof(HEADER) || header->qr)
return last_server;
/* enforce available interface configuration */
if (!(options & OPT_NOWILD))
{
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, ifr.ifr_name) == 0))
return last_server;
if (names || addrs)
{
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp)
for (tmp = addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == listen->family)
{
if (tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
break;
#ifdef HAVE_IPV6
else if (tmp->addr.sa.sa_family == AF_INET6 &&
memcmp(&tmp->addr.in6.sin6_addr,
&dst_addr.addr.addr6,
sizeof(struct in6_addr)) == 0)
break;
#endif
}
if (!tmp)
return last_server;
}
}
if (extract_request(header, (unsigned int)n, namebuff))
{
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff,
(struct all_addr *)&source_addr.in.sin_addr);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff,
(struct all_addr *)&source_addr.in6.sin6_addr);
#endif
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
mxname, mxtarget, options, now, local_ttl, namebuff);
if (m >= 1)
send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr);
else
last_server = forward_query(listen->fd, &source_addr, &dst_addr,
header, n, options, namebuff, servers,
last_server, now, local_ttl);
return last_server;
}
static struct frec *get_new_frec(time_t now)
{
struct frec *f = frec_list, *oldest = NULL;

249
src/isc.c Normal file
View 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

View File

@@ -16,10 +16,11 @@
static struct dhcp_lease *leases;
FILE *lease_file;
int dns_dirty, file_dirty;
int dns_dirty, file_dirty, new_lease;
int leases_left;
int lease_init(char *filename, char *domain, char *buff,
char *buff2, time_t now, struct dhcp_config *dhcp_configs)
char *buff2, time_t now, int maxleases)
{
unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3;
unsigned long ei;
@@ -27,20 +28,28 @@ int lease_init(char *filename, char *domain, char *buff,
unsigned char hwaddr[ETHER_ADDR_LEN];
struct in_addr addr;
struct dhcp_lease *lease;
struct dhcp_config *config;
int clid_len = 0;
int has_old = 0;
leases = NULL;
leases_left = maxleases;
/* NOTE: need a+ mode to create file if it doesn't exist */
if (!(lease_file = fopen(filename, "a+")))
die("cannot open or create leases file: %s", NULL);
rewind(lease_file); /* file opened with mode a+ which sets pointer at end. */
/* a+ mode lease pointer at end. */
rewind(lease_file);
while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %256s %500s",
&ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3, buff, buff2) == 13)
{
#ifdef HAVE_BROKEN_RTC
if (ei)
expires = (time_t)ei + now;
else
expires = (time_t)0;
#else
/* strictly time_t is opaque, but this hack should work on all sane systems,
even when sizeof(time_t) == 8 */
expires = (time_t)ei;
@@ -50,6 +59,7 @@ int lease_init(char *filename, char *domain, char *buff,
has_old = 1;
continue; /* expired */
}
#endif
hwaddr[0] = e0;
hwaddr[1] = e1;
@@ -74,7 +84,7 @@ int lease_init(char *filename, char *domain, char *buff,
}
if (!(lease = lease_allocate(buff2, clid_len, addr)))
die ("cannot get memory", NULL);
die ("too many stored leases", NULL);
lease->expires = expires;
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
@@ -85,32 +95,54 @@ int lease_init(char *filename, char *domain, char *buff,
dns_dirty = 1;
file_dirty = has_old;
new_lease = 0;
return fileno(lease_file);
}
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
{
/* changes to the config may change current leases. */
struct dhcp_lease *lease;
struct dhcp_config *config;
/* Deal with edits to the config file which may have changed the hostname
associated with a hardware address. Do this after the main loop so that
changes get written back out */
for (lease = leases; lease; lease = lease->next)
if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) &&
(config->hostname))
lease_set_hostname(lease, config->hostname, domain);
return fileno(lease_file);
}
void lease_update_dns(int force_dns)
void lease_update_file(int force, time_t now)
{
struct dhcp_lease *lease;
int i;
int i = force; /* avoid warning */
unsigned long expires;
#ifdef HAVE_BROKEN_RTC
if (force || new_lease)
{
lease_prune(NULL, now);
#else
if (file_dirty)
{
#endif
rewind(lease_file);
ftruncate(fileno(lease_file), 0);
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_BROKEN_RTC
if (lease->expires)
expires = (unsigned long) difftime(lease->expires, now);
else
expires = 0;
#else
expires = now; /* eliminate warning */
expires = (unsigned long)lease->expires;
#endif
fprintf(lease_file, "%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s ",
(unsigned long)lease->expires, lease->hwaddr[0], lease->hwaddr[1],
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 : "*");
@@ -129,13 +161,18 @@ void lease_update_dns(int force_dns)
fflush(lease_file);
fsync(fileno(lease_file));
file_dirty = 0;
new_lease = 0;
}
}
if (dns_dirty || force_dns)
void lease_update_dns(void)
{
struct dhcp_lease *lease;
if (dns_dirty)
{
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)
{
if (lease->fqdn)
@@ -173,6 +210,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if (lease->clid)
free(lease->clid);
free(lease);
leases_left++;
}
else
up = &lease->next;
@@ -220,7 +258,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr)
{
struct dhcp_lease *lease;
if (!(lease = malloc(sizeof(struct dhcp_lease))))
if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease))))
return NULL;
lease->clid = NULL;
@@ -245,6 +283,8 @@ struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_a
leases = lease;
file_dirty = 1;
new_lease = 1;
leases_left--;
return lease;
}
@@ -294,14 +334,12 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
lease_tmp->fqdn = NULL;
}
}
if (!new_name)
if (!new_name && (new_name = malloc(strlen(name) + 1)))
strcpy(new_name, name);
if (suffix && !new_fqdn && (new_fqdn = malloc(strlen(name) + strlen(suffix) + 2)))
{
new_name = malloc(strlen(name) + 1);
strcpy(new_name, name);
}
if (suffix && !new_fqdn)
{
new_fqdn = malloc(strlen(name) + strlen(suffix) + 2);
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
strcat(new_fqdn, suffix);

View File

@@ -14,152 +14,87 @@
#include "dnsmasq.h"
static char *add_iface(struct irec **list, unsigned int flags,
char *name, union mysockaddr *addr,
struct iname *names, struct iname *addrs,
struct iname *except)
static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *addr,
struct iname *names, struct iname *addrs,
struct iname *except)
{
struct irec *iface;
int fd, opt;
struct iname *tmp;
/* we may need to check the whitelist */
if (names)
{
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
{
tmp->found = 1;
break;
}
if (!(flags & IFF_LOOPBACK) && !tmp)
/* not on whitelist and not loopback */
return NULL;
}
if (addrs)
{
for (tmp = addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr))
{
tmp->found = 1;
break;
}
if (!tmp)
/* not on whitelist */
return NULL;
}
/* check blacklist */
if (except)
for (tmp = except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0)
return NULL;
/* we may need to check the whitelist */
if (names || addrs)
{
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;
}
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address
and we may be re-scanning. */
for (iface = *list; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
it is possible to have multiple interfaces with the same address */
for (; list; list = list->next)
if (sockaddr_isequal(&list->addr, addr))
break;
if (iface)
{
iface->valid = 1;
return NULL;
}
if ((fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
return "failed to create socket: %s";
/* Set SO_REUSEADDR on the socket, this allows is to bind
specific addresses even if BIND is running and has bound *:53 */
opt = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(fd, &addr->sa, sa_len(addr)) == -1)
{
int errsave = errno;
close(fd);
errno = errsave;
/* IPv6 interfaces sometimes return ENODEV to bind() for unknown
(to me) reasons. Don't treat that as fatal. */
return errno == ENODEV ? NULL : "failed to bind socket: %s";
}
if (list)
return NULL;
/* If OK, add it to the head of the list */
if (!(iface = malloc(sizeof(struct irec))))
{
close(fd);
return "cannot allocate interface";
}
iface->fd = fd;
iface = safe_malloc(sizeof(struct irec));
iface->addr = *addr;
iface->next = *list;
iface->valid = 1;
*list = iface;
return NULL;
return iface;
}
/* get all interfaces in system and for each one allowed add it to the chain
at interfacep. May be called more that once: interfaces which still exist
are left on the chain, those which have gone have sockets close()ed an are
unlinked. Return value is NULL if OK, an error string and the value of errno
on error. */
char *enumerate_interfaces(struct irec **interfacep,
struct iname *names,
struct iname *addrs,
struct iname *except,
struct dhcp_context *dhcp,
int port)
struct irec *enumerate_interfaces(struct iname *names,
struct iname *addrs,
struct iname *except,
int port)
{
/* this code is adapted from Stevens, page 434. It finally
destroyed my faith in the C/unix API */
int len = 100 * sizeof(struct ifreq);
int errsave, lastlen = 0;
struct irec *iface, *prev;
char *buf, *ptr, *err = NULL;
struct ifconf ifc;
struct irec *iface = NULL, *new;
char *buf, *ptr;
struct ifreq *ifr = NULL;
struct ifconf ifc;
int lastlen = 0;
int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0);
int rawfd = -1;
if (fd == -1)
return "cannot create socket to enumerate interfaces: %s";
/* make all interfaces as old. Any left that way after the scan are reaped. */
for (iface = *interfacep; iface; iface = iface->next)
iface->valid = 0;
die ("cannot create socket to enumerate interfaces: %s", NULL);
while (1)
{
if (!(buf = malloc(len)))
{
err = "cannot allocate buffer";
goto end;
}
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
{
err = "ioctl error while enumerating interfaces: %s";
goto end;
}
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
len += 10*sizeof(struct ifreq);
free(buf);
}
{
buf = safe_malloc(len);
for (ptr = buf; ptr < buf + ifc.ifc_len; )
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{
if (errno != EINVAL || lastlen != 0)
die ("ioctl error while enumerating interfaces: %s", NULL);
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
len += 10*sizeof(struct ifreq);
free(buf);
}
for (ptr = buf; ptr < buf + len; )
{
union mysockaddr addr;
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -168,10 +103,7 @@ char *enumerate_interfaces(struct irec **interfacep,
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
{
err = "cannot allocate buffer";
goto end;
}
die("cannot allocate buffer", NULL);
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
@@ -179,7 +111,7 @@ char *enumerate_interfaces(struct irec **interfacep,
ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
#endif
/* copy address since getting flags overwrites */
if (ifr->ifr_addr.sa_family == AF_INET)
{
@@ -202,14 +134,24 @@ char *enumerate_interfaces(struct irec **interfacep,
continue; /* unknown address family */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
die("ioctl error getting interface flags: %m", NULL);
/* 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))
{
err = "ioctl error getting interface flags: %m";
goto end;
struct iname *lo = safe_malloc(sizeof(struct iname));
lo->name = safe_string_alloc(ifr->ifr_name);
lo->next = names->next;
names->next = lo;
}
if ((err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
if ((new = add_iface(iface, ifr->ifr_name,
&addr, names, addrs, except)))
goto end;
{
new->next = iface;
iface = new;
}
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
@@ -252,141 +194,16 @@ char *enumerate_interfaces(struct irec **interfacep,
fclose(f);
}
if (found &&
(err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
&addr6, names, addrs, except)))
goto end;
if (found && (new = add_iface(iface, ifr->ifr_name,
&addr6, names, addrs, except)))
{
new->next = iface;
iface = new;
}
}
#endif /* LINUX */
/* dhcp is non-null only on the first call: set up the relevant
interface-related DHCP stuff here. DHCP is IPv4 only.
Because errors here are ultimately fatal we can return directly and not bother
closing the descriptor.
*/
if (dhcp && addr.sa.sa_family == AF_INET &&
!(ifr->ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
{
struct in_addr netmask, broadcast;
struct dhcp_context *context;
int opt = 1;
if (ioctl(fd, SIOCGIFNETMASK, ifr) < 0)
return "ioctl error getting interface netmask: %s";
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) < 0)
return "ioctl error getting interface broadcast address: %s";
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
for (context = dhcp; context; context = context->next)
if (!context->iface && /* may be more than one iface with same addr */
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->start.s_addr & netmask.s_addr)) &&
((addr.in.sin_addr.s_addr & netmask.s_addr) == (context->end.s_addr & netmask.s_addr)))
{
struct sockaddr_in saddr;
#ifdef HAVE_BPF
char filename[50];
int b = 0;
while (1)
{
sprintf(filename, "/dev/bpf%d", b);
if ((rawfd = open(filename, O_RDWR, 0)) == -1)
{
if (errno != EBUSY)
return"Cannot create DHCP BPF socket: %s";
b++;
}
else if (ioctl(rawfd, BIOCSETIF, ifr) < 0)
return "Can't attach interface to BPF device: %s";
else
break;
}
if (context->next)
return "no support for DHCP on multiple networks under this OS";
#endif
#ifdef HAVE_PF_PACKET
if (rawfd == -1 && /* same packet socket for all interfaces */
(rawfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1)
return "Cannot create DHCP packet socket: %s";
/* do this last so that the index is still in ifr for the
call to setsockopt(SO_BINDTODEVICE) */
if (ioctl(fd, SIOCGIFINDEX, ifr) < 0)
return "ioctl error getting interface index: %m";
context->ifindex = ifr->ifr_ifindex;
#endif
context->rawfd = rawfd;
context->serv_addr = addr.in.sin_addr;
context->netmask = netmask;
context->broadcast = broadcast;
if (!(context->iface = malloc(strlen(ifr->ifr_name) + 1)))
return "cannot allocate interface name";
strcpy(context->iface, ifr->ifr_name);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
if ((context->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
return "cannot create DHCP server socket: %s";
if (setsockopt(context->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
#ifdef HAVE_PF_PACKET
setsockopt(context->fd, SOL_SOCKET, SO_BINDTODEVICE, ifr, sizeof(*ifr)) == -1 ||
#endif
setsockopt(context->fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) == -1)
return "failed to set options on DHCP socket: %s";
if (bind(context->fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
return "failed to bind DHCP server socket: %s";
}
}
}
#ifdef HAVE_BPF
/* now go through the interfaces again, looking for AF_LINK records
to get hardware addresses from */
for (ptr = buf; ptr < buf + ifc.ifc_len; )
{
struct dhcp_context *context;
#ifdef HAVE_SOCKADDR_SA_LEN
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len)))
{
err = "cannot allocate buffer";
goto end;
}
memcpy(ifr, ptr, ifr_len);
ptr += ifr_len;
#else
ifr = (struct ifreq *)ptr;
ptr += sizeof(struct ifreq);
#endif
if (ifr->ifr_addr.sa_family == AF_LINK)
for (context = dhcp; context; context = context->next)
if (context->iface && strcmp(context->iface, ifr->ifr_name) == 0)
memcpy(context->hwaddr, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
}
#endif
end:
errsave = errno; /* since errno gets overwritten by close */
if (buf)
free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -394,37 +211,91 @@ char *enumerate_interfaces(struct irec **interfacep,
free(ifr);
#endif
close(fd);
if (err)
{
errno = errsave;
return err;
return iface;
}
struct listener *create_wildcard_listeners(int port)
{
union mysockaddr addr;
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);
addr.in6.sin6_flowinfo = htonl(0);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
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 listen;
}
struct listener *create_bound_listeners(struct irec *interfaces)
{
struct listener *listeners = NULL;
struct irec *iface;
int opt = 1;
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);
}
/* now remove interfaces which were not found on this scan */
for(prev = NULL, iface = *interfacep; iface; )
{
if (iface->valid)
{
prev = iface;
iface = iface->next;
}
else
{
struct irec *tmp = iface;
close(iface->fd);
/* remove pending queries from this interface */
reap_forward(iface->fd);
/* unlink */
if (prev)
prev->next = iface->next;
else
*interfacep = iface->next;
iface = iface->next;
free(tmp);
}
}
return NULL; /* no error */
return listeners;
}
static struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
@@ -607,7 +478,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = htonl(0);
source_addr.in6.sin6_addr= in6addr_any;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(query_port);
}
#endif /* IPV6 */

View File

@@ -21,7 +21,7 @@ struct myoption {
int val;
};
#define OPTSTRING "DNLERowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:"
#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:"
static struct myoption opts[] = {
{"version", 0, 0, 'v'},
@@ -66,6 +66,10 @@ static struct myoption opts[] = {
{"query-port", 1, 0, 'Q'},
{"except-interface", 1, 0, 'I'},
{"domain-needed", 0, 0, 'D'},
{"dhcp-lease-max", 1, 0, 'X' },
{"bind-interfaces", 0, 0, 'z'},
{"read-ethers", 0, 0, 'Z' },
{"alias", 1, 0, 'V' },
{0, 0, 0, 0}
};
@@ -85,9 +89,11 @@ static struct optflags optmap[] = {
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
{ 'L', OPT_LOCALMX},
{ 'N', OPT_NO_NEG},
{ 'D', OPT_NODOTS_LOCAL},
{ 'L', OPT_LOCALMX },
{ 'N', OPT_NO_NEG },
{ 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
@@ -134,8 +140,12 @@ static char *usage =
"-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"
"-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"
"-X, --dhcp-lease-max=number Specify maximum number of DHCP leases (defaults to %d).\n"
"-z, --bind-interfaces Bind only to interfaces in use.\n"
"-Z, --read-ethers Read DHCP static host information from " ETHERSFILE ".\n"
"\n";
@@ -146,15 +156,18 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
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,
char **dhcp_sname, struct in_addr *dhcp_next_server)
char **dhcp_sname, struct in_addr *dhcp_next_server, int *dhcp_max,
unsigned int *min_leasetime, struct doctor **doctors)
{
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 line_save = 0, lineno = 0;
opterr = 0;
*min_leasetime = UINT_MAX;
while (1)
{
@@ -166,24 +179,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] == '\t'))
while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if (*buff == 0)
continue;
@@ -200,7 +227,11 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if (strcmp(opts[i].name, buff) == 0)
option = opts[i].val;
if (!option)
die("bad option %s", buff);
{
sprintf(buff, "bad option at line %d of %s ", lineno, conffile);
complain(buff, NULL);
continue;
}
}
}
@@ -208,6 +239,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")))
{
@@ -220,7 +252,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if (!f && option == 'w')
{
fprintf (stderr, usage, CACHESIZ);
fprintf (stderr, usage, CACHESIZ, MAXLEASES);
exit(0);
}
@@ -236,21 +268,46 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
flags |= optmap[i].flag;
option = 0;
if (f && optarg)
die("extraneous parameter for %s in config file.", buff);
{
sprintf(buff, "extraneous parameter at line %d of %s ", lineno, conffile);
complain(buff, NULL);
}
break;
}
if (option && option != '?')
{
if (f && !optarg)
die("missing parameter for %s in config file.", buff);
{
sprintf(buff, "missing parameter at line %d of %s ", lineno, conffile);
complain(buff, NULL);
continue;
}
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);
@@ -332,7 +389,8 @@ 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->found = 0;
if (strchr(optarg, ':'))
flags |= OPT_NOWILD;
break;
}
@@ -342,6 +400,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;
}
@@ -364,8 +424,6 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
struct iname *new = safe_malloc(sizeof(struct iname));
new->next = *if_addrs;
*if_addrs = new;
new->found = 0;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, optarg, &new->addr.in.sin_addr))
{
@@ -392,7 +450,14 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
#endif
else
option = '?'; /* error */
{
option = '?'; /* error */
free(new);
new = NULL;
}
if (new)
*if_addrs = new;
break;
}
@@ -410,10 +475,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
char *domain;
*end = 0;
if (!canonicalise(optarg))
{
option = '?';
break;
}
option = '?';
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
serv = safe_malloc(sizeof(struct server));
serv->next = newlist;
@@ -443,20 +505,14 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
newlist->flags |= SERV_LITERAL_ADDRESS;
if (!(newlist->flags & SERV_TYPE))
{
option = '?';
break;
}
option = '?';
}
if (!*optarg)
{
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
{
option = '?';
break;
}
option = '?';
}
else
{
@@ -531,16 +587,26 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
serv = newlist;
while (serv->next)
if (option == '?')
while (newlist)
{
serv = newlist;
newlist = newlist->next;
free(serv);
}
else
{
serv->next->flags = serv->flags;
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
serv = serv->next;
serv = newlist;
while (serv->next)
{
serv->next->flags = serv->flags;
serv->next->addr = serv->addr;
serv->next->source_addr = serv->source_addr;
serv = serv->next;
}
serv->next = *serv_addrs;
*serv_addrs = newlist;
}
serv->next = *serv_addrs;
*serv_addrs = newlist;
break;
}
@@ -570,34 +636,77 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*local_ttl = (unsigned long)atoi(optarg);
break;
case 'X':
*dhcp_max = atoi(optarg);
break;
case 'F':
{
char *comma;
int k, leasepos = 2;
char *cp, *comma, *a[5] = { NULL, NULL, NULL, NULL, NULL };
struct dhcp_context *new = safe_malloc(sizeof(struct dhcp_context));
new->next = *dhcp;
*dhcp = new;
new->lease_time = DEFLEASE;
new->netmask.s_addr = 0;
new->broadcast.s_addr = 0;
new->netid = NULL;
if (!(comma = strchr(optarg, ',')) || (*comma = 0) ||
((new->start.s_addr = inet_addr(optarg)) == (in_addr_t)-1))
{
option = '?';
for (cp = optarg; *cp; cp++)
if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9')))
break;
if (*cp != ',' && (comma = strchr(optarg, ',')))
{
*comma = 0;
new->netid = safe_string_alloc(optarg);
a[0] = comma + 1;
}
else
a[0] = optarg;
for (k = 1; k < 5; k++)
{
if (!(a[k] = strchr(a[k-1], ',')))
break;
*(a[k]++) = 0;
}
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 == '?')
{
free(new);
break;
}
else
*dhcp = new;
optarg = comma + 1;
if ((comma = strchr(optarg, ',')))
if (k >= 3 && strchr(a[2], '.') &&
((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
leasepos = 3;
if (k >= 4 && strchr(a[3], '.') &&
((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
leasepos = 4;
if (k >= leasepos+1)
{
*(comma++) = 0;
if (strcmp(comma, "infinite") == 0)
if (strcmp(a[leasepos], "infinite") == 0)
new->lease_time = 0xffffffff;
else
{
int fac = 1;
if (strlen(comma) > 0)
if (strlen(a[leasepos]) > 0)
{
switch (comma[strlen(comma) - 1])
switch (a[leasepos][strlen(a[leasepos]) - 1])
{
case 'h':
case 'H':
@@ -606,47 +715,37 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case 'm':
case 'M':
fac *= 60;
comma[strlen(comma) - 1] = 0;
/* fall through */
case 's':
case 'S':
a[leasepos][strlen(a[leasepos]) - 1] = 0;
}
new->lease_time = atoi(comma) * fac;
new->lease_time = atoi(a[leasepos]) * fac;
}
}
}
if ((new->end.s_addr = inet_addr(optarg)) == (in_addr_t)-1)
{
option = '?';
break;
}
new->last = new->start;
new->iface = NULL;
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;
*dhcp_conf = new;
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;
@@ -654,37 +753,60 @@ 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;
int len;
arg += 3; /* dump id: */
if (strchr(arg, ':'))
{
s = (strlen(arg)/3) + 1;
/* decode in place */
for (len = 0; len < s; len++)
/* decode hex in place */
char *p = arg, *q = arg, *r;
while (*p)
{
if (arg[(len*3)+2] != ':')
option = '?';
arg[(len*3)+2] = 0;
arg[len] = strtol(&arg[len*3], NULL, 16);
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 = 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;
@@ -696,7 +818,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;
@@ -715,7 +840,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case 'm':
case 'M':
fac *= 60;
/* fall through */
case 's':
case 'S':
*lastp = 0;
}
}
@@ -729,82 +856,185 @@ 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;
{
new->lease_time = atoi(a[j]) * fac;
new->flags |= CONFIG_TIME;
}
}
if (option == '?')
{
if (new->flags & CONFIG_NAME)
free(new->hostname);
if (new->flags & CONFIG_CLID)
free(new->clid);
if (new->flags & CONFIG_NETID)
free(new->netid);
free(new);
}
else
{
if ((new->flags & CONFIG_TIME) && new->lease_time < *min_leasetime)
*min_leasetime = new->lease_time;
*dhcp_conf = new;
}
break;
}
case 'O':
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
char *cp, *comma = strchr(optarg, ',');
int addrs, is_addr;
char *cp, *comma;
int addrs, digs, is_addr, is_hex, is_dec;
new->next = *dhcp_opts;
new->len = 0;
new->is_addr = 0;
*dhcp_opts = new;
new->netid = NULL;
if ((comma = strchr(optarg, ',')))
{
*comma = 0;
for (cp = optarg; *cp; cp++)
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
break;
if (*cp)
{
new->netid = safe_string_alloc(optarg);
optarg = comma + 1;
if ((comma = strchr(optarg, ',')))
*comma = 0;
}
}
if ((new->opt = atoi(optarg)) == 0)
{
option = '?';
free(new);
break;
}
*dhcp_opts = new;
if (!comma)
break;
*comma = 0;
/* check for non-address list characters */
for (addrs = 1, is_addr = 0, cp = comma+1; *cp; cp++)
/* 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;
}
}
}
break;
}
@@ -826,13 +1056,54 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
break;
}
case 'V':
{
char *a[3] = { NULL, NULL, NULL };
int k;
struct in_addr in, out, mask;
struct doctor *new;
mask.s_addr = 0xffffffff;
a[0] = optarg;
for (k = 1; k < 4; k++)
{
if (!(a[k] = strchr(a[k-1], ',')))
break;
*(a[k]++) = 0;
}
if ((k < 2) ||
((in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
((out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
{
option = '?';
break;
}
if (k == 3)
mask.s_addr = inet_addr(a[2]);
new = safe_malloc(sizeof(struct doctor));
new->in = in;
new->out = out;
new->mask = mask;
new->next = *doctors;
*doctors = new;
break;
}
}
}
if (option == '?')
{
if (f)
die("bad argument for option %s", buff);
{
sprintf(buff, "error at line %d of %s ", lineno, conffile);
complain(buff, NULL);
}
else
die("bad command line options: try --help.", NULL);
}
@@ -884,7 +1155,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*resolv_files = 0;
else if (*resolv_files && (*resolv_files)->next && (flags & OPT_NO_POLL))
die("only one resolv.conf file allowed in no-poll mode.", NULL);
return flags;
}

View File

@@ -440,7 +440,19 @@ void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now
cache_end_insert();
}
void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now)
static void dns_doctor(struct doctor *doctor, struct in_addr *addr)
{
for (; doctor; doctor = doctor->next)
if ((doctor->in.s_addr & doctor->mask.s_addr) == (addr->s_addr & doctor->mask.s_addr))
{
addr->s_addr &= ~doctor->mask.s_addr;
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
break;
}
}
void extract_addresses(HEADER *header, unsigned int qlen, char *name,
time_t now, struct doctor *doctors)
{
unsigned char *p, *psave, *endrr;
int qtype, qclass, rdlen;
@@ -477,8 +489,11 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
}
if (qtype == T_A) /* A record. */
cache_insert(name, (struct all_addr *)p, now,
ttl, F_IPV4 | F_FORWARD);
{
dns_doctor(doctors, (struct in_addr *)p);
cache_insert(name, (struct all_addr *)p, now,
ttl, F_IPV4 | F_FORWARD);
}
#ifdef HAVE_IPV6
else if (qtype == T_AAAA) /* IPV6 address record. */
cache_insert(name, (struct all_addr *)p, now,
@@ -546,8 +561,11 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
return;
if (qtype == T_A) /* A record. */
cache_insert(name, (struct all_addr *)p, now,
cttl, F_IPV4 | F_FORWARD);
{
dns_doctor(doctors, (struct in_addr *)p);
cache_insert(name, (struct all_addr *)p, now,
cttl, F_IPV4 | F_FORWARD);
}
#ifdef HAVE_IPV6
else if (qtype == T_AAAA) /* IPV6 address record. */
cache_insert(name, (struct all_addr *)p, now,
@@ -620,7 +638,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
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)
else if (flags == F_NOERR || flags == F_QUERY)
header->rcode = NOERROR; /* empty domain */
else if (flags == F_NXDOMAIN)
header->rcode = NXDOMAIN;

View File

@@ -25,14 +25,17 @@
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_CLIENT_ID 61
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_REQUESTED_OPTIONS 55
#define OPTION_MESSAGE 56
#define OPTION_MAXMESSAGE 57
#define OPTION_T1 58
#define OPTION_T2 59
#define OPTION_CLIENT_ID 61
#define OPTION_END 255
#define DHCPDISCOVER 1
@@ -45,43 +48,84 @@
#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_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);
static void *option_ptr(unsigned char *opt);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type);
static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
struct dhcp_opt *config_opts,
char *domainname, char *hostname);
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
int iface_mtu, char *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 dhcp_packet *mess,
int dhcp_reply(struct dhcp_context *context,
struct in_addr iface_addr,
char *iface_name,
int iface_mtu,
struct udp_dhcp_packet *rawpacket,
unsigned int sz, time_t now, char *namebuff,
struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs,
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;
int clid_len;
struct dhcp_packet *mess = &rawpacket->data;
unsigned char *p = mess->options;
/* default max reply packet length, max be overridden */
unsigned char *end = (unsigned char *)(rawpacket + 1);
char *hostname = NULL;
char *req_options = NULL;
char *message = NULL;
unsigned int renewal_time, expires_time, def_time;
struct dhcp_config *config;
char *netid;
if (mess->op != BOOTREQUEST ||
mess->htype != ARPHRD_ETHER ||
mess->hlen != ETHER_ADDR_LEN ||
mess->cookie != htonl(DHCP_COOKIE))
return 0;
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)
return 0;
#else
if (mess->htype != ARPHRD_ETHER &&
mess->htype != ARPHRD_IEEE802)
return 0;
#endif
mess->op = BOOTREPLY;
if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE)))
{
int maxsize = (int)option_uint(opt, 2);
if (maxsize > DNSMASQ_PACKETSZ)
maxsize = DNSMASQ_PACKETSZ;
if (maxsize > iface_mtu)
maxsize = iface_mtu;
end = ((unsigned char *)rawpacket) + maxsize;
}
/* If there is no client identifier option, use the hardware address */
if ((opt = option_find(mess, sz, OPTION_CLIENT_ID)))
{
@@ -104,9 +148,9 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
memcpy(req_options, option_ptr(opt), len);
req_options[len] = OPTION_END;
}
if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) &&
config->hostname)
have_config(config, CONFIG_NAME))
hostname = config->hostname;
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME)))
{
@@ -120,30 +164,30 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
/* ensure there are no strange chars in there */
if (!canonicalise(hostname))
hostname = NULL;
}
if (hostname)
{
char *dot = strchr(hostname, '.');
if (dot)
else
{
if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix))
char *dot = strchr(hostname, '.');
if (dot)
{
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
hostname = NULL;
if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix))
{
syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname);
hostname = NULL;
}
else
*dot = 0; /* truncate */
}
else
*dot = 0; /* truncate */
}
}
/* 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;
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
netid = have_config(config, CONFIG_NETID) ? config->netid : context->netid;
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
{
unsigned int req_time = option_uint(opt);
unsigned int req_time = option_uint(opt, 4);
if (def_time == 0xffffffff ||
(req_time != 0xffffffff && req_time < def_time))
@@ -165,77 +209,101 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
switch (opt[2])
{
case DHCPRELEASE:
if (lease)
{
log_packet("RELEASE", &lease->addr, mess->chaddr, context->iface);
lease_prune(lease, now);
case DHCPDECLINE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
return 0;
/* sanitise any message. Paranoid? Moi? */
if ((opt = option_find(mess, sz, OPTION_MESSAGE)))
{
char *p = option_ptr(opt), *q = namebuff;
int i;
for (i = option_len(opt); i > 0; i--)
{
char c = *p++;
if (isprint(c))
*q++ = c;
}
*q++ = 0; /* add terminator */
message = namebuff;
}
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
return 0;
log_packet("DECLINE", option_ptr(opt), mess->chaddr, iface_name, message);
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
lease_prune(lease, now);
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->flags &= ~CONFIG_ADDR ;
}
return 0;
case DHCPRELEASE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) ||
(iface_addr.s_addr != option_addr(opt).s_addr))
return 0;
log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL);
if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
lease_prune(lease, now);
return 0;
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, context->iface);
if (lease)
mess->yiaddr = lease->addr;
else if (config && config->addr.s_addr && !lease_find_by_addr(config->addr))
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)))
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;
}
message = "no address available";
log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, iface_name, message);
if (message)
return 0;
bootp_option_put(mess, dhcp_file, dhcp_sname);
mess->siaddr = dhcp_next_server;
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, expires_time);
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, NULL);
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
NULL, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, context->iface);
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
return p - (unsigned char *)mess;
case DHCPREQUEST:
if (mess->ciaddr.s_addr)
{
/* RENEWING or REBINDING */
/* Must exist a lease for this address */
log_packet("REQUEST", &mess->ciaddr, mess->chaddr, context->iface);
if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
{
log_packet("NAK", &mess->ciaddr, mess->chaddr, context->iface);
mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
bootp_option_put(mess, NULL, NULL);
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK);
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
return (unsigned char *)mess - p; /* -ve to force bcast */
}
mess->yiaddr = mess->ciaddr;
}
else
if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
{
/* SELECTING or INIT_REBOOT */
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) &&
(context->serv_addr.s_addr != option_addr(opt).s_addr))
return 0;
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP)))
return 0;
mess->yiaddr = option_addr(opt);
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, context->iface);
/* The RFC says that this is already zero, but there exist
real-world counter examples. */
mess->ciaddr.s_addr = 0;
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) &&
(iface_addr.s_addr != option_addr(opt).s_addr))
return 0;
/* If a lease exists for this host and another address, squash it. */
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
@@ -246,24 +314,50 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
/* accept addresses in the dynamic range or ones allocated statically to
particular hosts or an address which the host already has. */
if (!lease &&
!address_available(context, mess->yiaddr) &&
(!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr))
{
log_packet("NAK", &mess->yiaddr, mess->chaddr, context->iface);
mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0;
bootp_option_put(mess, NULL, NULL);
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK);
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
return (unsigned char *)mess - p; /* -ve to force bcast */
if (!lease)
{
if (!address_available(context, mess->yiaddr) &&
(!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
message = "address unavailable";
else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
message = "no leases left";
}
if (!lease &&
!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
}
else
{
/* RENEWING or REBINDING */
/* Must exist a lease for this address */
if (!mess->ciaddr.s_addr)
return 0;
}
mess->yiaddr = mess->ciaddr;
if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr)
message = "lease not found";
}
/* 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 (have_config(config, CONFIG_DISABLE))
message = "disabled";
log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
if (message)
{
log_packet("NAK", &mess->yiaddr, mess->chaddr, iface_name, message);
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);
mess->flags |= htons(0x8000); /* broadcast */
return p - (unsigned char *)mess;
}
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
lease_set_hwaddr(lease, mess->chaddr);
lease_set_hostname(lease, hostname, domain_suffix);
@@ -271,38 +365,54 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
bootp_option_put(mess, dhcp_file, dhcp_sname);
mess->siaddr = dhcp_next_server;
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, renewal_time);
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname);
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
log_packet("ACK", &mess->yiaddr, mess->chaddr, context->iface);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
if (renewal_time != 0xffffffff)
{
unsigned short fuzz = rand16();
while (fuzz > (renewal_time/16))
fuzz = fuzz/2;
p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
}
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
return p - (unsigned char *)mess;
case DHCPINFORM:
log_packet("INFORM", &mess->ciaddr, mess->chaddr, context->iface);
if (have_config(config, CONFIG_DISABLE))
{
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, "ignored");
return 0;
}
p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr));
p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname);
p = option_put(p, &mess->options[308], OPTION_END, 0, 0);
log_packet("INFORM", &mess->ciaddr, mess->chaddr, iface_name, NULL);
log_packet("ACK", &mess->ciaddr, mess->chaddr, context->iface);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
hostname, router, iface_addr, iface_mtu, netid);
p = option_put(p, end, OPTION_END, 0, 0);
log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
return p - (unsigned char *)mess;
}
return 0;
}
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface)
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string)
{
syslog(LOG_INFO, "DHCP%s(%s)%s%s hwaddr=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
syslog(LOG_INFO, "DHCP%s(%s)%s%s %.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s",
type,
interface,
addr ? " " : "",
addr ? inet_ntoa(*addr) : "",
hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5],
string ? " " : "",
string ? string : "");
}
static int option_len(unsigned char *opt)
@@ -326,14 +436,17 @@ static struct in_addr option_addr(unsigned char *opt)
return ret;
}
static unsigned int option_uint(unsigned char *opt)
static unsigned int option_uint(unsigned char *opt, int size)
{
/* this worries about unaligned data and byte order */
unsigned int ret;
memcpy(&ret, option_ptr(opt), sizeof(unsigned int));
unsigned int ret = 0;
int i;
unsigned char *p = option_ptr(opt);
return ntohl(ret);
for (i = 0; i < size; i++)
ret = (ret << 8) | *p++;
return ret;
}
static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname)
@@ -349,21 +462,36 @@ static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sna
static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val)
{
int i;
if (p + len + 2 < end)
/* always keep one octet space for the END option. */
if ((opt == OPTION_END) || (p + len + 3 < end))
{
*(p++) = opt;
*(p++) = len;
for (i = 0; i < len; i++)
*(p++) = val >> (8 * (len - (i + 1)));
if (opt != OPTION_END)
{
*(p++) = len;
for (i = 0; i < len; i++)
*(p++) = val >> (8 * (len - (i + 1)));
}
}
return p;
}
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
{
if (p + strlen(string) + 3 < end)
{
*(p++) = opt;
*(p++) = strlen(string);
memcpy(p, string, strlen(string));
p += strlen(string);
}
return p;
}
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload)
{
if (!p)
return NULL;
@@ -425,10 +553,11 @@ static int in_list(unsigned char *list, int opt)
return 0;
}
static struct dhcp_opt *option_find2(struct dhcp_opt *opts, int opt)
static struct dhcp_opt *option_find2(char *netid, struct dhcp_opt *opts, int opt)
{
for (; opts; opts = opts->next)
if (opts->opt == opt)
if (opts->opt == opt &&
(!opts->netid || (netid && strcmp(opts->netid, netid) == 0)))
return opts;
return NULL;
}
@@ -437,87 +566,92 @@ static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end,
unsigned char *req_options,
struct dhcp_opt *config_opts,
char *domainname, char *hostname)
char *domainname, char *hostname,
struct in_addr router,
struct in_addr iface_addr,
int iface_mtu, char *netid)
{
int i;
if (!req_options)
return p;
if (in_list(req_options, OPTION_MAXMESSAGE))
p = option_put(p, end, OPTION_MAXMESSAGE, 2, sizeof(struct udp_dhcp_packet));
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(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(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(config_opts, OPTION_ROUTER))
p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->serv_addr.s_addr));
!option_find2(netid, config_opts, OPTION_ROUTER))
p = option_put(p, end, OPTION_ROUTER, INADDRSZ,
ntohl(router.s_addr));
if (in_list(req_options, OPTION_DNSSERVER) &&
!option_find2(config_opts, OPTION_DNSSERVER))
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->serv_addr.s_addr));
!option_find2(netid, config_opts, OPTION_DNSSERVER))
p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
if (in_list(req_options, OPTION_DOMAINNAME) &&
!option_find2(config_opts, OPTION_DOMAINNAME) &&
domainname && (p + strlen(domainname) + 2 < end))
{
*(p++) = OPTION_DOMAINNAME;
*(p++) = strlen(domainname);
memcpy(p, domainname, strlen(domainname));
p += strlen(domainname);
}
if (domainname && in_list(req_options, 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
--dhcp-option=12,<name> */
if (in_list(req_options, OPTION_HOSTNAME) &&
hostname && (p + strlen(hostname) + 2 < end))
{
*(p++) = OPTION_HOSTNAME;
*(p++) = strlen(hostname);
memcpy(p, hostname, strlen(hostname));
p += strlen(hostname);
}
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++)
{
struct dhcp_opt *opt = option_find2(config_opts, req_options[i]);
if (req_options[i] != OPTION_HOSTNAME &&
req_options[i] != OPTION_MAXMESSAGE &&
opt && (p + opt->len + 2 < end))
struct dhcp_opt *opt;
if (req_options[i] == OPTION_HOSTNAME ||
req_options[i] == OPTION_MAXMESSAGE ||
!(opt = option_find2(netid, config_opts, req_options[i])) ||
(p + opt->len + 3 >= end))
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, &context->serv_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;
}

View File

@@ -61,17 +61,15 @@ unsigned short rand16(void)
else
{
s = (char *) &seed;
while ( (c < sizeof(seed)) &&
((n = read(fd, sbuf, sizeof(seed)) > 0)) )
while ((c < sizeof(seed)) &&
((n = read(fd, sbuf, sizeof(seed)) > 0)))
{
memcpy(s, sbuf, n);
s += n;
c += n;
}
if (n < 0)
{
seed = badseed;
}
seed = badseed;
close(fd);
}
@@ -215,3 +213,18 @@ int hostname_isequal(unsigned char *a, unsigned char *b)
return 1;
}
time_t dnsmasq_time(int fd)
{
#ifdef HAVE_BROKEN_RTC
/* we use uptime as a time-base, rather than epoch time
because epoch time can break when a machine contacts
a nameserver and updates it. */
char buf[30];
lseek(fd, 0, SEEK_SET);
read(fd, buf, 30);
return (time_t)atol(buf);
#else
fd = 0; /* stop warning */
return time(NULL);
#endif
}