Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a84fa1d085 | ||
|
|
33820b7ed9 | ||
|
|
8a911ccc75 | ||
|
|
1cff166d37 | ||
|
|
44a2a3165c | ||
|
|
b49644f39f | ||
|
|
1ab84e2f35 |
297
CHANGELOG
297
CHANGELOG
@@ -666,4 +666,299 @@ release 2.0
|
||||
domains and IPv6 queries. Thanks to Roy Marples for
|
||||
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.
|
||||
|
||||
Allow empty DHCP options, like so: dhpc-option=44
|
||||
|
||||
Allow single-byte DHCP options like so: dhcp-option=20,1
|
||||
|
||||
Allow comments on the same line as options in
|
||||
/etc/dnsmasq.conf
|
||||
|
||||
Don't complain when the same name and address is
|
||||
allocated to a host using DHCP and /etc/hosts.
|
||||
|
||||
Added to the example configuration the dnsmasq equivalent
|
||||
of the ISC dhcpd settings given in
|
||||
http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
|
||||
|
||||
Fixed long-existing strangeness in Linux IPv6 interface
|
||||
discovery code. The flags field in /proc/net/if_inet6 is
|
||||
_not_ the interface flags.
|
||||
|
||||
Fail gracefully when getting an ENODEV error when trying
|
||||
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
|
||||
dhcp-host=<mac addr>,wibble
|
||||
or even
|
||||
dhcp-host=wibble
|
||||
and in /etc/hosts have
|
||||
wibble 1.2.3.4
|
||||
and for the correct thing to happen. Note that some sort
|
||||
of dhcp-host line is still needed, it's not possible for
|
||||
random host to claim an address in /etc/hosts without
|
||||
some explicit configuration.
|
||||
|
||||
Make 0.0.0.0 in a dhcp-option to mean "the machine
|
||||
running dnsmasq".
|
||||
|
||||
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". -
|
||||
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. - thanks to
|
||||
Jonathon Hudson for spotting that.
|
||||
|
||||
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.
|
||||
|
||||
release 2.7
|
||||
Allow the dhcp-host specification of id:* which makes
|
||||
dnsmasq ignore any client-id. This is useful to ensure
|
||||
that a dual-boot machine sees the same lease when one OS
|
||||
gives a client-id and the other doesn't. It's also useful
|
||||
when PXE boot DHCP does not use client IDs but the OS it boots
|
||||
does. Thanks to Grzegorz Nosek for suggesting this enhancement.
|
||||
|
||||
No longer assume that ciaddr is zero in received DHCPDISCOVER
|
||||
messages, just for security against broken clients.
|
||||
|
||||
Set default of siaddr field to the address of the machine running
|
||||
dnsmasq when not explicitly set using dhcp-boot
|
||||
option. This is the ISC dhcpd behaviour.
|
||||
|
||||
Send T1 and T2 options in DHCPOFFER packets. This is required
|
||||
by the DHCP client in some JetDirect printers. Thanks
|
||||
to Paul Mattal for work on this.
|
||||
|
||||
Fixed bug with DHCP on OpenBSD reported by Dominique Jacquel.
|
||||
The code which added loopback interfaces to the list
|
||||
was confusing the DHCP code, which expected one interface only.
|
||||
Solved by adding loopback interfaces to address list instead.
|
||||
|
||||
Add dhcp-vendorclass option to allow options to be sent only
|
||||
to certain classes of clients.
|
||||
|
||||
Tweaked option search code so that if a netid-qualified
|
||||
option is used, any unqualified option is ignored.
|
||||
|
||||
Changed the method of picking new dynamic IP
|
||||
addresses. This used to use the next consecutive
|
||||
address as long it was free, now it uses a hash
|
||||
from the client hardware address. This reduces the amount
|
||||
of address movement for clients which let their lease
|
||||
expire and allows consecutive DHCPOFFERS to the same host
|
||||
to (almost always) be for the same address, without
|
||||
storing state before a lease is granted.
|
||||
|
||||
Tweaked option handling code to return all possible
|
||||
options rather than none when DHCP "requested options"
|
||||
field is missing. This fixes interoperability with
|
||||
ancient IBM LANMAN DHCP clients. Thanks to Jim Louvau for
|
||||
help with this.
|
||||
|
||||
|
||||
63
FAQ
63
FAQ
@@ -154,6 +154,69 @@ A: [note: this was written in September 2003, things may well change.]
|
||||
registries pull the same stunt; there is a list of them all, and
|
||||
the addresses to block, at http://winware.org/bogus-domains.txt
|
||||
|
||||
Q: This new DHCP server is well and good, but it doesn't work for me.
|
||||
What's the problem?
|
||||
|
||||
A: There are a couple of configuration gotchas which have been
|
||||
encountered by people moving from the ISC dhcpd to the dnsmasq
|
||||
integrated DHCP daemon. Both are related to differences in
|
||||
in the way the two daemons bypass the IP stack to do "ground up"
|
||||
IP configuration and can lead to the dnsmasq daemon failing
|
||||
whilst the ISC one works.
|
||||
|
||||
The first thing to check is the broadcast address set for the
|
||||
ethernet interface. This is normally the adddress on the connected
|
||||
network with all ones in the host part. For instance if the
|
||||
address of the ethernet interface is 192.168.55.7 and the netmask
|
||||
is 255.255.255.0 then the broadcast address should be
|
||||
192.168.55.255. Having a broadcast address which is not on the
|
||||
network to which the interface is connected kills things stone
|
||||
dead.
|
||||
|
||||
The second potential problem relates to firewall rules: since the ISC
|
||||
daemon in some configurations bypasses the kernel firewall rules
|
||||
entirely, the ability to run the ISC daemon does not indicate
|
||||
that the current configuration is OK for the dnsmasq daemon.
|
||||
For the dnsmasq daemon to operate it's vital that UDP packets to
|
||||
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.
|
||||
|
||||
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -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)
|
||||
|
||||
@@ -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
|
||||
------------------
|
||||
|
||||
@@ -40,7 +41,7 @@ to store its leases it is necessary to remove the configuration line in
|
||||
|
||||
To enable DHCP, simply add a line like this to /etc/dnsmasq.conf
|
||||
|
||||
dhcp-range=192.168.0.100,192,168.0.200,12h
|
||||
dhcp-range=192.168.0.100,192.168.0.200,12h
|
||||
|
||||
which tells dnsmasq to us the addresses 192.168.0.100 to 192.168.0.200
|
||||
for dynamic IP addresses, and to issue twelve hour leases.
|
||||
@@ -55,11 +56,13 @@ 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.
|
||||
dnsmasq manpage and the annotated example configuration file. Also
|
||||
note that for some ISC dhcpd to dnsmasq DHCP upgrades there may be
|
||||
firewall issues: see the FAQ for details of this.
|
||||
|
||||
|
||||
|
||||
130
dnsmasq-mdk.spec
130
dnsmasq-mdk.spec
@@ -1,130 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# General mumbojumbo
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.0
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
Vendor: Simon Kelley
|
||||
Packager: Simon Kelley
|
||||
Distribution: Mandrake Linux
|
||||
URL: http://www.thekelleys.org.uk/dnsmasq
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
Requires: chkconfig
|
||||
BuildRoot: /var/tmp/%{name}-%{version}
|
||||
Summary: A lightweight caching nameserver
|
||||
|
||||
%description
|
||||
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
|
||||
is designed to provide DNS and, optionally, DHCP, to a small network. It can
|
||||
serve the names of local machines which are not in the global DNS. The DHCP
|
||||
server integrates with the DNS server and allows machines with DHCP-allocated
|
||||
addresses to appear in the DNS with names configured either in each host or
|
||||
in a central configuration file. Dnsmasq supports static and dynamic DHCP
|
||||
leases and BOOTP for network booting of diskless machines.
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Build
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%build
|
||||
make
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Install
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/sbin
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/etc/rc.d/init.d
|
||||
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
|
||||
cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
|
||||
strip src/dnsmasq
|
||||
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
|
||||
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
|
||||
###############################################################################
|
||||
#
|
||||
# Clean up
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-install scriptlet
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%post
|
||||
/sbin/chkconfig --add dnsmasq
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Pre-uninstall scriptlet
|
||||
#
|
||||
# If there's a time when your package needs to have one last look around before
|
||||
# the user erases it, the place to do it is in the %preun script. Anything that
|
||||
# a package needs to do immediately prior to RPM taking any action to erase the
|
||||
# package, can be done here.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%preun
|
||||
if [ $1 = 0 ]; then # execute this only if we are NOT doing an upgrade
|
||||
service dnsmasq stop >/dev/null 2>&1
|
||||
/sbin/chkconfig --del dnsmasq
|
||||
fi
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Post-uninstall scriptlet
|
||||
#
|
||||
# The %postun script executes after the package has been removed. It is the
|
||||
# last chance for a package to clean up after itself.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%postun
|
||||
if [ "$1" -ge "1" ]; then
|
||||
service dnsmasq restart >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# File list
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0
|
||||
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
|
||||
%attr(0664,root,root) /etc/dnsmasq.conf
|
||||
%config /etc/rc.d/init.d/dnsmasq
|
||||
%config /etc/dnsmasq.conf
|
||||
%attr(0755,root,root) /usr/sbin/dnsmasq
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.bz2
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.0
|
||||
Version: 2.7
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: System Environment/Daemons
|
||||
@@ -58,7 +58,6 @@ cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
|
||||
strip src/dnsmasq
|
||||
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
|
||||
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
|
||||
gzip $RPM_BUILD_ROOT/usr/share/man/man8/dnsmasq.8
|
||||
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
|
||||
|
||||
###############################################################################
|
||||
@@ -128,6 +127,6 @@ fi
|
||||
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
|
||||
%attr(0664,root,root) /etc/dnsmasq.conf
|
||||
%attr(0755,root,root) /usr/sbin/dnsmasq
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq.8.gz
|
||||
%attr(0644,root,root) /usr/share/man/man8/dnsmasq*
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
###############################################################################
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.0
|
||||
Version: 2.7
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Productivity/Networking/DNS/Servers
|
||||
@@ -100,7 +100,7 @@ rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0
|
||||
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0 rpm/README.susefirewall
|
||||
%config /etc/init.d/dnsmasq
|
||||
%config /etc/dnsmasq.conf
|
||||
/usr/sbin/rcdnsmasq
|
||||
|
||||
157
dnsmasq.8
157
dnsmasq.8
@@ -23,7 +23,10 @@ is lightweight and easy to configure. It is intended as be run on
|
||||
small router/firewalls and provide a DNS (and optionally, DHCP) service to a LAN.
|
||||
.SH OPTIONS
|
||||
Note that in general missing parameters are allowed and switch off
|
||||
functions, for instance "--pid-file=" disables writing a PID file.
|
||||
functions, for instance "--pid-file=" disables writing a PID file. On
|
||||
BSD, unless the GNU getopt library is linked, the long form of the
|
||||
options does not work on the command line; it is still recognised in
|
||||
the configuration file.
|
||||
.TP
|
||||
.B \-h, --no-hosts
|
||||
Don't read the hostnames in /etc/hosts.
|
||||
@@ -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
|
||||
@@ -132,7 +161,7 @@ time is the one used.
|
||||
.TP
|
||||
.B \-R, --no-resolv
|
||||
Don't read /etc/resolv.conf. Get upstream servers only from the command
|
||||
line or /etc/dnsmasq.conf.
|
||||
line or the dnsmasq configuration file.
|
||||
.TP
|
||||
.B \-o, --strict-order
|
||||
By default, dnsmasq will send queries to any of the upstream servers
|
||||
@@ -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
|
||||
@@ -251,16 +298,40 @@ an infinite DHCP lease.
|
||||
.B --dhcp-host=lap,192.168.0.199
|
||||
tells
|
||||
dnsmasq to always allocate the machine lap the IP address
|
||||
192.168.0.199. Addresses allocated like this are not contrained to be
|
||||
192.168.0.199. Addresses allocated like this are not constrained to be
|
||||
in the range given by the --dhcp-range option, but they must be on the
|
||||
network being served by the DHCP server. It is allowed to use client identifiers rather than
|
||||
hardware addresses to identify hosts by prefixing with 'id:'. Thus:
|
||||
.B --dhcp-host=id:01:02:03:04,.....
|
||||
refers to the host with client identifier 01:02:03:04. It is also
|
||||
allowed to specify the client ID as text, like this:
|
||||
.B --dhcp-host=id:clientidastext,.....
|
||||
.B --dhcp-host=id:clientidastext,.....
|
||||
The special option id:* means "ignore any client-id
|
||||
and use MAC addresses only." This is useful when a client presents a client-id sometimes
|
||||
but not others.
|
||||
If a name appears in /etc/hosts, the associated address can be
|
||||
allocated to a DHCP lease, but only if a
|
||||
.B --dhcp-host
|
||||
option specifying the name also exists. 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
|
||||
@@ -272,20 +343,46 @@ specfied in RFC2132. For example, to set the default route option to
|
||||
192.168.4.4, do
|
||||
.B --dhcp-option=3,192.168.4.4
|
||||
and to set the time-server address to 192.168.0.4, do
|
||||
.B dhcp-option=42,192.168.0.4
|
||||
.B --dhcp-option=42,192.168.0.4
|
||||
The special address 0.0.0.0 is taken to mean "the address of the
|
||||
machine running dnsmasq". 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
|
||||
persuade dnsmasq to generate illegal DHCP packets with injudicious use
|
||||
of this flag.
|
||||
.TP
|
||||
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
|
||||
Map from a vendor-class string to a network id. Most DHCP clients provide a
|
||||
"vendor class" which represents, in some sense, the type of host. This options
|
||||
maps vendor classes to network ids, so that DHCP options may be selectively delivered
|
||||
to different classes of hosts. For example
|
||||
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
|
||||
will allow options to be set only for HP printers like so:
|
||||
.B --dhcp-option=printers,3,192.168.4.4
|
||||
.TP
|
||||
.B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]]
|
||||
Set BOOTP options to be returned by the DHCP server. These are needed
|
||||
for machines which network boot, and tell the machine where to collect
|
||||
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;
|
||||
@@ -301,12 +398,15 @@ both as "laptop" and "laptop.thekelleys.org.uk".
|
||||
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. The format of this
|
||||
At startup, dnsmasq reads /etc/dnsmasq.conf, if it exists. (On
|
||||
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, /etc/dnsmasq.conf overrides
|
||||
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
|
||||
@@ -314,7 +414,7 @@ clears its cache and then re-loads /etc/hosts. If
|
||||
.B
|
||||
--no-poll
|
||||
is set SIGHUP also re-reads /etc/resolv.conf. SIGHUP
|
||||
does NOT re-read /etc/dnsmasq.conf.
|
||||
does NOT re-read the configuration file.
|
||||
.PP
|
||||
When it receives a SIGUSR1,
|
||||
.B dnsmasq
|
||||
@@ -325,14 +425,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
|
||||
@@ -359,7 +451,7 @@ dnsmasq can be set to poll both /etc/ppp/resolv.conf and
|
||||
last, giving automatic switching between DNS servers.
|
||||
.PP
|
||||
Upstream servers may also be specified on the command line or in
|
||||
/etc/dnsmasq.conf. These server specifications optionally take a
|
||||
the configuration file. These server specifications optionally take a
|
||||
domain name which tells dnsmasq to use that server only to find names
|
||||
in that particular domain.
|
||||
.PP
|
||||
@@ -376,17 +468,20 @@ and run dnsmasq with the
|
||||
option. This second technique allows for dynamic update of the server
|
||||
addresses by PPP or DHCP.
|
||||
.SH FILES
|
||||
.IR /etc/dnsmasq.conf
|
||||
.IR /etc/dnsmasq.conf
|
||||
|
||||
.IR /usr/local/etc/dnsmasq.conf
|
||||
|
||||
.IR /etc/resolv.conf
|
||||
|
||||
.IR /etc/hosts
|
||||
|
||||
.IR /var/lib/misc/dnsmasq.leases
|
||||
.IR /var/lib/misc/dnsmasq.leases
|
||||
|
||||
.IR /var/db/dnsmasq.leases
|
||||
|
||||
.IR /var/run/dnsmasq.pid
|
||||
.SH SEE ALSO
|
||||
.BR dhcp.leases (5),
|
||||
.BR hosts (5),
|
||||
.BR resolver (5)
|
||||
.SH AUTHOR
|
||||
|
||||
@@ -31,6 +31,13 @@ filterwin2k
|
||||
# somewhere other that /etc/resolv.conf
|
||||
#resolv-file=
|
||||
|
||||
# By default, dnsmasq will send queries to any of the upstream
|
||||
# servers it knows about and tries to favour servers to are known
|
||||
# to be up. Uncommenting this forces dnsmasq to try each query
|
||||
# with each server strictly in the order they appear in
|
||||
# /etc/resolv.conf
|
||||
#strict-order
|
||||
|
||||
# If you don't want dnsmasq to read /etc/resolv.conf or any other
|
||||
# file, getting its servers for this file instead (see below), then
|
||||
# uncomment this
|
||||
@@ -69,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
|
||||
@@ -80,17 +96,37 @@ 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, do we will give examples of each. Note that
|
||||
# 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
|
||||
# need to be on the same network.
|
||||
# need to be on the same network. The order of the parameters in these
|
||||
# do not matter, it's permissble to give name,adddress and MAC in any order
|
||||
|
||||
# Always allocate the host with ethernet address 11:22:33:44:55:66
|
||||
# The IP address 192.168.0.60
|
||||
@@ -116,31 +152,97 @@ filterwin2k
|
||||
# the IP address 192.168.0.60
|
||||
#dhcp-host=id:marjorie,192.168.0.60
|
||||
|
||||
# Enable the address given for "judge" in /etc/hosts
|
||||
# to be given to a machine presenting the name "judge" when
|
||||
# it asks for a DHCP lease.
|
||||
#dhcp-host=judge
|
||||
|
||||
# Never offer DHCP service to a machine whose ethernet
|
||||
# address is 11:22:33:44:55:66
|
||||
#dhcp-host=11:22:33:44:55:66,ignore
|
||||
|
||||
# Ignore any client-id presented by the machine with ethernet
|
||||
# address 11:22:33:44:55:66. This is useful to prevent a machine
|
||||
# being treated differently when running under different OS's or
|
||||
# between PXE boot and OS boot.
|
||||
#dhcp-host=11:22:33:44:55:66,id:*
|
||||
|
||||
# Send extra options which are tagged as "red" to
|
||||
# the machine with ethernet address 11:22:33:44:55:66
|
||||
#dhcp-host=11:22:33:44:55:66,net:red
|
||||
|
||||
# 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
|
||||
# broadcast address, DNS server and default route, are given
|
||||
# sane defaults by dnsmasq. You very likely will not need any
|
||||
# any dhcp-options. If you use Windows clients and Samba, there
|
||||
# are some options which are recommended, they are detailed at the
|
||||
# end of this section.
|
||||
# For reference, the common options are:
|
||||
# subnet mask - 1
|
||||
# default router - 3
|
||||
# DNS server - 6
|
||||
# broadcast address - 28
|
||||
|
||||
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
|
||||
#dhcp-option=42,192.168.0.4,10.10.0.5
|
||||
|
||||
# Set the NTP time server address to be the same machine as
|
||||
# is running dnsmasq
|
||||
#dhcp-option=42,0.0.0.0
|
||||
|
||||
# Set the NIS domain name to "welly"
|
||||
#dhcp-option=40,welly
|
||||
|
||||
# Set the default time-to-live to 50
|
||||
#dhcp-option=23,50
|
||||
|
||||
# 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
|
||||
# adapted for a typical dnsmasq installation where the host running
|
||||
# dnsmasq is also the host running samba.
|
||||
# you may want to uncomment them if you use Windows clients and Samba.
|
||||
#dhcp-option=19,0 # option ip-forwarding off
|
||||
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
|
||||
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
|
||||
#dhcp-option=46,8 # netbios node type
|
||||
#dhcp-option=47 # empty netbios scope.
|
||||
|
||||
|
||||
# Set the boot filename and tftpd server name and address
|
||||
# for BOOTP. You will only need this is you want to
|
||||
# boot machines over the network.
|
||||
#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.
|
||||
#dhcp-leasefile=/var/lib/dnsmasq/leases
|
||||
|
||||
# Override the default route (which is normally automagically set
|
||||
# to be the machine running dnsmasq
|
||||
#dhcp-option=2,192,168.4.4
|
||||
#dhcp-leasefile=/var/lib/misc/dnsmasq.leases
|
||||
|
||||
# Set the cachesize here.
|
||||
#cache-size=600
|
||||
#cache-size=150
|
||||
|
||||
# If you want to disable negative caching, uncomment this.
|
||||
#no-negcache
|
||||
@@ -159,11 +261,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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
rpm/README.susefirewall
Normal file
27
rpm/README.susefirewall
Normal file
@@ -0,0 +1,27 @@
|
||||
This is a patch against SuSEfirewall2-3.1-206 (SuSE 9.x and older)
|
||||
It fixes the depancy from the dns daemon name 'named'
|
||||
After appending the patch, the SuSEfirewall is again able to autodetect
|
||||
the dnsmasq named service.
|
||||
This is a very old bug in the SuSEfirewall script.
|
||||
The SuSE people think the name of the dns server will allways 'named'
|
||||
|
||||
|
||||
--- /sbin/SuSEfirewall2.orig 2004-01-23 13:30:09.000000000 +0100
|
||||
+++ /sbin/SuSEfirewall2 2004-01-23 13:31:56.000000000 +0100
|
||||
@@ -764,7 +764,7 @@
|
||||
echo 'FW_ALLOW_INCOMING_HIGHPORTS_UDP should be set to yes, if you are running a DNS server!'
|
||||
|
||||
test "$FW_SERVICE_AUTODETECT" = yes -o "$FW_SERVICE_AUTODETECT" = dmz -o "$FW_SERVICE_AUTODETECT" = ext && {
|
||||
- test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv named && {
|
||||
+ test "$FW_SERVICE_DNS" = no -a '!' "$START_NAMED" = no && check_srv dnsmasq && {
|
||||
echo -e 'Warning: detected activated named, enabling FW_SERVICE_DNS!
|
||||
You still have to allow tcp/udp port 53 on internal, dmz and/or external.'
|
||||
FW_SERVICE_DNS=$FW_SERVICE_AUTODETECT
|
||||
@@ -878,7 +878,7 @@
|
||||
test -e /etc/resolv.conf || echo "Warning: /etc/resolv.conf not found"
|
||||
# Get ports/IP bindings of NAMED/SQUID
|
||||
test "$FW_SERVICE_DNS" = yes -o "$FW_SERVICE_DNS" = dmz -o "$FW_SERVICE_DNS" = ext -o "$START_NAMED" = yes && DNS_PORT=`$LSOF -i -n -P | \
|
||||
- $AWK -F: '/^named .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
|
||||
+ $AWK -F: '/^dnsmasq .* UDP / {print $2}'| $GREP -vw 53 | $SORT -un`
|
||||
test "$FW_SERVICE_SQUID" = yes -o "$FW_SERVICE_SQUID" = dmz -o "$FW_SERVICE_SQUID" = ext -o "$START_SQUID" = yes && SQUID_PORT=`$LSOF -i -n -P | \
|
||||
$AWK -F: '/^squid .* UDP/ {print $2}'| $SORT -un`
|
||||
@@ -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
|
||||
|
||||
32
src/cache.c
32
src/cache.c
@@ -639,7 +639,10 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
|
||||
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4)))
|
||||
{
|
||||
if (crec->flags & F_HOSTS)
|
||||
syslog(LOG_WARNING, "Ignoring DHCP lease for %s because it clashes with an /etc/hosts entry.", host_name);
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (!(crec->flags & F_DHCP))
|
||||
{
|
||||
if (crec->flags & F_NEG)
|
||||
@@ -650,7 +653,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
|
||||
goto newrec;
|
||||
}
|
||||
else
|
||||
syslog(LOG_WARNING, "Ignoring 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;
|
||||
}
|
||||
@@ -671,7 +674,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
|
||||
crec->flags |= F_IMMORTAL;
|
||||
else
|
||||
crec->ttd = ttd;
|
||||
memcpy(&crec->addr, host_address, INADDRSZ);
|
||||
crec->addr.addr.addr4 = *host_address;
|
||||
crec->name.namep = host_name;
|
||||
crec->prev = dhcp_inuse;
|
||||
dhcp_inuse = crec;
|
||||
@@ -683,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)
|
||||
@@ -707,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" : "",
|
||||
@@ -719,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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -746,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
|
||||
|
||||
76
src/config.h
76
src/config.h
@@ -12,30 +12,35 @@
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#define VERSION "2.0"
|
||||
#define VERSION "2.7"
|
||||
|
||||
#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 CONFFILE "/etc/dnsmasq.conf"
|
||||
#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
|
||||
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
|
||||
# define CONFFILE "/etc/dnsmasq.conf"
|
||||
#endif
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
#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
|
||||
|
||||
@@ -54,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
|
||||
@@ -84,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.
|
||||
|
||||
@@ -110,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.
|
||||
|
||||
@@ -123,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
|
||||
|
||||
@@ -142,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
|
||||
@@ -150,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 */
|
||||
@@ -174,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 */
|
||||
@@ -192,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) && \
|
||||
@@ -203,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
|
||||
@@ -222,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 */
|
||||
@@ -254,3 +303,4 @@ typedef unsigned long in_addr_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
663
src/dhcp.c
663
src/dhcp.c
@@ -14,153 +14,340 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
struct dhcp_vendor *vendors,
|
||||
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 (!is_same_net(source, iface_addr, iface_netmask))
|
||||
iface_netmask.s_addr = 0;
|
||||
else if (ioctl(dhcp_fd, SIOCGIFBRDADDR, &ifr) != -1)
|
||||
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
|
||||
}
|
||||
|
||||
for (context = contexts; context; context = context->next)
|
||||
{
|
||||
struct in_addr netmask = context->netmask.s_addr ? context->netmask : iface_netmask;
|
||||
|
||||
if (netmask.s_addr &&
|
||||
is_same_net(source, context->start, netmask) &&
|
||||
is_same_net(source, context->end, netmask))
|
||||
break;
|
||||
}
|
||||
|
||||
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 (is_same_net(source, iface_addr, context->netmask))
|
||||
router = iface_addr;
|
||||
else
|
||||
router = source;
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
newlen = dhcp_reply(context, iface_addr, ifr.ifr_name, ifr.ifr_mtu,
|
||||
rawpacket, sz, now, namebuff,
|
||||
dhcp_opts, dhcp_configs, vendors, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -188,34 +379,47 @@ int address_available(struct dhcp_context *context, struct in_addr taddr)
|
||||
}
|
||||
|
||||
int address_allocate(struct dhcp_context *context, struct dhcp_config *configs,
|
||||
struct in_addr *addrp)
|
||||
struct in_addr *addrp, unsigned char *hwaddr)
|
||||
{
|
||||
/* Find a free address: exlude anything in use and anything allocated to
|
||||
a particular hwaddr/clientid/hostname in our configuration */
|
||||
|
||||
struct dhcp_config *config;
|
||||
struct in_addr start = context->last;
|
||||
struct in_addr start, addr ;
|
||||
int i, j;
|
||||
|
||||
/* start == end means no dynamic leases. */
|
||||
if (context->end.s_addr == context->start.s_addr)
|
||||
return 0;
|
||||
|
||||
/* pick a seed based on hwaddr then iterate until we find a free address. */
|
||||
for (j = 0, i = 0; i < ETHER_ADDR_LEN; i++)
|
||||
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
|
||||
|
||||
start.s_addr = addr.s_addr =
|
||||
htonl(ntohl(context->start.s_addr) +
|
||||
(j % (ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
|
||||
|
||||
do {
|
||||
if (context->last.s_addr == context->end.s_addr)
|
||||
context->last = context->start;
|
||||
if (addr.s_addr == context->end.s_addr)
|
||||
addr = context->start;
|
||||
else
|
||||
context->last.s_addr = htonl(ntohl(context->last.s_addr) + 1);
|
||||
addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
|
||||
|
||||
|
||||
if (!lease_find_by_addr(context->last))
|
||||
if (!lease_find_by_addr(addr))
|
||||
{
|
||||
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 == addr.s_addr)
|
||||
break;
|
||||
|
||||
if (!config)
|
||||
{
|
||||
*addrp = context->last;
|
||||
*addrp = addr;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (context->last.s_addr != start.s_addr);
|
||||
} while (addr.s_addr != start.s_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -224,9 +428,9 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
|
||||
{
|
||||
if (!context)
|
||||
return 1;
|
||||
if (config->addr.s_addr == 0)
|
||||
if (!(config->flags & CONFIG_ADDR))
|
||||
return 1;
|
||||
if ((config->addr.s_addr & context->netmask.s_addr) == (context->start.s_addr & context->netmask.s_addr))
|
||||
if (is_same_net(config->addr, context->start, context->netmask))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -241,33 +445,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;
|
||||
}
|
||||
|
||||
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->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->flags |= CONFIG_ADDR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
266
src/dnsmasq.c
266
src/dnsmasq.c
@@ -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;
|
||||
@@ -64,31 +66,36 @@ int main (int argc, char **argv)
|
||||
struct dhcp_context *dhcp_tmp, *dhcp = NULL;
|
||||
struct dhcp_config *dhcp_configs = NULL;
|
||||
struct dhcp_opt *dhcp_options = NULL;
|
||||
struct dhcp_vendor *dhcp_vendors = NULL;
|
||||
char *dhcp_file = NULL, *dhcp_sname = NULL;
|
||||
struct in_addr dhcp_next_server;
|
||||
int leasefd = 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,70 +103,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_vendors,
|
||||
&dhcp_file, &dhcp_sname, &dhcp_next_server, &maxleases, &min_leasetime,
|
||||
&doctors);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#ifndef HAVE_UDP_SRC_DST
|
||||
/* if we cannot support binding the wildcard address, set the "bind only
|
||||
interfaces in use" option */
|
||||
options |= OPT_NOWILD;
|
||||
#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));
|
||||
|
||||
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);
|
||||
@@ -197,19 +187,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);
|
||||
@@ -254,10 +241,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");
|
||||
|
||||
@@ -270,7 +265,15 @@ int main (int argc, char **argv)
|
||||
if (sighup)
|
||||
{
|
||||
cache_reload(options, dnamebuff, domain_suffix, addn_hosts);
|
||||
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),
|
||||
@@ -284,18 +287,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);
|
||||
@@ -311,19 +312,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 */
|
||||
@@ -340,17 +342,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. */
|
||||
@@ -387,64 +395,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, dhcp_vendors,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
161
src/dnsmasq.h
161
src/dnsmasq.h
@@ -11,22 +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
|
||||
|
||||
#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 <netinet/in.h>
|
||||
|
||||
#include <arpa/nameser.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -35,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>
|
||||
@@ -53,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
|
||||
@@ -79,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 {
|
||||
@@ -94,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 */
|
||||
@@ -112,7 +130,7 @@ struct crec {
|
||||
};
|
||||
|
||||
#define F_IMMORTAL 1
|
||||
#define F_CONFIG 2
|
||||
#define F_CONFIG 2
|
||||
#define F_REVERSE 4
|
||||
#define F_FORWARD 8
|
||||
#define F_DHCP 16
|
||||
@@ -174,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;
|
||||
};
|
||||
|
||||
@@ -201,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;
|
||||
@@ -219,29 +238,43 @@ 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
|
||||
#define CONFIG_NOCLID 128
|
||||
|
||||
struct dhcp_opt {
|
||||
unsigned char opt;
|
||||
unsigned char len;
|
||||
int opt, len, is_addr;
|
||||
unsigned char *val;
|
||||
char *netid;
|
||||
struct dhcp_opt *next;
|
||||
};
|
||||
};
|
||||
|
||||
struct dhcp_vendor {
|
||||
int len;
|
||||
char *data, *net;
|
||||
struct dhcp_vendor *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 start, end, last; /* range of available addresses */
|
||||
struct in_addr netmask, broadcast;
|
||||
struct in_addr start, end; /* range of available addresses */
|
||||
char *netid;
|
||||
struct dhcp_context *next;
|
||||
};
|
||||
|
||||
@@ -294,7 +327,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,6 +347,8 @@ 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);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
|
||||
/* option.c */
|
||||
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
|
||||
@@ -322,48 +358,56 @@ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resol
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize,
|
||||
int *port, int *query_port, unsigned long *local_ttl, char **addn_hosts,
|
||||
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf, struct dhcp_opt **opts,
|
||||
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server);
|
||||
struct dhcp_context **dhcp, struct dhcp_config **dhcp_conf,
|
||||
struct dhcp_opt **opts, struct dhcp_vendor **dhcp_vendors,
|
||||
char **dhcp_file, char **dhcp_sname, struct in_addr *dhcp_next_server,
|
||||
int *maxleases, unsigned int *min_leasetime, struct doctor **doctors);
|
||||
|
||||
/* 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,
|
||||
struct dhcp_vendor *vendors,
|
||||
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);
|
||||
struct in_addr *addrp, unsigned char *hwaddr);
|
||||
struct dhcp_config *find_config(struct dhcp_config *configs,
|
||||
struct dhcp_context *context,
|
||||
unsigned char *clid, int clid_len,
|
||||
unsigned char *hwaddr, char *hostname);
|
||||
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);
|
||||
@@ -371,10 +415,21 @@ 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,
|
||||
struct dhcp_vendor *vendors,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server);
|
||||
struct in_addr dhcp_next_server, struct in_addr router);
|
||||
|
||||
/* isc.c */
|
||||
#ifdef HAVE_ISC_READER
|
||||
void load_dhcp(char *file, char *suffix, time_t now, char *hostname);
|
||||
#endif
|
||||
|
||||
273
src/forward.c
273
src/forward.c
@@ -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)
|
||||
@@ -132,13 +201,19 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
|
||||
flags = 0; /* may be better match from previous literal */
|
||||
domain = serv->domain;
|
||||
matchlen = domainlen;
|
||||
type = SERV_HAS_DOMAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
@@ -161,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);
|
||||
@@ -227,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);
|
||||
|
||||
@@ -237,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 */
|
||||
@@ -261,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);
|
||||
}
|
||||
@@ -272,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
249
src/isc.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
|
||||
/* Code in this file is based on contributions by John Volpe. */
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_ISC_READER
|
||||
|
||||
struct isc_lease {
|
||||
char *name, *fqdn;
|
||||
time_t expires;
|
||||
struct in_addr addr;
|
||||
struct isc_lease *next;
|
||||
};
|
||||
|
||||
static struct isc_lease *leases = NULL;
|
||||
static off_t lease_file_size = (off_t)0;
|
||||
static ino_t lease_file_inode = (ino_t)0;
|
||||
static int logged_lease = 0;
|
||||
|
||||
static int next_token (char *token, int buffsize, FILE * fp)
|
||||
{
|
||||
int c, count = 0;
|
||||
char *cp = token;
|
||||
|
||||
while((c = getc(fp)) != EOF)
|
||||
{
|
||||
if (c == '#')
|
||||
do { c = getc(fp); } while (c != '\n' && c != EOF);
|
||||
|
||||
if (c == ' ' || c == '\t' || c == '\n' || c == ';')
|
||||
{
|
||||
if (count)
|
||||
break;
|
||||
}
|
||||
else if ((c != '"') && (count<buffsize-1))
|
||||
{
|
||||
*cp++ = c;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
*cp = 0;
|
||||
return count ? 1 : 0;
|
||||
}
|
||||
|
||||
void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
|
||||
{
|
||||
char token[MAXTOK], *dot;
|
||||
struct in_addr host_address;
|
||||
time_t ttd, tts;
|
||||
FILE *fp;
|
||||
struct isc_lease *lease, *tmp, **up;
|
||||
struct stat statbuf;
|
||||
|
||||
if (stat(file, &statbuf) == -1)
|
||||
{
|
||||
if (!logged_lease)
|
||||
syslog(LOG_WARNING, "failed to access %s: %m", file);
|
||||
logged_lease = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
logged_lease = 0;
|
||||
|
||||
if ((statbuf.st_size <= lease_file_size) &&
|
||||
(statbuf.st_ino == lease_file_inode))
|
||||
return;
|
||||
|
||||
lease_file_size = statbuf.st_size;
|
||||
lease_file_inode = statbuf.st_ino;
|
||||
|
||||
if (!(fp = fopen (file, "r")))
|
||||
{
|
||||
syslog (LOG_ERR, "failed to load %s: %m", file);
|
||||
return;
|
||||
}
|
||||
|
||||
syslog (LOG_INFO, "reading %s", file);
|
||||
|
||||
while ((next_token(token, MAXTOK, fp)))
|
||||
{
|
||||
if (strcmp(token, "lease") == 0)
|
||||
{
|
||||
hostname[0] = '\0';
|
||||
ttd = tts = (time_t)(-1);
|
||||
if (next_token(token, MAXTOK, fp) &&
|
||||
(host_address.s_addr = inet_addr(token)) != (in_addr_t) -1)
|
||||
{
|
||||
if (next_token(token, MAXTOK, fp) && *token == '{')
|
||||
{
|
||||
while (next_token(token, MAXTOK, fp) && *token != '}')
|
||||
{
|
||||
if ((strcmp(token, "client-hostname") == 0) ||
|
||||
(strcmp(token, "hostname") == 0))
|
||||
{
|
||||
if (next_token(hostname, MAXDNAME, fp))
|
||||
if (!canonicalise(hostname))
|
||||
{
|
||||
*hostname = 0;
|
||||
syslog(LOG_ERR, "bad name in %s", file);
|
||||
}
|
||||
}
|
||||
else if ((strcmp(token, "ends") == 0) ||
|
||||
(strcmp(token, "starts") == 0))
|
||||
{
|
||||
struct tm lease_time;
|
||||
int is_ends = (strcmp(token, "ends") == 0);
|
||||
if (next_token(token, MAXTOK, fp) && /* skip weekday */
|
||||
next_token(token, MAXTOK, fp) && /* Get date from lease file */
|
||||
sscanf (token, "%d/%d/%d",
|
||||
&lease_time.tm_year,
|
||||
&lease_time.tm_mon,
|
||||
&lease_time.tm_mday) == 3 &&
|
||||
next_token(token, MAXTOK, fp) &&
|
||||
sscanf (token, "%d:%d:%d:",
|
||||
&lease_time.tm_hour,
|
||||
&lease_time.tm_min,
|
||||
&lease_time.tm_sec) == 3)
|
||||
{
|
||||
/* There doesn't seem to be a universally available library function
|
||||
which converts broken-down _GMT_ time to seconds-in-epoch.
|
||||
The following was borrowed from ISC dhcpd sources, where
|
||||
it is noted that it might not be entirely accurate for odd seconds.
|
||||
Since we're trying to get the same answer as dhcpd, that's just
|
||||
fine here. */
|
||||
static int months [11] = { 31, 59, 90, 120, 151, 181,
|
||||
212, 243, 273, 304, 334 };
|
||||
time_t time = ((((((365 * (lease_time.tm_year - 1970) + /* Days in years since '70 */
|
||||
(lease_time.tm_year - 1969) / 4 + /* Leap days since '70 */
|
||||
(lease_time.tm_mon > 1 /* Days in months this year */
|
||||
? months [lease_time.tm_mon - 2]
|
||||
: 0) +
|
||||
(lease_time.tm_mon > 2 && /* Leap day this year */
|
||||
!((lease_time.tm_year - 1972) & 3)) +
|
||||
lease_time.tm_mday - 1) * 24) + /* Day of month */
|
||||
lease_time.tm_hour) * 60) +
|
||||
lease_time.tm_min) * 60) + lease_time.tm_sec;
|
||||
if (is_ends)
|
||||
ttd = time;
|
||||
else
|
||||
tts = time; }
|
||||
}
|
||||
}
|
||||
|
||||
/* missing info? */
|
||||
if (!*hostname)
|
||||
continue;
|
||||
if (ttd == (time_t)(-1))
|
||||
continue;
|
||||
|
||||
/* We use 0 as infinite in ttd */
|
||||
if ((tts != -1) && (ttd == tts - 1))
|
||||
ttd = (time_t)0;
|
||||
else if (difftime(now, ttd) > 0)
|
||||
continue;
|
||||
|
||||
if ((dot = strchr(hostname, '.')))
|
||||
{
|
||||
if (!suffix || hostname_isequal(dot+1, suffix))
|
||||
{
|
||||
syslog(LOG_WARNING,
|
||||
"Ignoring DHCP lease for %s because it has an illegal domain part",
|
||||
hostname);
|
||||
continue;
|
||||
}
|
||||
*dot = 0;
|
||||
}
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
if (hostname_isequal(lease->name, hostname))
|
||||
{
|
||||
lease->expires = ttd;
|
||||
lease->addr = host_address;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!lease && (lease = malloc(sizeof(struct isc_lease))))
|
||||
{
|
||||
lease->expires = ttd;
|
||||
lease->addr = host_address;
|
||||
lease->fqdn = NULL;
|
||||
lease->next = leases;
|
||||
if (!(lease->name = malloc(strlen(hostname)+1)))
|
||||
free(lease);
|
||||
else
|
||||
{
|
||||
leases = lease;
|
||||
strcpy(lease->name, hostname);
|
||||
if (suffix && (lease->fqdn = malloc(strlen(hostname) + strlen(suffix) + 2)))
|
||||
{
|
||||
strcpy(lease->fqdn, hostname);
|
||||
strcat(lease->fqdn, ".");
|
||||
strcat(lease->fqdn, suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* prune expired leases */
|
||||
for (lease = leases, up = &leases; lease; lease = tmp)
|
||||
{
|
||||
tmp = lease->next;
|
||||
if (lease->expires != (time_t)0 && difftime(now, lease->expires) > 0)
|
||||
{
|
||||
*up = lease->next; /* unlink */
|
||||
free(lease->name);
|
||||
if (lease->fqdn)
|
||||
free(lease->fqdn);
|
||||
free(lease);
|
||||
}
|
||||
else
|
||||
up = &lease->next;
|
||||
}
|
||||
|
||||
|
||||
/* remove all existing DHCP cache entries */
|
||||
cache_unhash_dhcp();
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
if (lease->fqdn)
|
||||
{
|
||||
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires, F_REVERSE);
|
||||
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, 0);
|
||||
}
|
||||
else
|
||||
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires, F_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
92
src/lease.c
92
src/lease.c
@@ -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);
|
||||
|
||||
536
src/network.c
536
src/network.c
@@ -14,159 +14,104 @@
|
||||
|
||||
#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;
|
||||
|
||||
return list;
|
||||
|
||||
/* 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 list;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
it is possible to have multiple interfaces with the same address */
|
||||
for (iface = list; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&iface->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)))
|
||||
{
|
||||
int errsave = errno;
|
||||
close(fd);
|
||||
errno = errsave;
|
||||
return "failed to bind socket: %s";
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
/* 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;
|
||||
iface->next = list;
|
||||
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 irec *iface = NULL;
|
||||
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; )
|
||||
{
|
||||
struct ifreq *ifr = (struct ifreq *) ptr;
|
||||
union mysockaddr addr;
|
||||
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
ptr += ifr->ifr_addr.sa_len + IF_NAMESIZE;
|
||||
/* subsequent entries may not be aligned, so copy into
|
||||
an aligned buffer to avoid nasty complaints about
|
||||
unaligned accesses. */
|
||||
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
|
||||
if (!(ifr = realloc(ifr, ifr_len)))
|
||||
die("cannot allocate buffer", NULL);
|
||||
|
||||
memcpy(ifr, ptr, ifr_len);
|
||||
ptr += ifr_len;
|
||||
#else
|
||||
ifr = (struct ifreq *)ptr;
|
||||
ptr += sizeof(struct ifreq);
|
||||
#endif
|
||||
|
||||
|
||||
/* copy address since getting flags overwrites */
|
||||
if (ifr->ifr_addr.sa_family == AF_INET)
|
||||
{
|
||||
@@ -189,212 +134,171 @@ 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. Note that
|
||||
this is done as addresses rather than interface names so
|
||||
as not to confuse the no-IPRECVIF workaround on the DHCP code */
|
||||
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->addr = addr;
|
||||
lo->next = *addrs;
|
||||
*addrs = lo;
|
||||
}
|
||||
|
||||
if ((err = add_iface(interfacep, ifr->ifr_flags, ifr->ifr_name,
|
||||
&addr, names, addrs, except)))
|
||||
goto end;
|
||||
|
||||
/* 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;
|
||||
iface = add_iface(iface, ifr->ifr_name, &addr, *names, *addrs, except);
|
||||
|
||||
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 ifreq *ifr = (struct ifreq *) ptr;
|
||||
struct dhcp_context *context;
|
||||
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
ptr += ifr->ifr_addr.sa_len + IF_NAMESIZE;
|
||||
#else
|
||||
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);
|
||||
close(fd);
|
||||
if (err)
|
||||
{
|
||||
errno = errsave;
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
|
||||
{
|
||||
FILE *f = fopen(IP6INTERFACES, "r");
|
||||
|
||||
if (f)
|
||||
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
|
||||
/* This code snarfed from net-tools 1.60 and certainly linux specific, though
|
||||
it shouldn't break on other Unices, and their SIOGIFCONF might work. */
|
||||
{
|
||||
union mysockaddr addr;
|
||||
unsigned int plen, scope, flags, if_idx;
|
||||
char devname[20], addrstring[32];
|
||||
|
||||
while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n",
|
||||
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
|
||||
FILE *f = fopen(IP6INTERFACES, "r");
|
||||
int found = 0;
|
||||
union mysockaddr addr6;
|
||||
|
||||
if (f)
|
||||
{
|
||||
int i;
|
||||
unsigned char *addr6p = (unsigned char *) &addr.in6.sin6_addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sa.sa_family = AF_INET6;
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned int byte;
|
||||
sscanf(addrstring+i+i, "%02x", &byte);
|
||||
addr6p[i] = byte;
|
||||
}
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
/* For completeness - should never be defined on Linux. */
|
||||
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
addr.in6.sin6_port = htons(port);
|
||||
addr.in6.sin6_flowinfo = htonl(0);
|
||||
addr.in6.sin6_scope_id = htonl(scope);
|
||||
unsigned int plen, scope, flags, if_idx;
|
||||
char devname[20], addrstring[32];
|
||||
|
||||
if ((err = add_iface(interfacep, flags, devname, &addr, names, addrs, except)))
|
||||
while (fscanf(f, "%32s %02x %02x %02x %02x %20s\n",
|
||||
addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF)
|
||||
{
|
||||
errsave = errno;
|
||||
fclose(f);
|
||||
errno = errsave;
|
||||
return err;
|
||||
if (strcmp(devname, ifr->ifr_name) == 0)
|
||||
{
|
||||
int i;
|
||||
unsigned char *addr6p = (unsigned char *) &addr6.in6.sin6_addr;
|
||||
memset(&addr6, 0, sizeof(addr6));
|
||||
addr6.sa.sa_family = AF_INET6;
|
||||
for (i=0; i<16; i++)
|
||||
{
|
||||
unsigned int byte;
|
||||
sscanf(addrstring+i+i, "%02x", &byte);
|
||||
addr6p[i] = byte;
|
||||
}
|
||||
addr6.in6.sin6_port = htons(port);
|
||||
addr6.in6.sin6_flowinfo = htonl(0);
|
||||
addr6.in6.sin6_scope_id = htonl(scope);
|
||||
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
if (found)
|
||||
{
|
||||
if (*names && (ifr->ifr_flags & IFF_LOOPBACK))
|
||||
{
|
||||
struct iname *lo = safe_malloc(sizeof(struct iname));
|
||||
lo->addr = addr6;
|
||||
lo->next = *addrs;
|
||||
*addrs = lo;
|
||||
}
|
||||
|
||||
iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* LINUX */
|
||||
|
||||
/* 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 */
|
||||
|
||||
if (buf)
|
||||
free(buf);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
if (ifr)
|
||||
free(ifr);
|
||||
#endif
|
||||
close(fd);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
static struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
|
||||
@@ -577,7 +481,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 */
|
||||
|
||||
599
src/option.c
599
src/option.c
@@ -1,4 +1,4 @@
|
||||
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
|
||||
/* dnsmasq is Copyright (c) 2000 - 2004 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@ struct myoption {
|
||||
int val;
|
||||
};
|
||||
|
||||
#define OPTSTRING "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:U:"
|
||||
|
||||
static struct myoption opts[] = {
|
||||
{"version", 0, 0, 'v'},
|
||||
@@ -66,6 +66,11 @@ 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' },
|
||||
{"dhcp-vendorclass", 1, 0, 'U'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -85,9 +90,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 }
|
||||
@@ -133,9 +140,14 @@ static char *usage =
|
||||
"-t, --mx-target=host_name Specify the host in an MX reply.\n"
|
||||
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
|
||||
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
|
||||
"-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\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";
|
||||
|
||||
|
||||
@@ -145,16 +157,19 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
|
||||
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port,
|
||||
int *query_port, unsigned long *local_ttl, char **addn_hosts, struct dhcp_context **dhcp,
|
||||
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, char **dhcp_file,
|
||||
char **dhcp_sname, struct in_addr *dhcp_next_server)
|
||||
struct dhcp_config **dhcp_conf, struct dhcp_opt **dhcp_opts, struct dhcp_vendor **dhcp_vendors, char **dhcp_file,
|
||||
char **dhcp_sname, struct in_addr *dhcp_next_server, int *dhcp_max,
|
||||
unsigned int *min_leasetime, struct doctor **doctors)
|
||||
{
|
||||
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,23 +181,41 @@ 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 (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 == '#' || *buff == 0)
|
||||
continue; /* comment */
|
||||
if (*buff == 0)
|
||||
continue;
|
||||
if ((p=strchr(buff, '=')))
|
||||
{
|
||||
optarg = p+1;
|
||||
@@ -196,7 +229,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +241,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")))
|
||||
{
|
||||
@@ -216,7 +254,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);
|
||||
}
|
||||
|
||||
@@ -232,21 +270,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);
|
||||
@@ -328,7 +391,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;
|
||||
}
|
||||
|
||||
@@ -338,6 +402,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;
|
||||
}
|
||||
|
||||
@@ -360,8 +426,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))
|
||||
{
|
||||
@@ -388,7 +452,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;
|
||||
}
|
||||
|
||||
@@ -406,10 +477,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;
|
||||
@@ -439,20 +507,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
|
||||
{
|
||||
@@ -527,16 +589,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;
|
||||
}
|
||||
|
||||
@@ -566,34 +638,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':
|
||||
@@ -602,47 +717,36 @@ 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 = DEFLEASE;
|
||||
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;
|
||||
@@ -650,37 +754,65 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
}
|
||||
|
||||
for(j = 0; j < k; j++)
|
||||
if (strchr(a[j], ':')) /* ethernet address or binary CLID */
|
||||
if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
|
||||
{
|
||||
char *arg = a[j];
|
||||
if ((arg[0] == 'i' || arg[0] == 'I') &&
|
||||
(arg[1] == 'd' || arg[1] == 'D') &&
|
||||
arg[2] == ':')
|
||||
{
|
||||
int s, len;
|
||||
arg += 3; /* dump id: */
|
||||
if (strchr(arg, ':'))
|
||||
{
|
||||
s = (strlen(arg)/3) + 1;
|
||||
/* decode in place */
|
||||
for (len = 0; len < s; len++)
|
||||
{
|
||||
if (arg[(len*3)+2] != ':')
|
||||
option = '?';
|
||||
arg[(len*3)+2] = 0;
|
||||
arg[len] = strtol(&arg[len*3], NULL, 16);
|
||||
}
|
||||
}
|
||||
if (arg[3] == '*')
|
||||
new->flags |= CONFIG_NOCLID;
|
||||
else
|
||||
len = strlen(arg);
|
||||
|
||||
new->clid_len = len;
|
||||
new->clid = safe_malloc(len);
|
||||
memcpy(new->clid, arg, len);
|
||||
{
|
||||
int len;
|
||||
arg += 3; /* dump id: */
|
||||
if (strchr(arg, ':'))
|
||||
{
|
||||
/* decode hex in place */
|
||||
char *p = arg, *q = arg, *r;
|
||||
while (*p)
|
||||
{
|
||||
for (r = p; *r && *r != ':'; r++);
|
||||
if (*r)
|
||||
{
|
||||
if (r != p)
|
||||
{
|
||||
*r = 0;
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
}
|
||||
p = r+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*p)
|
||||
*(q++) = strtol(p, NULL, 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
len = q - arg;
|
||||
}
|
||||
else
|
||||
len = strlen(arg);
|
||||
|
||||
new->flags |= CONFIG_CLID;
|
||||
new->clid_len = len;
|
||||
new->clid = safe_malloc(len);
|
||||
memcpy(new->clid, arg, len);
|
||||
}
|
||||
}
|
||||
else if ((arg[0] == 'n' || arg[0] == 'N') &&
|
||||
(arg[1] == 'e' || arg[1] == 'E') &&
|
||||
(arg[2] == 't' || arg[3] == 'T') &&
|
||||
arg[3] == ':')
|
||||
{
|
||||
new->flags |= CONFIG_NETID;
|
||||
new->netid = 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;
|
||||
@@ -692,7 +824,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;
|
||||
@@ -706,18 +841,20 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
{
|
||||
case 'h':
|
||||
case 'H':
|
||||
fac *= 60;
|
||||
/* fall through */
|
||||
fac *= 60;
|
||||
/* fall through */
|
||||
case 'm':
|
||||
case 'M':
|
||||
fac *= 60;
|
||||
|
||||
/* fall through */
|
||||
case 's':
|
||||
case 'S':
|
||||
*lastp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (cp = a[j]; *cp; cp++)
|
||||
if (!isdigit(*cp))
|
||||
if (!isdigit(*cp) && *cp != ' ')
|
||||
break;
|
||||
|
||||
if (*cp)
|
||||
@@ -725,62 +862,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;
|
||||
char *cp, *comma;
|
||||
int addrs, digs, is_addr, is_hex, is_dec;
|
||||
|
||||
new->next = *dhcp_opts;
|
||||
*dhcp_opts = new;
|
||||
new->len = 0;
|
||||
new->is_addr = 0;
|
||||
new->netid = NULL;
|
||||
|
||||
if ((comma = strchr(optarg, ',')))
|
||||
{
|
||||
*comma = 0;
|
||||
|
||||
if (!comma || (*comma = 0) || (new->opt = atoi(optarg)) == 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;
|
||||
}
|
||||
|
||||
/* check for non-address list characters */
|
||||
for (addrs = 1, cp = comma+1; *cp; cp++)
|
||||
if (*cp == ',')
|
||||
addrs++;
|
||||
else if (!(*cp == '.' || *cp == ' ' || (*cp >='0' && *cp <= '9')))
|
||||
break;
|
||||
*dhcp_opts = new;
|
||||
|
||||
if (*cp)
|
||||
if (!comma)
|
||||
break;
|
||||
|
||||
/* characterise the value */
|
||||
is_addr = is_hex = is_dec = 1;
|
||||
addrs = digs = 1;
|
||||
for (cp = comma+1; *cp; cp++)
|
||||
if (*cp == ',')
|
||||
{
|
||||
addrs++;
|
||||
is_dec = is_hex = 0;
|
||||
}
|
||||
else if (*cp == ':')
|
||||
{
|
||||
digs++;
|
||||
is_dec = is_addr = 0;
|
||||
}
|
||||
else 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;
|
||||
new->len = INADDRSZ * addrs;
|
||||
new->val = op = safe_malloc(new->len);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -802,13 +1062,74 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'U':
|
||||
{
|
||||
char *comma;
|
||||
|
||||
if (!(comma = strchr(optarg, ',')))
|
||||
option = '?';
|
||||
else
|
||||
{
|
||||
struct dhcp_vendor *new = safe_malloc(sizeof(struct dhcp_vendor));
|
||||
*comma = 0;
|
||||
new->net = safe_string_alloc(optarg);
|
||||
new->len = strlen(comma+1);
|
||||
new->data = safe_malloc(new->len);
|
||||
memcpy(new->data, comma+1, new->len);
|
||||
new->next = *dhcp_vendors;
|
||||
*dhcp_vendors = new;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -860,7 +1181,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (is_same_net(doctor->in, *addr, doctor->mask))
|
||||
{
|
||||
addr->s_addr &= ~doctor->mask.s_addr;
|
||||
addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
|
||||
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;
|
||||
|
||||
557
src/rfc2131.c
557
src/rfc2131.c
@@ -25,14 +25,18 @@
|
||||
#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_VENDOR_ID 60
|
||||
#define OPTION_CLIENT_ID 61
|
||||
#define OPTION_END 255
|
||||
|
||||
#define DHCPDISCOVER 1
|
||||
@@ -45,43 +49,87 @@
|
||||
#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,
|
||||
struct dhcp_vendor *vendors,
|
||||
char *domain_suffix, char *dhcp_file, char *dhcp_sname,
|
||||
struct in_addr dhcp_next_server)
|
||||
struct in_addr dhcp_next_server, struct in_addr router)
|
||||
{
|
||||
unsigned char *opt, *clid;
|
||||
struct dhcp_lease *lease;
|
||||
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;
|
||||
struct in_addr addr;
|
||||
unsigned short fuzz = 0;
|
||||
|
||||
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)))
|
||||
{
|
||||
@@ -93,20 +141,9 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = namebuff;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
}
|
||||
|
||||
|
||||
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 +157,51 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 : context->lease_time;
|
||||
|
||||
def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
|
||||
|
||||
netid = context->netid;
|
||||
if (have_config(config, CONFIG_NETID))
|
||||
netid = config->netid;
|
||||
else if ((opt = option_find(mess, sz, OPTION_VENDOR_ID)))
|
||||
{
|
||||
struct dhcp_vendor *vendor;
|
||||
for (vendor = vendors; vendor; vendor = vendor->next)
|
||||
if (vendor->len == option_len(opt) &&
|
||||
memcmp(vendor->data, option_ptr(opt), vendor->len) == 0)
|
||||
netid = vendor->net;
|
||||
}
|
||||
|
||||
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
|
||||
if (have_config(config, CONFIG_NOCLID))
|
||||
{
|
||||
clid = mess->chaddr;
|
||||
clid_len = 0;
|
||||
}
|
||||
|
||||
/* do we have a lease in store? */
|
||||
lease = lease_find_by_client(clid, clid_len);
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
|
||||
{
|
||||
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))
|
||||
@@ -159,83 +217,124 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
|
||||
else
|
||||
expires_time = def_time;
|
||||
}
|
||||
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS)))
|
||||
{
|
||||
int len = option_len(opt);
|
||||
req_options = namebuff;
|
||||
memcpy(req_options, option_ptr(opt), len);
|
||||
req_options[len] = OPTION_END;
|
||||
}
|
||||
|
||||
if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE)))
|
||||
return 0;
|
||||
|
||||
switch (opt[2])
|
||||
switch (option_uint(opt, 1))
|
||||
{
|
||||
case 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);
|
||||
addr = 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 ((!opt || !address_available(context, mess->yiaddr)) &&
|
||||
!address_allocate(context, dhcp_configs, &mess->yiaddr))
|
||||
{
|
||||
syslog(LOG_WARNING, "address pool exhausted");
|
||||
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);
|
||||
else if (lease && is_same_net(lease->addr, context->start, context->netmask))
|
||||
mess->yiaddr = lease->addr;
|
||||
else if (opt && address_available(context, addr))
|
||||
mess->yiaddr = addr;
|
||||
else if (!address_allocate(context, dhcp_configs, &mess->yiaddr, mess->chaddr))
|
||||
message = "no address available";
|
||||
|
||||
log_packet("OFFER" , &mess->yiaddr, mess->chaddr, context->iface);
|
||||
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
|
||||
|
||||
if (message)
|
||||
return 0;
|
||||
|
||||
/* ensure that we send the reply by steam even if a buggy client sets this. */
|
||||
mess->ciaddr.s_addr = 0;
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, expires_time);
|
||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||
if (expires_time != 0xffffffff)
|
||||
{
|
||||
p = option_put(p, end, OPTION_T1, 4, (expires_time/2));
|
||||
p = option_put(p, end, OPTION_T2, 4, ((expires_time * 7)/8));
|
||||
}
|
||||
p = do_req_options(context, p, end, req_options, dhcp_opts, domain_suffix,
|
||||
NULL, router, iface_addr, iface_mtu, netid);
|
||||
p = option_put(p, end, OPTION_END, 0, 0);
|
||||
|
||||
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 (have_config(config, CONFIG_DISABLE))
|
||||
message = "disabled";
|
||||
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,63 +345,104 @@ 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";
|
||||
|
||||
/* desynchronise renewals */
|
||||
fuzz = rand16();
|
||||
while (fuzz > (renewal_time/16))
|
||||
fuzz = fuzz/2;
|
||||
}
|
||||
|
||||
/* If a machine moves networks whilst it has a lease, we catch that here. */
|
||||
if (!message && !is_same_net(mess->yiaddr, context->start, context->netmask))
|
||||
message = "wrong network";
|
||||
|
||||
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);
|
||||
lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
|
||||
|
||||
bootp_option_put(mess, dhcp_file, dhcp_sname);
|
||||
mess->siaddr = dhcp_next_server;
|
||||
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);
|
||||
mess->siaddr = dhcp_next_server.s_addr ? dhcp_next_server : iface_addr;
|
||||
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
|
||||
p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
|
||||
p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
|
||||
if (renewal_time != 0xffffffff)
|
||||
{
|
||||
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))
|
||||
message = "ignored";
|
||||
|
||||
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, message);
|
||||
|
||||
if (message || mess->ciaddr.s_addr == 0)
|
||||
return 0;
|
||||
|
||||
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 +466,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 +492,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;
|
||||
|
||||
@@ -418,6 +576,10 @@ static int in_list(unsigned char *list, int opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* If no requested options, send everything, not nothing. */
|
||||
if (!list)
|
||||
return 1;
|
||||
|
||||
for (i = 0; list[i] != OPTION_END; i++)
|
||||
if (opt == list[i])
|
||||
return 1;
|
||||
@@ -425,74 +587,105 @@ 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)
|
||||
return opts;
|
||||
return NULL;
|
||||
struct dhcp_opt *tmp;
|
||||
|
||||
for (tmp = opts; tmp; tmp = tmp->next)
|
||||
if (tmp->opt == opt &&
|
||||
((!netid && !tmp->netid) ||
|
||||
(netid && tmp->netid && strcmp(tmp->netid, netid) == 0)))
|
||||
return tmp;
|
||||
|
||||
return netid ? option_find2(NULL, opts, opt) : NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
struct dhcp_opt *opt;
|
||||
|
||||
if (in_list(req_options, OPTION_MAXMESSAGE))
|
||||
p = option_put(p, end, OPTION_MAXMESSAGE, 2,
|
||||
DNSMASQ_PACKETSZ > iface_mtu ?
|
||||
iface_mtu : DNSMASQ_PACKETSZ);
|
||||
|
||||
if (!req_options)
|
||||
return p;
|
||||
|
||||
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++)
|
||||
for (opt=config_opts; opt; opt = opt->next)
|
||||
{
|
||||
struct dhcp_opt *opt = option_find2(config_opts, req_options[i]);
|
||||
if (req_options[i] != OPTION_HOSTNAME && opt && (p + opt->len + 2 < end))
|
||||
if (opt->opt == OPTION_HOSTNAME ||
|
||||
opt->opt == OPTION_MAXMESSAGE ||
|
||||
!in_list(req_options, opt->opt) ||
|
||||
opt != option_find2(netid, config_opts, opt->opt) ||
|
||||
p + opt->len + 3 >= end)
|
||||
continue;
|
||||
|
||||
/* For the options we have default values on
|
||||
dhc-option=<optionno> means "don't include this option"
|
||||
not "include a zero-length option" */
|
||||
if (opt->len == 0 &&
|
||||
(opt->opt == OPTION_NETMASK ||
|
||||
opt->opt == OPTION_BROADCAST ||
|
||||
opt->opt == OPTION_ROUTER ||
|
||||
opt->opt == OPTION_DNSSERVER))
|
||||
continue;
|
||||
|
||||
*(p++) = opt->opt;
|
||||
*(p++) = opt->len;
|
||||
if (opt->len == 0)
|
||||
continue;
|
||||
|
||||
if (opt->is_addr)
|
||||
{
|
||||
int j;
|
||||
struct in_addr *a = (struct in_addr *)opt->val;
|
||||
for (j = 0; j < opt->len; j+=INADDRSZ, a++)
|
||||
{
|
||||
/* zero means "self" */
|
||||
if (a->s_addr == 0)
|
||||
memcpy(p, &iface_addr, INADDRSZ);
|
||||
else
|
||||
memcpy(p, a, INADDRSZ);
|
||||
p += INADDRSZ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*(p++) = opt->opt;
|
||||
*(p++) = opt->len;
|
||||
memcpy(p, opt->val, opt->len);
|
||||
p += opt->len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
28
src/util.c
28
src/util.c
@@ -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,23 @@ 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
|
||||
}
|
||||
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
|
||||
{
|
||||
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user