Compare commits

...

13 Commits
v2.41 ... v2.55

Author SHA1 Message Date
Simon Kelley
c52e189734 import of dnsmasq-2.55.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
8ef5ada238 import of dnsmasq-2.53.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
316e2730ac import of dnsmasq-2.52.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
1f15b81d61 import of dnsmasq-2.51.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
77e94da7bb import of dnsmasq-2.50.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
03a97b6170 import of dnsmasq-2.49.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
7622fc06ab import of dnsmasq-2.48.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
73a08a248d import of dnsmasq-2.47.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
9009d74652 import of dnsmasq-2.46.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
1ad24ae15c import of dnsmasq-2.45.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
3927da46aa import of dnsmasq-2.44.tar.gz 2012-01-05 17:31:14 +00:00
Simon Kelley
1a6bca81f6 import of dnsmasq-2.43.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
9e038946a1 import of dnsmasq-2.42.tar.gz 2012-01-05 17:31:13 +00:00
61 changed files with 19687 additions and 12595 deletions

3085
CHANGELOG

File diff suppressed because it is too large Load Diff

2509
CHANGELOG.archive Normal file

File diff suppressed because it is too large Load Diff

39
FAQ
View File

@@ -16,6 +16,14 @@ A: The high ports that dnsmasq opens are for replies from the upstream
you to specify the UDP port to be used for this purpose. If not
specified, the operating system will select an available port number
just as it did before.
Second addendum: following the discovery of a security flaw in the
DNS protocol, dnsmasq from version 2.43 has changed behavior. It
now uses a new, randomly selected, port for each query. The old
default behaviour (use one port allocated by the OS) is available by
setting --query-port=0, and setting the query port to a positive
value is still works. You should think hard and know what you are
doing before using either of these options.
Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
that?
@@ -226,7 +234,7 @@ A: What is happening is this: The boot process sends a DHCP
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.
Linux all network types (including FireWire) are supported.
Q: What is this strange "bind-interface" option?
@@ -324,6 +332,17 @@ A: By default, the identity of a machine is determined by using the
method for setting the client-id varies with DHCP client software,
dhcpcd uses the "-I" flag. Windows uses a registry setting,
see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
Addendum:
From version 2.46, dnsmasq has a solution to this which doesn't
involve setting client-IDs. It's possible to put more than one MAC
address in a --dhcp-host configuration. This tells dnsmasq that it
should use the specified IP for any of the specified MAC addresses,
and furthermore it gives dnsmasq permission to sumarily abandon a
lease to one of the MAC addresses if another one comes along. Note
that this will work fine only as longer as only one interface is
up at any time. There is no way for dnsmasq to enforce this
constraint: if you configure multiple MAC addresses and violate
this rule, bad things will happen.
Q: Can dnsmasq do DHCP on IP-alias interfaces?
@@ -387,12 +406,12 @@ A: This a variant on the iptables problem. Explicit details on how to
http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2007q4/001764.html
Q: Dnsmasq logs "running as root because setting capabilities failed"
when it starts up. Why did that happen and what can do to fix it?
Q: Dnsmasq fails to start up with a message about capabilities.
Why did that happen and what can do to fix it?
A: Change your kernel configuration: either deselect CONFIG_SECURITY
_or_ select CONFIG_SECURITY_CAPABILITIES.
_or_ select CONFIG_SECURITY_CAPABILITIES. Alternatively, you can
remove the need to set capabilities by running dnsmasq as root.
Q: Where can I get .rpms Suitable for Suse?
@@ -437,6 +456,16 @@ A: In almost all cases: none. If you have the normal arrangement with
and turn on syslog-ng's dns-cache function.
Q: DHCP doesn't work with windows Vista, but everything else is fine.
A: The DHCP client on windows Vista (and possibly later versions)
demands that the DHCP server send replies as broadcasts. Most other
clients don't do this. The broadcasts are send to
255.255.255.255. A badly configured firewall which blocks such
packets will show exactly these symptoms (Vista fails, others
work).

View File

@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2007 Simon Kelley
# dnsmasq is Copyright (c) 2000-2010 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
@@ -10,38 +10,41 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
PREFIX = /usr/local
BINDIR = ${PREFIX}/sbin
MANDIR = ${PREFIX}/share/man
LOCALEDIR = ${PREFIX}/share/locale
PKG_CONFIG = pkg-config
INSTALL = install
MSGMERGE = msgmerge
MSGFMT = msgfmt
XGETTEXT = xgettext
CFLAGS = -Wall -W -O2
#################################################################
SRC = src
PO = po
MAN = man
PKG_CONFIG = pkg-config
AWK = nawk
INSTALL = install
DNSMASQ_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
DNSMASQ_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi`
DBUS_MINOR=" `echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --modversion dbus-1 | $(AWK) -F . -- '{ if ($$(NF-1)) print \"-DDBUS_MINOR=\"$$(NF-1) }'`"
DBUS_CFLAGS="`echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --cflags dbus-1`"
DBUS_LIBS=" `echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --libs dbus-1`"
SUNOS_VER=" `if uname | grep SunOS 2>&1 >/dev/null; then uname -r | $(AWK) -F . -- '{ print \"-DSUNOS_VER=\"$$2 }'; fi`"
SUNOS_LIBS=" `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi `"
OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
all : dnsmasq
dnsmasq :
cd $(SRC) && $(MAKE) \
DBUS_MINOR=$(DBUS_MINOR) \
DBUS_CFLAGS=$(DBUS_CFLAGS) \
DBUS_LIBS=$(DBUS_LIBS) \
SUNOS_LIBS=$(SUNOS_LIBS) \
SUNOS_VER=$(SUNOS_VER) \
-f ../bld/Makefile dnsmasq
all :
@cd $(SRC) && $(MAKE) \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS)" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS)" \
-f ../Makefile dnsmasq
clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
@@ -55,26 +58,40 @@ install-common :
$(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
all-i18n :
cd $(SRC) && $(MAKE) \
@cd $(SRC) && $(MAKE) \
I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' \
DBUS_MINOR=$(DBUS_MINOR) \
DBUS_CFLAGS=$(DBUS_CFLAGS) \
DBUS_LIBS=$(DBUS_LIBS) \
SUNOS_LIBS=$(SUNOS_LIBS) \
SUNOS_VER=$(SUNOS_VER) \
-f ../bld/Makefile dnsmasq
cd $(PO); for f in *.po; do \
cd ../$(SRC) && $(MAKE) -f ../bld/Makefile $${f%.po}.mo; \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS) `$(PKG_CONFIG) --libs libidn`" \
-f ../Makefile dnsmasq
@cd $(PO); for f in *.po; do \
cd ../$(SRC) && $(MAKE) \
-f ../Makefile $${f%.po}.mo; \
done
install-i18n : all-i18n install-common
cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR)
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR)
cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge :
$(MAKE) I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' -f ../bld/Makefile -C $(SRC) dnsmasq.pot
cd $(PO); for f in *.po; do \
msgmerge -U $$f ../$(SRC)/dnsmasq.pot; \
@cd $(SRC) && $(MAKE) -f ../Makefile dnsmasq.pot
@cd $(PO); for f in *.po; do \
echo -n msgmerge $$f && $(MSGMERGE) --no-wrap -U $$f ../$(SRC)/dnsmasq.pot; \
done
# rules below are targets in recusive makes with cwd=$(SRC)
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $<
dnsmasq : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS)
dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h
$(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(OBJS:.o=.c)
%.mo : ../po/%.po dnsmasq.pot
$(MSGMERGE) -o - ../po/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo -
.PHONY : all clean install install-common all-i18n install-i18n merge

View File

@@ -1,17 +0,0 @@
CFLAGS = -Wall -W -O2
OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(DBUS_MINOR) $(I18N) $(DBUS_CFLAGS) $(SUNOS_VER) $(RPM_OPT_FLAGS) -c $<
dnsmasq : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(DBUS_LIBS) $(SUNOS_LIBS) $(LIBS)
dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h
xgettext -d dnsmasq --foreign-user --keyword=_ -o dnsmasq.pot -i $(OBJS:.o=.c)
%.mo : ../po/%.po dnsmasq.pot
msgmerge -o - ../po/$*.po dnsmasq.pot | msgfmt -o $*.mo -

View File

@@ -2,8 +2,8 @@
for f in *; do
if [ -d $f ]; then
install -m 755 -d $1/$f/man8
install -m 644 $f/dnsmasq.8 $1/$f/man8
$2 -m 755 -d $1/$f/man8
$2 -m 644 $f/dnsmasq.8 $1/$f/man8
echo installing $1/$f/man8/dnsmasq.8
fi
done

View File

@@ -1,8 +1,8 @@
#!/bin/sh
for f in *.mo; do
install -m 755 -d $1/${f%.mo}/LC_MESSAGES
install -m 644 $f $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
$2 -m 755 -d $1/${f%.mo}/LC_MESSAGES
$2 -m 644 $f $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
echo installing $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
done

View File

@@ -1,7 +1,10 @@
#!/bin/sh
if grep "^\#.*define.*HAVE_DBUS" config.h 2>&1 >/dev/null || \
grep HAVE_DBUS 2>&1 >/dev/null ; then
search=$1
shift
if grep "^\#.*define.*$search" config.h 2>&1 >/dev/null || \
grep $search 2>&1 >/dev/null ; then
exec $*
fi

36
contrib/CPE-WAN/README Normal file
View File

@@ -0,0 +1,36 @@
Dnsmasq from version 2.52 has a couple of rather application-specific
features designed to allow for implementation of the DHCP part of CPE
WAN management protocol.
http://www.broadband-forum.org/technical/download/TR-069_Amendment-2.pdf
http://en.wikipedia.org/wiki/TR-069
The relevant sections are F.2.1 "Gateway Requirements" and F.2.5 "DHCP
Vendor Options".
First, dnsmasq checks for DHCP requests which contain an option-125
vendor-class option which in turn holds a vendor section for IANA
enterprise number 3561 which contains sub-options codes 1 and 2. If
this is present then the network-tag "cpewan-id" is set.
This allows dnsmasq to be configured to reply with the correct
GatewayManufacturerOUI, GatewaySerialNumber and GatewayProductClass like this:
dhcp-option=cpewan-id,vi-encap:3561,4,"<GatewayManufacturerOUI>"
dhcp-option=cpewan-id,vi-encap:3561,5,"<SerialNumber>"
dhcp-option=cpewan-id,vi-encap:3561,6,"<ProductClass>"
Second, the received sub-options 1, 2, and 3 are passed to the DHCP
lease-change script as the environment variables DNSMASQ_CPEWAN_OUI,
DNSMASQ_CPEWAN_SERIAL, and DNSMASQ_CPEWAN_CLASS respectively. This allows
the script to be used to maintain a ManageableDevice table as
specified in F.2.1. Note that this data is not retained in dnsmasq's
internal DHCP lease database, so it is not available on every call to
the script (this is the same as some other data such as vendor and
user classes). It will however be available for at least the "add"
call, and should be stored then against the IP address as primary
key for future use.
This feature was added to dnsmasq under sponsorship from Ericsson.

View File

@@ -0,0 +1,38 @@
This is a launchd item for Mac OS X and Mac OS X Server.
For more information about launchd, the
"System wide and per-user daemon/agent manager", see the launchd
man page, or the wikipedia page: http://en.wikipedia.org/wiki/Launchd
This launchd item uses the following flags:
--keep-in-foreground - this is crucial for use with launchd
--log-queries - this is optional and you can remove it
--log-facility=/var/log/dnsmasq.log - again optional instead of system.log
To use this launchd item for dnsmasq:
If you don't already have a folder /Library/LaunchDaemons, then create one:
sudo mkdir /Library/LaunchDaemons
sudo chown root:admin /Library/LaunchDaemons
sudo chmod 775 /Library/LaunchDaemons
Copy uk.org.thekelleys.dnsmasq.plist there and then set ownership/permissions:
sudo cp uk.org.thekelleys.dnsmasq.plist /Library/LaunchDaemons/
sudo chown root:admin /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
sudo chmod 644 /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
Optionally, edit your dnsmasq configuration file to your liking.
To start the launchd job, which starts dnsmaq, reboot or use the command:
sudo launchctl load /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
To stop the launchd job, which stops dnsmasq, use the command:
sudo launchctl unload /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
If you want to permanently stop the launchd job, so it doesn't start the job even after a reboot, use the following command:
sudo launchctl unload -w /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
If you make a change to the configuration file, you should relaunch dnsmasq;
to do this unload and then load again:
sudo launchctl unload /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist
sudo launchctl load /Library/LaunchDaemons/uk.org.thekelleys.dnsmasq.plist

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>uk.org.thekelleys.dnsmasq</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/dnsmasq</string>
<string>--keep-in-foreground</string>
<string>--log-queries</string>
<string>--log-facility=/var/log/dnsmasq.log</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

28
contrib/Solaris10/README Normal file
View File

@@ -0,0 +1,28 @@
From: David Connelly <dconnelly@gmail.com>
Date: Mon, Apr 7, 2008 at 3:31 AM
Subject: Solaris 10 service manifest
To: dnsmasq-discuss@lists.thekelleys.org.uk
I've found dnsmasq much easier to set up on my home server running Solaris
10 than the stock dhcp/dns server, which is probably overkill anyway for my
simple home network needs. Since Solaris now uses SMF (Service Management
Facility) to manage services I thought I'd create a simple service manifest
for the dnsmasq service. The manifest currently assumes that dnsmasq has
been installed in '/usr/local/sbin/dnsmasq' and the configuration file in
'/usr/local/etc/dnsmasq.conf', so you may have to adjust these paths for
your local installation. Here are the steps I followed to install and enable
the dnsmasq service:
# svccfg import dnsmasq.xml
# svcadm enable dnsmasq
To confirm that the service is enabled and online:
# svcs -l dnsmasq
I've just started learning about SMF so if anyone has any
corrections/feedback they are more than welcome.
Thanks,
David

View File

@@ -0,0 +1,8 @@
Hi Simon,
I just wanted to let you know that I have built a Solaris .pkg install package of your dnsmasq utility for people to use. Feel free to point them in my direction if you have people who want this sort of thing.
http://ejesconsulting.wordpress.com/2010/05/12/gnu-dnsmasq-for-opensolaris-sparc/
Thanks
-evan

View File

@@ -0,0 +1,25 @@
Ok, script attached ... seems to be working ok for me,
tried to install and remove a few times. It does the
right thing with the smf when installing, you can then
simply enable the service. Upon removal it cleans up the
files but won't clean up the services (I think until
a reboot) ... I've only started looking at the new
packages stuff in the last day or two, so I could be
missing something, but I can't find any way to force
a proper cleanup.
It requires that you have a writable repository setup
as per the docs on the opensolaris website and it will
create a dnsmasq package (package name is a variable
in the script). The script takes a version number for
the package and assumes that it's in the contrib/Solaris10
directory, it then works out the base tree directory
from $0.
i.e. $ contrib/Solaris10/create_package 2.52-1
or $ cd contrib/Solaris10; ./create_package 2.52-1
It's a bit more complex than it could be because I
prefer putting the daemon in /usr/sbin and the config
in /etc, so the script will actually create a new
version of the existing contrib dnsmasq.xml.

View File

@@ -0,0 +1,87 @@
#!/bin/sh
#
# For our package, and for the SMF script, we need to define where we
# want things to go...
#
BIN_DIR="/usr/sbin"
CONF_DIR="/etc"
MAN_DIR="/usr/share/man/man8"
PACKAGE_NAME="dnsmasq"
#
# Since we know we are in the contrib directory we can work out where
# the rest of the tree is...
#
BASEDIR="`dirname $0`/../.."
#
# We need a version number to use for the package creation...
#
if [ $# != 1 ]; then
echo "Usage: $0 <package_version_number>" >&2
exit 1
fi
VERSION="$1"
#
# First thing we do is fix-up the smf file to use the paths we prefer...
#
if [ ! -f "${BASEDIR}/contrib/Solaris10/dnsmasq.xml" ]; then
echo "$0: unable to find contrib/Solaris10/dnsmasq.xml" >&2
exit 1
fi
echo "Fixing up smf file ... \c"
cat "${BASEDIR}/contrib/Solaris10/dnsmasq.xml" | \
sed -e "s%/usr/local/etc%${CONF_DIR}%" \
-e "s%/usr/local/sbin%${BIN_DIR}%" \
-e "s%/usr/local/man%${MAN_DIR}%" > ${BASEDIR}/contrib/Solaris10/dnsmasq-pkg.xml
echo "done."
echo "Creating packaging file ... \c"
cat <<EOF >${BASEDIR}/contrib/Solaris10/dnsmasq_package.inc
#
# header
#
set name=pkg.name value="dnsmasq"
set name=pkg.description value="dnsmasq daemon - dns, dhcp, tftp etc"
set name=pkg.detailed_url value="http://www.thekelleys.org.uk/dnsmasq/doc.html"
set name=info.maintainer value="TBD (tbd@tbd.com)"
set name=info.upstream value="dnsmasq-discuss@lists.thekelleys.org.uk"
set name=info.upstream_url value="http://www.thekelleys.org.uk/dnsmasq/doc.html"
#
# dependencies ... none?
#
#
# directories
#
dir mode=0755 owner=root group=bin path=${BIN_DIR}/
dir mode=0755 owner=root group=sys path=${CONF_DIR}/
dir mode=0755 owner=root group=sys path=${MAN_DIR}/
dir mode=0755 owner=root group=sys path=/var/
dir mode=0755 owner=root group=sys path=/var/svc
dir mode=0755 owner=root group=sys path=/var/svc/manifest
dir mode=0755 owner=root group=sys path=/var/svc/manifest/network
#
# files
#
file ${BASEDIR}/src/dnsmasq mode=0555 owner=root group=bin path=${BIN_DIR}/dnsmasq
file ${BASEDIR}/man/dnsmasq.8 mode=0555 owner=root group=bin path=${MAN_DIR}/dnsmasq.8
file ${BASEDIR}/dnsmasq.conf.example mode=0644 owner=root group=sys path=${CONF_DIR}/dnsmasq.conf preserve=strawberry
file ${BASEDIR}/contrib/Solaris10/dnsmasq-pkg.xml mode=0644 owner=root group=sys path=/var/svc/manifest/network/dnsmasq.xml restart_fmri=svc:/system/manifest-import:default
EOF
echo "done."
echo "Creating package..."
eval `pkgsend open ${PACKAGE_NAME}@${VERSION}`
pkgsend include ${BASEDIR}/contrib/Solaris10/dnsmasq_package.inc
if [ "$?" = 0 ]; then
pkgsend close
else
echo "Errors"
fi

View File

@@ -0,0 +1,65 @@
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!-- Service manifest for dnsmasq -->
<service_bundle type='manifest' name='dnsmasq'>
<service name='network/dnsmasq' type='service' version='1'>
<create_default_instance enabled='false'/>
<single_instance/>
<dependency name='multi-user'
grouping='require_all'
restart_on='refresh'
type='service'>
<service_fmri value='svc:/milestone/multi-user'/>
</dependency>
<dependency name='config'
grouping='require_all'
restart_on='restart'
type='path'>
<service_fmri value='file:///usr/local/etc/dnsmasq.conf'/>
</dependency>
<dependent name='dnsmasq_multi-user-server'
grouping='optional_all'
restart_on='none'>
<service_fmri value='svc:/milestone/multi-user-server' />
</dependent>
<exec_method type='method' name='start'
exec='/usr/local/sbin/dnsmasq -C /usr/local/etc/dnsmasq.conf'
timeout_seconds='60' >
<method_context>
<method_credential user='root' group='root' privileges='all'/>
</method_context>
</exec_method>
<exec_method type='method'
name='stop'
exec=':kill'
timeout_seconds='60'/>
<exec_method type='method'
name='refresh'
exec=':kill -HUP'
timeout_seconds='60' />
<template>
<common_name>
<loctext xml:lang='C'>dnsmasq server</loctext>
</common_name>
<description>
<loctext xml:lang='C'>
dnsmasq - A lightweight DHCP and caching DNS server.
</loctext>
</description>
<documentation>
<manpage title='dnsmasq' section='8' manpath='/usr/local/man'/>
</documentation>
</template>
</service>
</service_bundle>

View File

@@ -0,0 +1,20 @@
Hello,
For some specific application I needed to deny access to a MAC address
to a lease. For this reason I modified the dhcp-script behavior and is
called with an extra parameter "access" once a dhcp request or discover
is received. In that case if the exit code of the script is zero,
dnsmasq continues normally, and if non-zero the packet is ignored.
This was not added as a security feature but as a mean to handle
differently some addresses. It is also quite intrusive since it requires
changes in several other subsystems.
It attach the patch in case someone is interested.
regards,
Nikos
nmav@gennetsa.com

View File

@@ -0,0 +1,578 @@
Index: src/dnsmasq.c
===================================================================
--- src/dnsmasq.c (revision 696)
+++ src/dnsmasq.c (revision 821)
@@ -59,7 +59,6 @@
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig);
-static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev);
static void poll_resolv(void);
@@ -275,7 +274,7 @@
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
- send_event(pipewrite, EVENT_RELOAD, 0);
+ send_event(pipewrite, EVENT_RELOAD, 0, 0);
err_pipe[1] = -1;
@@ -340,7 +339,7 @@
}
else if (getuid() == 0)
{
- send_event(err_pipe[1], EVENT_PIDFILE, errno);
+ send_event(err_pipe[1], EVENT_PIDFILE, errno, 0);
_exit(0);
}
}
@@ -372,7 +371,7 @@
(setgroups(0, &dummy) == -1 ||
setgid(gp->gr_gid) == -1))
{
- send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
+ send_event(err_pipe[1], EVENT_GROUP_ERR, errno, 0);
_exit(0);
}
@@ -415,14 +414,14 @@
if (bad_capabilities != 0)
{
- send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
+ send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, 0);
_exit(0);
}
/* finally drop root */
if (setuid(ent_pw->pw_uid) == -1)
{
- send_event(err_pipe[1], EVENT_USER_ERR, errno);
+ send_event(err_pipe[1], EVENT_USER_ERR, errno, 0);
_exit(0);
}
@@ -434,7 +433,7 @@
/* lose the setuid and setgid capbilities */
if (capset(hdr, data) == -1)
{
- send_event(err_pipe[1], EVENT_CAP_ERR, errno);
+ send_event(err_pipe[1], EVENT_CAP_ERR, errno, 0);
_exit(0);
}
#endif
@@ -647,7 +646,7 @@
}
if (FD_ISSET(piperead, &rset))
- async_event(piperead, now);
+ async_event(piperead, now, NULL, 0);
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset))
@@ -674,7 +673,7 @@
#endif
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
- dhcp_packet(now);
+ dhcp_packet(piperead, now);
#ifndef NO_FORK
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
@@ -719,17 +718,18 @@
else
return;
- send_event(pipewrite, event, 0);
+ send_event(pipewrite, event, 0, 0);
errno = errsave;
}
}
-void send_event(int fd, int event, int data)
+void send_event(int fd, int event, int data, int priv)
{
struct event_desc ev;
ev.event = event;
ev.data = data;
+ ev.priv = priv;
/* error pipe, debug mode. */
if (fd == -1)
@@ -771,14 +771,17 @@
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
}
}
-
-static void async_event(int pipe, time_t now)
+
+/* returns the private data of the event
+ */
+int async_event(int pipe, time_t now, struct event_desc* event, unsigned int secs)
{
pid_t p;
struct event_desc ev;
int i;
- if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
+ if (read_timeout(pipe, (unsigned char *)&ev, sizeof(ev), now, secs) > 0)
+ {
switch (ev.event)
{
case EVENT_RELOAD:
@@ -872,6 +875,14 @@
flush_log();
exit(EC_GOOD);
}
+ }
+ else
+ return -1; /* timeout */
+
+ if (event)
+ memcpy( event, &ev, sizeof(ev));
+
+ return 0;
}
static void poll_resolv()
Index: src/config.h
===================================================================
--- src/config.h (revision 696)
+++ src/config.h (revision 821)
@@ -51,6 +51,8 @@
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
+#define SCRIPT_TIMEOUT 6
+#define LEASE_CHECK_TIMEOUT 10
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
Index: src/dnsmasq.h
===================================================================
--- src/dnsmasq.h (revision 696)
+++ src/dnsmasq.h (revision 821)
@@ -116,6 +116,7 @@
/* Async event queue */
struct event_desc {
int event, data;
+ unsigned int priv;
};
#define EVENT_RELOAD 1
@@ -390,6 +391,7 @@
#define ACTION_OLD_HOSTNAME 2
#define ACTION_OLD 3
#define ACTION_ADD 4
+#define ACTION_ACCESS 5
#define DHCP_CHADDR_MAX 16
@@ -709,6 +711,7 @@
char *print_mac(char *buff, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
int read_write(int fd, unsigned char *packet, int size, int rw);
+int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs);
/* log.c */
void die(char *message, char *arg1, int exit_code);
@@ -748,7 +751,7 @@
/* dhcp.c */
void dhcp_init(void);
-void dhcp_packet(time_t now);
+void dhcp_packet(int piperead, time_t now);
struct dhcp_context *address_available(struct dhcp_context *context,
struct in_addr addr,
@@ -792,14 +795,16 @@
void rerun_scripts(void);
/* rfc2131.c */
-size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
+size_t dhcp_reply(int pipefd, struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform);
/* dnsmasq.c */
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
-void send_event(int fd, int event, int data);
+void send_event(int fd, int event, int data, int priv);
void clear_cache_and_reload(time_t now);
+int wait_for_child(int pipe);
+int async_event(int pipe, time_t now, struct event_desc*, unsigned int timeout);
/* isc.c */
#ifdef HAVE_ISC_READER
@@ -832,9 +837,9 @@
/* helper.c */
#ifndef NO_FORK
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
-void helper_write(void);
+int helper_write(void);
void queue_script(int action, struct dhcp_lease *lease,
- char *hostname, time_t now);
+ char *hostname, time_t now, unsigned int uid);
int helper_buf_empty(void);
#endif
Index: src/util.c
===================================================================
--- src/util.c (revision 696)
+++ src/util.c (revision 821)
@@ -444,3 +444,38 @@
return 1;
}
+int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs)
+{
+ ssize_t n, done;
+ time_t expire;
+
+ expire = now + secs;
+
+ for (done = 0; done < size; done += n)
+ {
+ retry:
+ if (secs > 0) alarm(secs);
+ n = read(fd, &packet[done], (size_t)(size - done));
+
+ if (n == 0)
+ return 0;
+ else if (n == -1)
+ {
+ if (errno == EINTR) {
+ my_syslog(LOG_INFO, _("read timed out (errno %d)"), errno);
+ return 0;
+ }
+
+ if (retry_send() || errno == ENOMEM || errno == ENOBUFS || errno == EAGAIN)
+ {
+ if (secs == 0 || (secs > 0 && dnsmasq_time() < expire))
+ goto retry;
+ }
+
+ my_syslog(LOG_INFO, _("error in read (timeout %d, errno %d)"), secs, errno);
+ return 0;
+ }
+ }
+ return 1;
+}
+
Index: src/dhcp.c
===================================================================
--- src/dhcp.c (revision 696)
+++ src/dhcp.c (revision 821)
@@ -103,7 +103,7 @@
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
}
-void dhcp_packet(time_t now)
+void dhcp_packet(int piperead, time_t now)
{
struct dhcp_packet *mess;
struct dhcp_context *context;
@@ -239,7 +239,8 @@
if (!iface_enumerate(&parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
- iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
+
+ iov.iov_len = dhcp_reply(piperead, parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform);
lease_update_file(now);
lease_update_dns();
Index: src/helper.c
===================================================================
--- src/helper.c (revision 696)
+++ src/helper.c (revision 821)
@@ -45,6 +45,7 @@
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
char interface[IF_NAMESIZE];
+ unsigned int uid;
};
static struct script_data *buf = NULL;
@@ -60,7 +61,7 @@
then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{
- send_event(err_fd, EVENT_PIPE_ERR, errno);
+ send_event(err_fd, EVENT_PIPE_ERR, errno, 0);
_exit(0);
}
@@ -87,13 +88,13 @@
{
if (daemon->options & OPT_NO_FORK)
/* send error to daemon process if no-fork */
- send_event(event_fd, EVENT_HUSER_ERR, errno);
+ send_event(event_fd, EVENT_HUSER_ERR, errno, 0);
else
{
/* kill daemon */
- send_event(event_fd, EVENT_DIE, 0);
+ send_event(event_fd, EVENT_DIE, 0, 0);
/* return error */
- send_event(err_fd, EVENT_HUSER_ERR, errno);;
+ send_event(err_fd, EVENT_HUSER_ERR, errno, 0);
}
_exit(0);
}
@@ -122,6 +123,8 @@
action_str = "del";
else if (data.action == ACTION_ADD)
action_str = "add";
+ else if (data.action == ACTION_ACCESS)
+ action_str = "access";
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
action_str = "old";
else
@@ -178,9 +181,11 @@
{
/* On error send event back to main process for logging */
if (WIFSIGNALED(status))
- send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
- else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
- send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
+ send_event(event_fd, EVENT_KILLED, WTERMSIG(status), data.uid);
+ else if (WIFEXITED(status))
+ send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), data.uid);
+ else
+ send_event(event_fd, EVENT_EXITED, -1, data.uid);
break;
}
@@ -263,7 +268,7 @@
err = errno;
}
/* failed, send event so the main process logs the problem */
- send_event(event_fd, EVENT_EXEC_ERR, err);
+ send_event(event_fd, EVENT_EXEC_ERR, err, data.uid);
_exit(0);
}
}
@@ -295,7 +300,7 @@
}
/* pack up lease data into a buffer */
-void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
+void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now, unsigned int uid)
{
unsigned char *p;
size_t size;
@@ -332,6 +337,7 @@
buf_size = size;
}
+ buf->uid = uid;
buf->action = action;
buf->hwaddr_len = lease->hwaddr_len;
buf->hwaddr_type = lease->hwaddr_type;
@@ -393,12 +399,15 @@
return bytes_in_buf == 0;
}
-void helper_write(void)
+/* returns -1 if write failed for a reason, 1 if no data exist
+ * and 0 if everything was ok.
+ */
+int helper_write(void)
{
ssize_t rc;
if (bytes_in_buf == 0)
- return;
+ return 1;
if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
{
@@ -409,9 +418,11 @@
else
{
if (errno == EAGAIN || errno == EINTR)
- return;
+ return -1;
bytes_in_buf = 0;
}
+
+ return 0;
}
#endif
Index: src/rfc2131.c
===================================================================
--- src/rfc2131.c (revision 696)
+++ src/rfc2131.c (revision 821)
@@ -100,8 +100,49 @@
int clid_len, unsigned char *clid, int *len_out);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
+static int check_access_script( int piperead, struct dhcp_lease *lease, struct dhcp_packet *mess, time_t now)
+{
+#ifndef NO_FORK
+unsigned int uid;
+struct event_desc ev;
+int ret;
+struct dhcp_lease _lease;
+
+ if (daemon->lease_change_command == NULL) return 0; /* ok */
+
+ if (!lease) { /* if host has not been seen before lease is NULL */
+ memset(&_lease, 0, sizeof(_lease));
+ lease = &_lease;
+ lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
+ }
+
+ uid = rand16();
+ queue_script(ACTION_ACCESS, lease, NULL, now, uid);
+
+ /* send all data to helper process */
+ do
+ {
+ helper_write();
+ } while (helper_buf_empty() == 0);
+
+ /* wait for our event */
+ ret = 0;
+ do
+ {
+ ret = async_event( piperead, now, &ev, SCRIPT_TIMEOUT);
+ }
+ while(ev.priv != uid && ret >= 0);
+
+ if (ret < 0 || ev.data != 0) /* timeout or error */
+ {
+ return -1;
+ }
+
+#endif
+ return 0; /* ok */
+}
-size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
+size_t dhcp_reply(int piperead, struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform)
{
unsigned char *opt, *clid = NULL;
@@ -252,7 +293,7 @@
mac->netid.next = netid;
netid = &mac->netid;
}
-
+
/* Determine network for this packet. Our caller will have already linked all the
contexts which match the addresses of the receiving interface but if the
machine has an address already, or came via a relay, or we have a subnet selector,
@@ -329,7 +370,7 @@
my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
}
}
-
+
mess->op = BOOTREPLY;
config = find_config(daemon->dhcp_conf, context, clid, clid_len,
@@ -418,7 +459,7 @@
else
mess->yiaddr = lease->addr;
}
-
+
if (!message &&
!lease &&
(!(lease = lease_allocate(mess->yiaddr))))
@@ -641,7 +682,14 @@
memcpy(req_options, option_ptr(opt, 0), option_len(opt));
req_options[option_len(opt)] = OPTION_END;
}
-
+
+ if (mess_type == DHCPREQUEST || mess_type == DHCPDISCOVER)
+ if (check_access_script(piperead, lease, mess, now) < 0)
+ {
+ my_syslog(LOG_INFO, _("Ignoring client due to access script"));
+ return 0;
+ }
+
switch (mess_type)
{
case DHCPDECLINE:
Index: src/log.c
===================================================================
--- src/log.c (revision 696)
+++ src/log.c (revision 821)
@@ -73,7 +73,7 @@
if (!log_reopen(daemon->log_file))
{
- send_event(errfd, EVENT_LOG_ERR, errno);
+ send_event(errfd, EVENT_LOG_ERR, errno, 0);
_exit(0);
}
Index: src/lease.c
===================================================================
--- src/lease.c (revision 696)
+++ src/lease.c (revision 821)
@@ -511,7 +511,7 @@
if (lease->old_hostname)
{
#ifndef NO_FORK
- queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
+ queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
#endif
free(lease->old_hostname);
lease->old_hostname = NULL;
@@ -520,7 +520,7 @@
else
{
#ifndef NO_FORK
- queue_script(ACTION_DEL, lease, lease->hostname, now);
+ queue_script(ACTION_DEL, lease, lease->hostname, now, 0);
#endif
old_leases = lease->next;
@@ -540,7 +540,7 @@
if (lease->old_hostname)
{
#ifndef NO_FORK
- queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
+ queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
#endif
free(lease->old_hostname);
lease->old_hostname = NULL;
@@ -552,7 +552,7 @@
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
{
#ifndef NO_FORK
- queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
+ queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now, 0);
#endif
lease->new = lease->changed = lease->aux_changed = 0;
Index: man/dnsmasq.8
===================================================================
--- man/dnsmasq.8 (revision 696)
+++ man/dnsmasq.8 (revision 821)
@@ -724,12 +724,15 @@
.B \-6 --dhcp-script=<path>
Whenever a new DHCP lease is created, or an old one destroyed, the
binary specified by this option is run. The arguments to the process
-are "add", "old" or "del", the MAC
+are "add", "old", "access" or "del", the MAC
address of the host (or "<null>"), the IP address, and the hostname,
if known. "add" means a lease has been created, "del" means it has
been destroyed, "old" is a notification of an existing lease when
dnsmasq starts or a change to MAC address or hostname of an existing
lease (also, lease length or expiry and client-id, if leasefile-ro is set).
+The "access" keyword means that a request was just received and depending
+on the script exit status request for address will be granted, if exit status
+is zero or not if it is non-zero.
The process is run as root (assuming that dnsmasq was originally run as
root) even if dnsmasq is configured to change UID to an unprivileged user.
The environment is inherited from the invoker of dnsmasq, and if the

View File

@@ -0,0 +1,35 @@
#!/bin/sh
# Contributed by Darren Hoo <darren.hoo@gmail.com>
# If you use dnsmasq as DHCP server on a router, you may have
# met with attackers trying ARP Poison Routing (APR) on your
# local area network. This script will setup a 'permanent' entry
# in the router's ARP table upon each DHCP transaction so as to
# make the attacker's efforts less successful.
# Usage:
# edit /etc/dnsmasq.conf and specify the path of this script
# to dhcp-script, for example:
# dhcp-script=/usr/sbin/static-arp
# if $1 is add or old, update the static arp table entry.
# if $1 is del, then delete the entry from the table
# if $1 is init which is called by dnsmasq at startup, it's ignored
ARP=/usr/sbin/arp
# Arguments.
# $1 is action (add, del, old)
# $2 is MAC
# $3 is address
# $4 is hostname (optional, may be unset)
if [ ${1} = del ] ; then
${ARP} -d $3
fi
if [ ${1} = old ] || [ ${1} = add ] ; then
${ARP} -s $3 $2
fi

View File

@@ -0,0 +1,11 @@
A remake of patch Bob Carroll had posted to dnsmasq,
now compatible with version 2.47. Hopefully he doesn't
mind (sending a copy of this mail to him too).
Maybe the patch in question is not acceptible
as it doesn't add new switch, rather it binds itself to "strict-order".
What it does is: if you have strict-order in the
dnsmasq config file and query a domain that would result
in NXDOMAIN, it iterates the whole given nameserver list
until the last one says NXDOMAIN.

View File

@@ -0,0 +1,17 @@
diff -ur dnsmasq-2.47/src/forward.c dnsmasq-2.47-patched/src/forward.c
--- dnsmasq-2.47/src/forward.c 2009-02-01 17:59:48.000000000 +0200
+++ dnsmasq-2.47-patched/src/forward.c 2009-03-18 19:10:22.000000000 +0200
@@ -488,9 +488,12 @@
return;
server = forward->sentto;
+
+ if ( (header->rcode == NXDOMAIN) && ((daemon->options & OPT_ORDER) != 0) && (server->next != NULL) )
+ header->rcode = SERVFAIL;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
- !(daemon->options & OPT_ORDER) &&
+ ((daemon->options & OPT_ORDER) != 0) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{

Binary file not shown.

View File

@@ -21,6 +21,9 @@ and avoids startup races with the provider of nameserver information.
Dnsmasq provides one service on the DBus: uk.org.thekelleys.dnsmasq
and a single object: /uk/org/thekelleys/dnsmasq
1. METHODS
----------
Methods are of the form
uk.org.thekelleys.<method>
@@ -91,4 +94,38 @@ Each call to SetServers completely replaces the set of servers
specified by via the DBus, but it leaves any servers specified via the
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
2. SIGNALS
----------
If dnsmasq's DHCP server is active, it will send signals over DBUS whenever
the DHCP lease database changes. Think of these signals as transactions on
a database with the IP address acting as the primary key.
Signals are of the form:
uk.org.thekelleys.<signal>
and their parameters are:
STRING "192.168.1.115"
STRING "01:23:45:67:89:ab"
STRING "hostname.or.fqdn"
Available signals are:
DhcpLeaseAdded
---------------
This signal is emitted when a DHCP lease for a given IP address is created.
DhcpLeaseDeleted
----------------
This signal is emitted when a DHCP lease for a given IP address is deleted.
DhcpLeaseUpdated
----------------
This signal is emitted when a DHCP lease for a given IP address is updated.

View File

@@ -5,12 +5,10 @@
<policy user="root">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
<allow send_interface="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy context="default">
<deny own="uk.org.thekelleys.dnsmasq"/>
<deny send_destination="uk.org.thekelleys.dnsmasq"/>
<deny send_interface="uk.org.thekelleys.dnsmasq"/>
</policy>
</busconfig>

View File

@@ -66,12 +66,12 @@
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# --server=10.1.2.3@eth1
# server=10.1.2.3@eth1
# and this sets the source (ie local) address used to talk to
# 10.1.2.3 to 192.168.1.1 port 55 (there must be a interface with that
# IP on the machine, obviously).
# --server=10.1.2.3@192.168.1.1#55
# server=10.1.2.3@192.168.1.1#55
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
@@ -122,6 +122,12 @@
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Set a different domain for a particular subnet
#domain=wireless.thekelleys.org.uk,192.168.2.0/24
# Same idea, but range rather then subnet
#domain=reserved.thekelleys.org.uk,192.68.3.100,192.168.3.200
# 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 network, you will need to
@@ -135,10 +141,22 @@
# 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
# This is an example of a DHCP range which sets a tag, so that
# some DHCP options may be set only for this network.
#dhcp-range=red,192.168.0.50,192.168.0.150
#dhcp-range=set:red,192.168.0.50,192.168.0.150
# Use this DHCP range only when the tag "green" is set.
#dhcp-range=tag:green,192.168.0.50,192.168.0.150,12h
# Specify a subnet which can't be used for dynamic address allocation,
# is available for hosts with matching --dhcp-host lines. Note that
# dhcp-host declarations will be ignored unless there is a dhcp-range
# of some type for the subnet in question.
# In this case the netmask is implied (it comes from the network
# configuration on the machine running dnsmasq) it is possible to give
# an explict netmask instead.
#dhcp-range=192.168.0.0,static
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
@@ -157,6 +175,14 @@
# the name fred and IP address 192.168.0.60 and lease time 45 minutes
#dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
# Give a host with ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
# that these two ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.
#dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.60
# Give the machine which says its name is "bert" IP address
# 192.168.0.70 and an infinite lease
#dhcp-host=bert,192.168.0.70,infinite
@@ -186,29 +212,29 @@
# 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
#dhcp-host=11:22:33:44:55:66,set:red
# Send extra options which are tagged as "red" to
# any machine with ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,net:red
#dhcp-host=11:22:33:*:*:*,set:red
# Ignore any clients which are specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unkown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=#known
#dhcp-ignore=tag:!known
# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=red,Linux
#dhcp-vendorclass=set:red,Linux
# Send extra options which are tagged as "red" to any machine one
# of whose DHCP userclass strings includes the substring "accounts"
#dhcp-userclass=red,accounts
#dhcp-userclass=set:red,accounts
# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.
#dhcp-mac=red,00:60:8C:*:*:*
#dhcp-mac=set:red,00:60:8C:*:*:*
# 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
@@ -262,20 +288,20 @@
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
# Note that the net: part must precede the option: part.
#dhcp-option = net:red, option:ntp-server, 192.168.1.1
# Note that the tag: part must precede the option: part.
#dhcp-option = tag:red, option:ntp-server, 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.
# you may want to uncomment some or all of 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.
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
@@ -315,7 +341,7 @@
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for BOOTP. You will only need
# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
@@ -324,10 +350,54 @@
# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=net:#gpxe,undionly.kpxe
#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=tag:!gpxe,undionly.kpxe
#dhcp-boot=mybootimage
# Encapsulated options for Etherboot gPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 189, 1b # BIOS drive code
#dhcp-option=encap:175, 190, user # iSCSI username
#dhcp-option=encap:175, 191, pass # iSCSI password
# Test for the architecture of a netboot client. PXE clients are
# supposed to send their architecture as option 93. (See RFC 4578)
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
#pxe-prompt="What system shall I netboot?"
# or with timeout before first available action is taken:
#pxe-prompt="Press F8 for menu.", 60
# Available boot services. for PXE.
#pxe-service=x86PC, "Boot from local disk"
# Loads <tftp-root>/pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux
# Loads <tftp-root>/pxelinux.0 from TFTP server at 1.2.3.4.
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1
# Use bootserver at a known IP address.
#pxe-service=x86PC, "Install windows from RIS server", 1, 1.2.3.4
# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# Enable dnsmasq's built-in TFTP server
#enable-tftp
@@ -338,11 +408,17 @@
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.
#tftp-no-blocksize
# Set the boot file name only when the "red" tag is set.
#dhcp-boot=net:red,pxelinux.red-net
# An example of dhcp-boot with an external server: the name and IP
# An example of dhcp-boot with an external TFTP server: the name and IP
# address of the server are given after the filename.
# Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
@@ -395,7 +471,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
# and this maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
#alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
@@ -425,11 +502,11 @@
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
# ldapserver.example.com port 389
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289 (using domain=)
# ldapserver.example.com port 389 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
@@ -458,6 +535,10 @@
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# Provide an alias for a "local" DNS name. Note that this _only_ works
# for targets which are names from DHCP or /etc/hosts. Give host
# "bert" another name, bertrand
#cname=bertand,bert
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.

View File

@@ -11,7 +11,7 @@ Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
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/TFTP for network booting of diskless machines.
DHCP leases and BOOTP/TFTP/PXE for network booting of diskless machines.
<P>
Dnsmasq is targeted at home networks using NAT and
connected to the internet via a modem, cable-modem or ADSL
@@ -89,12 +89,15 @@ Dnsmasq is part of the Debian distribution, it can be downloaded from
<A HREF="http://ftp.debian.org/debian/pool/main/d/dnsmasq/"> here</A> or installed using <TT>apt</TT>.
<H2>Links.</H2>
There is an article in German on dnsmasq at <A
HREF="http://www.linuxnetmag.com/de/issue7/m7dnsmasq1.html">http://www.linuxnetmag.com/de/issue7/m7dnsmasq1.html</A>
and Damien Raude-Morvan has one in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
Damien Raude-Morvan has an article in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
There is a good article about dnsmasq at <A
HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
and Ilya Evseev has an article in Russian about dnsmasq to be found at <A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq"> http://ilya-evseev.narod.ru/articles/dnsmasq</A>
and another at <A
HREF="http://www.linux.com/articles/149040">http://www.linux.com/articles/149040</A>
and Ilya Evseev has an article in Russian about dnsmasq to be found at
<A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq">
http://ilya-evseev.narod.ru/articles/dnsmasq</A>. Ismael Ull has an
article about dnsmasq in Spanish at <A HREF="http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq">http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq</A>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details.

View File

@@ -15,8 +15,8 @@ contents of /etc/hosts so that local hostnames
which do not appear in the global DNS can be resolved and also answers
DNS queries for DHCP configured hosts.
.PP
The dnsmasq DHCP server supports static address assignments, multiple
networks, DHCP-relay and RFC3011 subnet specifiers. It automatically
The dnsmasq DHCP server supports static address assignments and multiple
networks. It automatically
sends a sensible default set of DHCP options, and can be configured to
send any desired set of DHCP options, including vendor-encapsulated
options. It includes a secure, read-only,
@@ -31,17 +31,22 @@ 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 --test
Read and syntax check configuration file(s). Exit with code 0 if all
is OK, or a non-zero code otherwise. Do not start up dnsmasq.
.TP
.B \-h, --no-hosts
Don't read the hostnames in /etc/hosts.
.TP
.B \-H, --addn-hosts=<file>
Additional hosts file. Read the specified file as well as /etc/hosts. If -h is given, read
only the specified file. This option may be repeated for more than one
additional hosts file.
additional hosts file. If a directory is given, then read all the files contained in that directory.
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names.
in the same way as for DHCP-derived names. Note that this does not
apply to domain names in cnames, PTR records, TXT records etc.
.TP
.B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases
@@ -60,6 +65,12 @@ cache the reply. This option gives a default value for time-to-live
(in seconds) which dnsmasq uses to cache negative replies even in
the absence of an SOA record.
.TP
.B --max-ttl=<time>
Set a maximum TTL value that will be handed out to clients. The specified
maximum TTL will be given to clients instead of the true TTL value if it is
lower. The true TTL value is however kept in the cache to avoid flooding
the upstream DNS servers.
.TP
.B \-k, --keep-in-foreground
Do not go into the background at startup but otherwise run as
normal. This is intended for use when dnsmasq is run under daemontools
@@ -77,9 +88,10 @@ Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on r
.B \-8, --log-facility=<facility>
Set the facility to which dnsmasq will send syslog entries, this
defaults to DAEMON, and to LOCAL0 when debug mode is in operation. If
the facilty given contains at least one '/' character, it is taken to
the facility given contains at least one '/' character, it is taken to
be a filename, and dnsmasq logs to the given file, instead of
syslog. (Errors whilst reading configuration will still go to syslog,
syslog. If the facility is '-' then dnsmasq logs to stderr.
(Errors whilst reading configuration will still go to syslog,
but all output from a successful startup, and all output whilst
running, will go exclusively to the file.) When logging to a file,
dnsmasq will close and reopen the file when it receives SIGUSR2. This
@@ -118,13 +130,21 @@ to zero completely disables DNS function, leaving only DHCP and/or TFTP.
.TP
.B \-P, --edns-packet-max=<size>
Specify the largest EDNS.0 UDP packet which is supported by the DNS
forwarder. Defaults to 1280, which is the RFC2671-recommended maximum
for ethernet.
forwarder. Defaults to 4096, which is the RFC5625-recommended size.
.TP
.B \-Q, --query-port=<query_port>
Send outbound DNS queries from, and listen for their replies on, the specific UDP port <query_port> instead of using one chosen at runtime. Useful to simplify your
firewall rules; without this, your firewall would have to allow connections from outside DNS servers to a range of UDP ports, or dynamically adapt to the
port being used by the current dnsmasq instance.
Send outbound DNS queries from, and listen for their replies on, the
specific UDP port <query_port> instead of using random ports. NOTE
that using this option will make dnsmasq less secure against DNS
spoofing attacks but it may be faster and use less resources. Setting this option
to zero makes dnsmasq use a single port allocated to it by the
OS: this was the default behaviour in versions prior to 2.43.
.TP
.B --min-port=<port>
Do not use ports less than that given as source for outbound DNS
queries. Dnsmasq picks random ports as source for outbound queries:
when this option is given, the ports used will always to larger
than that specified. Useful for systems behind firewalls.
.TP
.B \-i, --interface=<interface name>
Listen only on the specified interface(s). Dnsmasq automatically adds
@@ -199,13 +219,17 @@ Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192
which are not found in /etc/hosts or the DHCP leases file are answered
with "no such domain" rather than being forwarded upstream.
.TP
.B \-V, --alias=<old-ip>,<new-ip>[,<mask>]
.B \-V, --alias=[<old-ip>]|[<start-ip>-<end-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".
Cisco PIX routers call "DNS doctoring". If the old IP is given as
range, then only addresses in the range, rather than a whole subnet,
are re-written. So
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
maps 192.168.0.10->192.168.0.40 to 10.0.0.10->10.0.0.40
.TP
.B \-B, --bogus-nxdomain=<ipaddr>
Transform replies which contain the IP address given into "No such
@@ -259,6 +283,17 @@ Reject (and log) addresses from upstream nameservers which are in the
private IP ranges. This blocks an attack where a browser behind a
firewall is used to probe machines on the local network.
.TP
.B --rebind-localhost-ok
Exempt 127.0.0.0/8 from rebinding checks. This address range is
returned by realtime black hole servers, so blocking it may disable
these services.
.TP
.B --rebind-domain-ok=[<domain>]|[[/<domain>/[<domain>/]
Do not detect and block dns-rebind on queries to these domains. The
argument may be either a single domain, or multiple domains surrounded
by '/', like the --server syntax, eg.
.B --rebind-domain-ok=/domain1/domain2/domain3/
.TP
.B \-n, --no-poll
Don't poll /etc/resolv.conf for changes.
.TP
@@ -291,7 +326,19 @@ dots in them. A non-standard port may be specified as
part of the IP
address using a # character.
More than one -S flag is allowed, with
repeated domain or ipaddr parts as required.
repeated domain or ipaddr parts as required.
More specific domains take precendence over less specific domains, so:
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/2.3.4.5
will send queries for *.google.com to 1.2.3.4, except *www.google.com,
which will go to 2.3.4.5
The special server address '#' means, "use the standard servers", so
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/#
will send queries for *.google.com to 1.2.3.4, except *www.google.com which will
be forwarded as usual.
Also permitted is a -S
flag which gives a domain but no IP address; this tells dnsmasq that
@@ -373,12 +420,23 @@ so any number may be included, split by commas.
.B --ptr-record=<name>[,<target>]
Return a PTR DNS record.
.TP
.B --naptr-record=<name>,<order>,<preference>,<flags>,<service>,<regexp>[,<replacement>]
Return an NAPTR DNS record, as specified in RFC3403.
.TP
.B --cname=<cname>,<target>
Return a CNAME record which indicates that <cname> is really
<target>. There are significant limitations on the target; it must be a
DNS name which is known to dnsmasq from /etc/hosts (or additional
hosts files) or from DHCP. If the target does not satisfy this
criteria, the whole cname is ignored. The cname must be unique, but it
is permissable to have more than one cname pointing to the same target.
.TP
.B --interface-name=<name>,<interface>
Return a DNS record associating the name with the primary address on
the given interface. This flag specifies an A record for the given
name in the same way as an /etc/hosts line, except that the address is
not constant, but taken from the given interface. If the interface is
down, not configured or non-existant, an empty record is returned. The
down, not configured or non-existent, an empty record is returned. The
matching PTR record is also created, mapping the interface address to
the name. More than one name may be associated with an interface
address by repeating the flag; in that case the first instance is used
@@ -398,39 +456,50 @@ Set the maximum number of concurrent DNS queries. The default value is
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries.
.TP
.B \-F, --dhcp-range=[[net:]network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<netmask>[,<broadcast>]][,<lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
in
.B dhcp-host
options. If the lease time is given, then leases
will be given for that length of time. The lease time is in seconds,
or minutes (eg 45m) or hours (eg 1h) or the literal "infinite". The
minimum lease time is two minutres. This
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
the default lease time is one hour. The
minimum lease time is two minutes. This
option may be repeated, with different addresses, to enable DHCP
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
receive 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 before version 4.0. It is always
allowed to have more than one dhcp-range in a single subnet. The optional
network-id is a alphanumeric label which marks this network so that
always optional. It is always
allowed to have more than one dhcp-range in a single subnet.
The optional
.B set:<tag>
sets an alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis.
When it is prefixed with 'net:' then its meaning changes from setting
When it is prefixed with 'tag:' instead, then its meaning changes from setting
a tag to matching it. Only one tag may be set, but more than one tag may be matched.
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
to dynamically allocate IP addresses: only hosts which have static
addresses given via
.B dhcp-host
or from /etc/ethers will be served.
or from /etc/ethers will be served. The end address may be replaced by
the keyword
.B proxy
in which case dnsmasq will provide proxy-DHCP on the specified
subnet. (See
.B pxe-prompt
and
.B pxe-service
for details.)
The interface:<interface name> section is not normally used. See the
NOTES section for details of this.
.TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<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
@@ -445,46 +514,79 @@ 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 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
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 in
the same subnet as some valid dhcp-range. For
subnets which don't need a pool of dynamically allocated addresses,
use the "static" keyword in the dhcp-range declaration.
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,.....
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"
option specifying the name also exists. Only one hostname can be
given in a
.B dhcp-host
option, but aliases are possible by using CNAMEs. (See
.B --cname
).
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> sets the network-id tag
whenever this dhcp-host directive is in use.This can be used to
selectively send DHCP options just for this host. When a host matches any
be used by some machines.
The set:<tag> contruct sets the tag
whenever this dhcp-host directive is in use. This can be used to
selectively send DHCP options just for this host. More than one tag
can be set in a dhcp-host directive (but not in other places where
"set:<tag>" is allowed). When a host matches any
dhcp-host directive (or one implied by /etc/ethers) then the special
network-id tag "known" is set. This allows dnsmasq to be configured to
tag "known" is set. This allows dnsmasq to be configured to
ignore requests from unknown machines using
.B --dhcp-ignore=#known
.B --dhcp-ignore=tag:!known
Ethernet addresses (but not client-ids) may have
wildcard bytes, so for example
.B --dhcp-host=00:20:e0:3b:13:*,ignore
will cause dnsmasq to ignore a range of hardware addresses. Note that
the "*" will need to be escaped or quoted on a command line, but not
in the configuration file. Hardware addresses normally match any
in the configuration file.
Hardware addresses normally match any
network (ARP) type, but it is possible to restrict them to a single
ARP type by preceding them with the ARP-type (in HEX) and "-". so
.B --dhcp-host=06-00:20:e0:3b:13:af,1.2.3.4
will only match a
Token-Ring hardware address, since the ARP-address type for token ring
is 6.
is 6.
As a special case, it is possible to include more than one
hardware address. eg:
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
This allows an IP address to be associated with
multiple hardware addresses, and gives dnsmasq permission to abandon a
DHCP lease to one of the hardware addresses when another one asks for
a lease. Beware that this is a dangerous thing to do, it will only
work reliably if only one of the hardware addresses is active at any
time and there is no way for dnsmasq to enforce this. It is, for instance,
useful to allocate a stable IP address to a laptop which
has both wired and wireless interfaces.
.TP
.B --dhcp-hostsfile=<file>
Read DHCP host information from the specified file. The file contains
@@ -496,7 +598,12 @@ the file will be re-read when dnsmasq receives SIGHUP.
.B --dhcp-optsfile=<file>
Read DHCP option information from the specified file. The advantage of
using this option is the same as for --dhcp-hostsfile: the
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP.
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a
.B --dhcp-boot
flag as DHCP options, using the options names bootfile-name,
server-ip-address and tftp-server. This allows these to be included
in a dhcp-optsfile.
.TP
.B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The
@@ -507,7 +614,7 @@ have exactly the same effect as
options containing the same information. /etc/ethers is re-read when
dnsmasq receives SIGHUP.
.TP
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
Specify 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
@@ -530,8 +637,8 @@ or
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-separated hex digits
and a text string. If the optional network-ids are given then
this option is only sent when all the network-ids are matched.
and a text string. If the optional tags are given then
this option is only sent when all the tags are matched.
Special processing is done on a text argument for option 119, to
conform with RFC 3397. Text or dotted-quad IP addresses as arguments
@@ -567,10 +674,24 @@ client. It is
possible to omit the vendorclass completely;
.B --dhcp-option=vendor:,1,0.0.0.0
in which case the encapsulated option is always sent.
Options may be encapsulated within other options: for instance
.B --dhcp-option=encap:175, 190, "iscsi-client0"
will send option 175, within which is the option 190. If multiple
options are given which are encapsulated with the same option number
then they will be correctly combined into one encapsulated option.
encap: and vendor: are may not both be set in the same dhcp-option.
The final variant on encapsulated options is "Vendor-Identifying
Vendor Options" as specified by RFC3925. These are denoted like this:
.B --dhcp-option=vi-encap:2, 10, "text"
The number in the vi-encap: section is the IANA enterprise number
used to identify this option.
The address 0.0.0.0 is not treated specially in
encapsulated vendor class options.
encapsulated options.
.TP
.B --dhcp-option-force=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]]
.B --dhcp-option-force=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]]
This works in exactly the same way as
.B --dhcp-option
except that the option will always be sent, even if the client does
@@ -585,20 +706,20 @@ DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Map from a vendor-class string to a network id tag. Most DHCP clients provide a
.B \-U, --dhcp-vendorclass=set:<tag>,<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option
maps vendor classes to tags, so that DHCP options may be selectively delivered
to different classes of hosts. For example
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
.B dhcp-vendorclass=set: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
.B --dhcp-option=tag:printers,3,192.168.4.4
The vendor-class string is
substring matched against the vendor-class supplied by the client, to
allow fuzzy matching.
allow fuzzy matching. The set: prefix is optional but allowed for consistency.
.TP
.B \-j, --dhcp-userclass=<network-id>,<user-class>
Map from a user-class string to a network id tag (with substring
.B \-j, --dhcp-userclass=set:<tag>,<user-class>
Map from a user-class string to a tag (with substring
matching, like vendor classes). Most DHCP clients provide a
"user class" which is configurable. This option
maps user classes to tags, so that DHCP options may be selectively delivered
@@ -606,62 +727,162 @@ to different classes of hosts. It is possible, for instance to use
this to set a different printer server for hosts in the class
"accounts" than for hosts in the class "engineering".
.TP
.B \-4, --dhcp-mac=<network-id>,<MAC address>
Map from a MAC address to a network-id tag. The MAC address may include
.B \-4, --dhcp-mac=set:<tag>,<MAC address>
Map from a MAC address to a tag. The MAC address may include
wildcards. For example
.B --dhcp-mac=3com,01:34:23:*:*:*
.B --dhcp-mac=set:3com,01:34:23:*:*:*
will set the tag "3com" for any host whose MAC address matches the pattern.
.TP
.B --dhcp-circuitid=<network-id>,<circuit-id>, --dhcp-remoteid=<network-id>,<remote-id>
Map from RFC3046 relay agent options to network-id tags. This data may
.B --dhcp-circuitid=set:<tag>,<circuit-id>, --dhcp-remoteid=set:<tag>,<remote-id>
Map from RFC3046 relay agent options to tags. This data may
be provided by DHCP relay agents. The circuit-id or remote-id is
normally given as colon-separated hex, but is also allowed to be a
simple string. If an exact match is achieved between the circuit or
agent ID and one provided by a relay agent, the network-id tag is set.
agent ID and one provided by a relay agent, the tag is set.
.TP
.B --dhcp-subscrid=<network-id>,<subscriber-id>
Map from RFC3993 subscriber-id relay agent options to network-id tags.
.B --dhcp-subscrid=set:<tag>,<subscriber-id>
Map from RFC3993 subscriber-id relay agent options to tags.
.TP
.B --dhcp-match=<network-id>,<option number>
Set the network-id tag if the client sends a DHCP option of the given
number. This can be used to identify particular clients which send
information using private option numbers.
.B --dhcp-proxy[=<ip addr>]......
A normal DHCP relay agent is only used to forward the initial parts of
a DHCP interaction to the DHCP server. Once a client is configured, it
communicates directly with the server. This is undesirable if the
relay agent is addding extra information to the DHCP packets, such as
that used by
.B dhcp-circuitid
and
.B dhcp-remoteid.
A full relay implementation can use the RFC 5107 serverid-override
option to force the DHCP server to use the relay as a full proxy, with all
packets passing through it. This flag provides an alternative method
of doing the same thing, for relays which don't support RFC
5107. Given alone, it manipulates the server-id for all interactions
via relays. If a list of IP addresses is given, only interactions via
relays at those addresses are affected.
.TP
.B \-J, --dhcp-ignore=<network-id>[,<network-id>]
When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore the host and do
.B --dhcp-match=set:<tag>,<option number>|option:<option name>|vi-encap:<enterprise>[,<value>]
Without a value, set the tag if the client sends a DHCP
option of the given number or name. When a value is given, set the tag only if
the option is sent and matches the value. The value may be of the form
"01:ff:*:02" in which case the value must match (apart from widcards)
but the option sent may have unmatched data past the end of the
value. The value may also be of the same form as in
.B dhcp-option
in which case the option sent is treated as an array, and one element
must match, so
--dhcp-match=set:efi-ia32,option:client-arch,6
will set the tag "efi-ia32" if the the number 6 appears in the list of
architectures sent by the client in option 93. (See RFC 4578 for
details.) If the value is a string, substring matching is used.
The special form with vi-encap:<enterpise number> matches against
vendor-identifying vendor classes for the specified enterprise. Please
see RFC 3925 for more details of these rare and interesting beasts.
.TP
.B --tag-if=set:<tag>[,set:<tag>[,tag:<tag>[,tag:<tag>]]]
Perform boolean operations on tags. Any tag appearing as set:<tag> is set if
all the tags which appear as tag:<tag> are set, (or unset when tag:!<tag> is used)
If no tag:<tag> appears set:<tag> tags are set unconditionally.
Any number of set: and tag: forms may appear, in any order.
Tag-if lines ares executed in order, so if the tag in tag:<tag> is a
tag set by another
.B tag-if,
the line which sets the tag must precede the one which tests it.
.TP
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
When all the given tags appear in the tag set ignore the host and do
not allocate it a DHCP lease.
.TP
.B --dhcp-ignore-names[=<network-id>[,<network-id>]]
When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore any hostname
provided by the host. Note that, unlike dhcp-ignore, it is permissable
to supply no netid tags, in which case DHCP-client supplied hostnames
.B --dhcp-ignore-names[=tag:<tag>[,tag:<tag>]]
When all the given tags appear in the tag set, ignore any hostname
provided by the host. Note that, unlike dhcp-ignore, it is permissible
to supply no tags, in which case DHCP-client supplied hostnames
are always ignored, and DHCP hosts are added to the DNS using only
dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
/etc/ethers.
.TP
.B --dhcp-broadcast=<network-id>[,<network-id>]
When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, always use broadcast to
communicate with the host when it is unconfigured. Most DHCP clients which
.B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
Generate a name for DHCP clients which do not otherwise have one,
using the MAC address expressed in hex, seperated by dashes. Note that
if a host provides a name, it will be used by preference to this,
unless
.B --dhcp-ignore-names
is set.
.TP
.B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]]
When all the given tags appear in the tag set, always use broadcast to
communicate with the host when it is unconfigured. It is permissible
to supply no tags, in which case this is unconditional. Most DHCP clients which
need broadcast replies set a flag in their requests so that this
happens automatically, some old BOOTP clients do not.
.TP
.B \-M, --dhcp-boot=[net:<network-id>,]<filename>,[<servername>[,<server address>]]
.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>]]
Set BOOTP options to be returned by the DHCP server. Server name and
address are optional: if not provided, the name is left empty, and the
address set to the address of the machine running dnsmasq. If dnsmasq
is providing a TFTP service (see
.B --enable-tftp
) then only the filename is required here to enable network booting.
If the optional network-id(s) are given,
they must match for this configuration to be sent. Note that
network-ids are prefixed by "net:" to distinguish them.
If the optional tag(s) are given,
they must match for this configuration to be sent.
.TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>]
Most uses of PXE boot-ROMS simply allow the PXE
system to obtain an IP address and then download the file specified by
.B dhcp-boot
and execute it. However the PXE system is capable of more complex
functions when supported by a suitable DHCP server.
This specifies a boot option which may appear in a PXE boot menu. <CSA> is
client system type, only services of the correct type will appear in a
menu. The known types are x86PC, PC98, IA64_EFI, Alpha, Arc_x86,
Intel_Lean_Client, IA32_EFI, BC_EFI, Xscale_EFI and X86-64_EFI; an
integer may be used for other types. The
parameter after the menu text may be a file name, in which case dnsmasq acts as a
boot server and directs the PXE client to download the file by TFTP,
either from itself (
.B enable-tftp
must be set for this to work) or another TFTP server if the final IP
address is given.
Note that the "layer"
suffix (normally ".0") is supplied by PXE, and should not be added to
the basename. If an integer boot service type, rather than a basename
is given, then the PXE client will search for a
suitable boot service for that type on the network. This search may be done
by broadcast, or direct to a server if its IP address is provided.
If no boot service type or filename is provided (or a boot service type of 0 is specified)
then the menu entry will abort the net boot procedure and
continue booting from local media.
.TP
.B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>]
Setting this provides a prompt to be displayed after PXE boot. If the
timeout is given then after the
timeout has elapsed with no keyboard input, the first available menu
option will be automatically executed. If the timeout is zero then the first available menu
item will be executed immediately. If
.B pxe-prompt
is ommitted the system will wait for user input if there are multiple
items in the menu, but boot immediately if
there is only one. See
.B pxe-service
for details of menu items.
Dnsmasq supports PXE "proxy-DHCP", in this case another DHCP server on
the network is responsible for allocating IP addresses, and dnsmasq
simply provides the information given in
.B pxe-prompt
and
.B pxe-service
to allow netbooting. This mode is enabled using the
.B proxy
keyword in
.B dhcp-range.
.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
default is 1000. This limit is to prevent DoS attacks from hosts which
create thousands of leases and use lots of memory in the dnsmasq
process.
.TP
@@ -671,13 +892,23 @@ It changes the behaviour from strict RFC compliance so that DHCP requests on
unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances. It also
allows dnsmasq to rebuild its lease database without each client needing to
reaquire a lease, if the database is lost.
reacquire a lease, if the database is lost.
.TP
.B \-3, --bootp-dynamic
.B --dhcp-alternate-port[=<server port>[,<client port>]]
Change the ports used for DHCP from the default. If this option is
given alone, without arguments, it changes the ports used for DHCP
from 67 and 68 to 1067 and 1068. If a single argument is given, that
port number is used for the server and the port number plus one used
for the client. Finally, two port numbers allows arbitrary
specification of both server and client ports for DHCP.
.TP
.B \-3, --bootp-dynamic[=<network-id>[,<network-id>]]
Enable dynamic allocation of IP addresses to BOOTP clients. Use this
with care, since each address allocated to a BOOTP client is leased
forever, and therefore becomes permanently unavailable for re-use by
other hosts.
other hosts. if this is given without tags, then it unconditionally
enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets.
.TP
.B \-5, --no-ping
By default, the DHCP server will attempt to ensure that an address in
@@ -688,59 +919,86 @@ tried. This flag disables this check. Use with caution.
.TP
.B --log-dhcp
Extra logging for DHCP: log all the options sent to DHCP clients and
the netid tags used to determine them.
the tags used to determine them.
.TP
.B \-l, --dhcp-leasefile=<path>
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. In any case note that ISC leasefile integration is a deprecated
feature. It should not be used in new installations, and will be
removed in a future release.
Use the specified file to store DHCP lease information.
.TP
.B \-6 --dhcp-script=<path>
Whenever a new DHCP lease is created, or an old one destroyed, the
binary specified by this option is run. The arguments to the process
executable specified by this option is run. <path>
must be an absolute pathname, no PATH search occurs.
The arguments to the process
are "add", "old" or "del", the MAC
address of the host (or "<null>"), the IP address, and the hostname,
address of the host, the IP address, and the hostname,
if known. "add" means a lease has been created, "del" means it has
been destroyed, "old" is a notification of an existing lease when
dnsmasq starts or a change to MAC address or hostname of an existing
lease (also, lease length or expiry and client-id, if leasefile-ro is set).
The process is run as root (assuming that dnsmasq was originally run as
If the MAC address is from a network type other than ethernet,
it will have the network type prepended, eg "06-01:23:45:67:89:ab" for
token ring. The process is run as root (assuming that dnsmasq was originally run as
root) even if dnsmasq is configured to change UID to an unprivileged user.
The environment is inherited from the invoker of dnsmasq, and if the
host provided a client-id, this is stored in the environment variable
DNSMASQ_CLIENT_ID. If the client provides vendor-class or user-class
information, these are provided in DNSMASQ_VENDOR_CLASS and
The environment is inherited from the invoker of dnsmasq, with some or
all of the following variables added.
DNSMASQ_CLIENT_ID if the host provided a client-id.
DNSMASQ_DOMAIN if the fully-qualified domain name of the host is
known, this is set to the domain part.
If the client provides vendor-class, hostname or user-class,
these are provided in DNSMASQ_VENDOR_CLASS
DNSMASQ_SUPPLIED_HOSTNAME and
DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn variables, but only for
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database. If dnsmasq was compiled with HAVE_BROKEN_RTC, then
database.
If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in
DNSMASQ_LEASE_LENGTH, otherwise the time of lease expiry is stored in
DNSMASQ_LEASE_EXPIRES. The number of seconds until lease expiry is
always stored in DNSMASQ_TIME_REMAINING.
If a lease used to have a hostname, which is
removed, an "old" event is generated with the new state of the lease,
ie no name, and the former name is provided in the environment
variable DNSMASQ_OLD_HOSTNAME. DNSMASQ_INTERFACE stores the name of
the interface on which the reuest arrived; this is not set for "old"
actions when dnsmasq restarts.
All file decriptors are
variable DNSMASQ_OLD_HOSTNAME.
DNSMASQ_INTERFACE stores the name of
the interface on which the request arrived; this is not set for "old"
actions when dnsmasq restarts.
DNSMASQ_RELAY_ADDRESS is set if the client
used a DHCP relay to contact dnsmasq and the IP address of the relay
is known.
DNSMASQ_TAGS contains all the tags set during the
DHCP transaction, separated by spaces.
All file descriptors are
closed except stdin, stdout and stderr which are open to /dev/null
(except in debug mode).
The script is not invoked concurrently: if subsequent lease
changes occur, the script is not invoked again until any existing
invocation exits. At dnsmasq startup, the script will be invoked for
The script is not invoked concurrently: at most one instance
of the script is ever running (dnsmasq waits for an instance of script to exit
before running the next). Changes to the lease database are which
require the script to be invoked are queued awaiting exit of a running instance.
If this queueing allows multiple state changes occur to a single
lease before the script can be run then
earlier states are discarded and the current state of that lease is
reflected when the script finally runs.
At dnsmasq startup, the script will be invoked for
all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". <path>
must be an absolute pathname, no PATH search occurs. When dnsmasq
leases will be called with "del" and others with "old". When dnsmasq
receives a HUP signal, the script will be invoked for existing leases
with an "old " event.
with an "old " event.
.TP
.B --dhcp-scriptuser
Specify the user as which to run the lease-change script. This defaults to root, but can be changed to another user using this flag.
.TP
.B \-9, --leasefile-ro
Completely suppress use of the lease database file. The file will not
@@ -758,12 +1016,13 @@ to the client-id and lease length and expiry time.
.TP
.B --bridge-interface=<interface>,<alias>[,<alias>]
Treat DHCP request packets arriving at any of the <alias> interfaces
as if they had arrived at <interface>. This option is only available
on FreeBSD and Dragonfly BSD, and is necessary when using "old style" bridging, since
as if they had arrived at <interface>. This option is necessary when
using "old style" bridging on BSD platforms, since
packets arrive at tap interfaces which don't have an IP address.
.TP
.B \-s, --domain=<domain>
Specifies the domain for the DHCP server. This has two effects;
.B \-s, --domain=<domain>[,<address range>]
Specifies DNS domains for the DHCP server. Domains may be be given
unconditionally (without the IP range) or for limited IP ranges. This has two effects;
firstly it causes the DHCP server to return the domain to any hosts
which request it, and secondly it sets the domain which it is legal
for DHCP-configured hosts to claim. The intention is to constrain
@@ -780,19 +1039,43 @@ and have a machine whose DHCP hostname is "laptop". The IP address for that mach
.B dnsmasq
both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
given as "#" then the domain is read from the first "search" directive
in /etc/resolv.conf (or equivalent).
in /etc/resolv.conf (or equivalent). The address range can be of the form
<ip address>,<ip address> or <ip address>/<netmask> or just a single
<ip address>. See
.B --dhcp-fqdn
which can change the behaviour of dnsmasq with domains.
.TP
.B --enable-tftp
.B --dhcp-fqdn
In the default mode, dnsmasq inserts the unqualified names of
DHCP clients into the DNS. For this reason, the names must be unique,
even if two clients which have the same name are in different
domains. If a second DHCP client appears which has the same name as an
existing client, the name is transfered to the new client. If
.B --dhcp-fqdn
is set, this behaviour changes: the unqualified name is no longer
put in the DNS, only the qualified name. Two DHCP clients with the
same name may both keep the name, provided that the domain part is
different (ie the fully qualified names differ.) To ensure that all
names have a domain part, there must be at least
.B --domain
without an address specified when
.B --dhcp-fqdn
is set.
.TP
.B --enable-tftp[=<interface>]
Enable the TFTP server function. This is deliberately limited to that
needed to net-boot a client: Only reading is allowed, and only in
binary/octet mode. The tsize and blksize extensions are supported.
needed to net-boot a client. Only reading is allowed; the tsize and
blksize extensions are supported (tsize is only supported in octet
mode). See NOTES section for use of the interface argument.
.TP
.B --tftp-root=<directory>
.B --tftp-root=<directory>[,<interface>]
Look for files to transfer using TFTP relative to the given
directory. When this is set, TFTP paths which include ".." are
rejected, to stop clients getting outside the specified root.
Absolute paths (starting with /) are allowed, but they must be within
the tftp-root.
the tftp-root. If the optional interface argument is given, the
directory is only used for TFTP requests via that interface.
.TP
.B --tftp-unique-root
Add the IP address of the TFTP client as a path component on the end
@@ -842,10 +1125,11 @@ of concurrent TFTP connections is limited by the size of the port range.
Specify a different configuration file. The conf-file option is also allowed in
configuration files, to include multiple configuration files.
.TP
.B \-7, --conf-dir=<directory>
.B \-7, --conf-dir=<directory>[,<file-extension>......]
Read all the files in the given directory as configuration
files. Files whose names end in ~ or start with . or start and end
with # are skipped. This flag may be given on the command
files. If extension(s) are given, any files which end in those
extensions are skipped. Any files whose names end in ~ or start with . or start and end
with # are always skipped. This flag may be given on the command
line or in a configuration file.
.SH CONFIG FILE
At startup, dnsmasq reads
@@ -902,7 +1186,7 @@ will close and reopen the log file. Note that during this operation,
dnsmasq will not be running as root. When it first creates the logfile
dnsmasq changes the ownership of the file to the non-root user it will run
as. Logrotate should be configured to create a new log file with
the ownership which matches the exising one before sending SIGUSR2.
the ownership which matches the existing one before sending SIGUSR2.
If TCP DNS queries are in progress, the old logfile will remain open in
child processes which are handling TCP queries and may continue to be
written. There is a limit of 150 seconds, after which all existing TCP
@@ -978,28 +1262,41 @@ the CNAME. To work around this, add the CNAME to /etc/hosts so that
the CNAME is shadowed too.
.PP
The network-id system works as follows: For each DHCP request, dnsmasq
collects a set of valid network-id tags, one from the
The tag system works as follows: For each DHCP request, dnsmasq
collects a set of valid tags from active configuration lines which
include set:<tag>, including one from the
.B dhcp-range
used to allocate the address, one from any matching
.B dhcp-host
and possibly many from matching vendor classes and user
classes sent by the DHCP client. Any
(and "known" if a dhcp-host matches)
The tag "bootp" is set for BOOTP requests, and a tag whose name is the
name of the interface on which the request arrived is also set.
Any configuration lines which includes one or more tag:<tag> contructs
will only be valid if all that tags are matched in the set derived
above. Typically this is dhcp-option.
.B dhcp-option
which has network-id tags will be used in preference to an untagged
which has tags will be used in preference to an untagged
.B dhcp-option,
provided that _all_ the tags match somewhere in the
set collected as described above. The prefix '#' on a tag means 'not'
so --dhcp=option=#purple,3,1.2.3.4 sends the option when the
network-id tag purple is not in the set of valid tags.
set collected as described above. The prefix '!' on a tag means 'not'
so --dhcp=option=tag:!purple,3,1.2.3.4 sends the option when the
tag purple is not in the set of valid tags. (If using this in a
command line rather than a configuration file, be sure to escape !,
which is a shell metacharacter)
.PP
If the network-id in a
Note that for
.B dhcp-range
is prefixed with 'net:' then its meaning changes from setting a
tag to matching it. Thus if there is more than dhcp-range on a subnet,
and one is tagged with a network-id which is set (for instance
from a vendorclass option) then hosts which set the netid tag will be
allocated addresses in the tagged range.
both tag:<tag> and set:<tag> are allowed, to both select the range in
use based on (eg) dhcp-host, and to affect the options sent, based on
the range selected.
This system evolved from an earlier, more limited one and for backward
compatibility "net:" may be used instead of "tag:" and "set:" may be
omitted. (Except in
.B dhcp-host,
where "net:" may be used instead of "set:".) For the same reason, '#'
may be used instead of '!' to indicate NOT.
.PP
The DHCP server in dnsmasq will function as a BOOTP server also,
provided that the MAC address and IP address for clients are given,
@@ -1012,11 +1309,56 @@ configurations or in
configuration option is present to activate the DHCP server
on a particular network. (Setting --bootp-dynamic removes the need for
static address mappings.) The filename
parameter in a BOOTP request is matched against netids in
.B dhcp-option
configurations, as is the tag "bootp", allowing some control over the options returned to
parameter in a BOOTP request is used as a tag,
as is the tag "bootp", allowing some control over the options returned to
different classes of hosts.
.B dhcp-range
may have an interface name supplied as
"interface:<interface-name>". The semantics if this are as follows:
For DHCP, if any other dhcp-range exists _without_ an interface name,
then the interface name is ignored and and dnsmasq behaves as if the
interface parts did not exist, otherwise DHCP is only provided to
interfaces mentioned in dhcp-range
declarations. For DNS, if there are no
.B --interface
or
.B --listen-address
flags, behaviour is unchanged by the interface part. If either of
these flags are present, the interfaces mentioned in
dhcp-ranges are added to the set which get DNS service.
Similarly,
.B enable-tftp
may take an interface name, which enables TFTP only for a particular
interface, ignoring
.B --interface
or
.B --listen-address
flags. In addition
.B --tftp-secure
and
.B --tftp-unique-root
and
.B --tftp-no-blocksize
are ignored for requests from such interfaces. (A
.B --tftp-root
directive giving a root path and an interface should be
provided too.)
These rules may seem odd at first sight, but they
allow a single line of the form "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200"
to be added to dnsmasq configuration which then supplies
DHCP and DNS services to that interface, without affecting
what services are supplied to other interfaces and irrespective of
the existance or lack of "interface=<interface>"
lines elsewhere in the dnsmasq configuration.
"enable-tftp=virt0" and "tftp-root=<root>,virt0" do the same job for TFTP.
The idea is
that such a line can be added automatically by libvirt
or equivalent systems, without disturbing any manual
configuration.
.SH EXIT CODES
.PP
0 - Dnsmasq successfully forked into the background, or terminated
@@ -1027,7 +1369,7 @@ normally if backgrounding is not enabled.
2 - A problem with network access occurred (address in use, attempt
to use privileged ports without permission).
.PP
3 - A problem occured with a filesystem operation (missing
3 - A problem occurred with a filesystem operation (missing
file/directory, permissions).
.PP
4 - Memory allocation failure.
@@ -1047,10 +1389,7 @@ following applies to dnsmasq-2.37: earlier versions did not scale as well.
.PP
Dnsmasq is capable of handling DNS and DHCP for at least a thousand
clients. Clearly to do this the value of
.B --dhcp-lease-max
must be increased,
and lease times should not be very short (less than one hour). The
clients. The DHCP lease times should not be very short (less than one hour). The
value of
.B --dns-forward-max
can be increased: start with it equal to
@@ -1082,6 +1421,24 @@ or an additional hosts file. The list can be very long,
dnsmasq has been tested successfully with one million names. That size
file needs a 1GHz processor and about 60Mb of RAM.
.SH INTERNATIONALISATION
Dnsmasq can be compiled to support internationalisation. To do this,
the make targets "all-i18n" and "install-i18n" should be used instead of
the standard targets "all" and "install". When internationalisation
is compiled in, dnsmasq will produce log messages in the local
language and support internationalised domain names (IDN). Domain
names in /etc/hosts, /etc/ethers and /etc/dnsmasq.conf which contain
non-ASCII characters will be translated to the DNS-internal punycode
representation. Note that
dnsmasq determines both the language for messages and the assumed
charset for configuration
files from the LANG environment variable. This should be set to the system
default value by the script which is responsible for starting
dnsmasq. When editing the configuration files, be careful to do so
using only the system-default locale and not user-specific one, since
dnsmasq has no direct way of determining the charset in use, and must
assume that it is the system default.
.SH FILES
.IR /etc/dnsmasq.conf

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1660
po/de.po

File diff suppressed because it is too large Load Diff

1595
po/es.po

File diff suppressed because it is too large Load Diff

1487
po/fi.po

File diff suppressed because it is too large Load Diff

1472
po/fr.po

File diff suppressed because it is too large Load Diff

1116
po/id.po

File diff suppressed because it is too large Load Diff

1487
po/it.po

File diff suppressed because it is too large Load Diff

1413
po/no.po

File diff suppressed because it is too large Load Diff

1672
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1424
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -63,16 +63,20 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
}
}
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len; ptr += len )
for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
{
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
#ifdef HAVE_SOCKADDR_SA_LEN
len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
#else
len = sizeof(struct ifreq);
#ifdef HAVE_SOCKADDR_SA_LEN
ifr = (struct ifreq *)ptr;
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
#endif
if (!expand_buf(&ifreq, len))
goto err;
@@ -126,7 +130,7 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
#endif
#if defined(HAVE_BSD_NETWORK)
#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
#include <net/bpf.h>
void init_bpf(void)
@@ -171,7 +175,7 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
/* Only know how to do ethernet on *BSD */
if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
{
my_syslog(LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"),
mess->htype, ifr->ifr_name);
return;
}
@@ -212,8 +216,8 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
sum = (sum & 0xffff) + (sum >> 16);
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
udp.uh_sport = htons(DHCP_SERVER_PORT);
udp.uh_dport = htons(DHCP_CLIENT_PORT);
udp.uh_sport = htons(daemon->dhcp_server_port);
udp.uh_dport = htons(daemon->dhcp_client_port);
if (len & 1)
((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
udp.uh_sum = 0;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,14 +10,17 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
static struct crec *dhcp_spare = NULL, *new_chain = NULL;
#ifdef HAVE_DHCP
static struct crec *dhcp_spare = NULL;
#endif
static struct crec *new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
@@ -45,6 +48,7 @@ static const struct {
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
{ 35, "NAPTR" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
@@ -63,7 +67,6 @@ static const struct {
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
static void cache_link(struct crec *crecp);
static char *record_source(struct hostsfile *add_hosts, int index);
static void rehash(int size);
static void cache_hash(struct crec *crecp);
@@ -223,7 +226,7 @@ char *cache_get_name(struct crec *crecp)
{
if (crecp->flags & F_BIGNAME)
return crecp->name.bname->name;
else if (crecp->flags & F_DHCP)
else if (crecp->flags & (F_DHCP | F_CONFIG))
return crecp->name.namep;
return crecp->name.sname;
@@ -359,10 +362,11 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
struct crec *new;
union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE;
int free_avail = 0;
log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* CONFIG bit no needed except for logging */
/* CONFIG bit means something else when stored in cache entries */
flags &= ~F_CONFIG;
/* if previous insertion failed give up now. */
@@ -392,8 +396,19 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (new->flags & (F_FORWARD | F_REVERSE))
{
/* If free_avail set, we believe that an entry has been freed.
Bugs have been known to make this not true, resulting in
a tight loop here. If that happens, abandon the
insert. Once in this state, all inserts will probably fail. */
if (free_avail)
{
insert_error = 1;
return NULL;
}
if (freed_all)
{
free_avail = 1; /* Must be free space now. */
cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
cache_live_freed++;
}
@@ -516,19 +531,22 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
Make sure that re-ordering doesn't break the hash-chain
order invariants.
*/
if (!insert)
{
insert = up;
ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
up = &crecp->hash_next;
}
else if ((crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
{
*up = crecp->hash_next;
crecp->hash_next = *insert;
*insert = crecp;
insert = &crecp->hash_next;
}
else
{
if (!insert)
{
insert = up;
ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
}
up = &crecp->hash_next;
}
}
else
/* case : not expired, incorrect entry. */
@@ -627,153 +645,231 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
unsigned short flags, int index, int addr_dup)
{
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
int i;
int i, nameexists = 0;
struct cname *a;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
free(cache);
else
if (lookup && (lookup->flags & F_HOSTS))
{
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as
the last one we saw, which eliminates most in the case of an ad-block
file with thousands of entries for the same address.
Then we search and bail at the first matching address that came from
a HOSTS file. Since the first host entry gets reverse, we know
then that it must exist without searching exhaustively for it. */
if (addr_dup)
flags &= ~F_REVERSE;
else
for (i=0; i<hash_size; i++)
{
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
if ((lookup->flags & F_HOSTS) &&
(lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
break;
}
if (lookup)
nameexists = 1;
if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
free(cache);
return;
}
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as
the last one we saw, which eliminates most in the case of an ad-block
file with thousands of entries for the same address.
Then we search and bail at the first matching address that came from
a HOSTS file. Since the first host entry gets reverse, we know
then that it must exist without searching exhaustively for it. */
if (addr_dup)
flags &= ~F_REVERSE;
else
for (i=0; i<hash_size; i++)
{
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
if ((lookup->flags & F_HOSTS) &&
(lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
break;
}
}
if (lookup)
break;
}
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache->name.sname, a->target) &&
(lookup = whine_malloc(sizeof(struct crec))))
{
lookup->flags = F_FORWARD | F_IMMORTAL | F_CONFIG | F_HOSTS | F_CNAME;
lookup->name.namep = a->alias;
lookup->addr.cname.cache = cache;
lookup->addr.cname.uid = index;
cache_hash(lookup);
}
}
static int eatspace(FILE *f)
{
int c, nl = 0;
while (1)
{
if ((c = getc(f)) == '#')
while (c != '\n' && c != EOF)
c = getc(f);
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
if (c == EOF)
return 1;
if (!isspace(c))
{
ungetc(c, f);
return nl;
}
if (c == '\n')
nl = 1;
}
}
static int gettok(FILE *f, char *token)
{
int c, count = 0;
while (1)
{
if ((c = getc(f)) == EOF)
return (count == 0) ? EOF : 1;
if (isspace(c) || c == '#')
{
ungetc(c, f);
return eatspace(f);
}
if (count < (MAXDNAME - 1))
{
token[count++] = c;
token[count] = 0;
}
}
}
static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index, int cache_size)
static int read_hostsfile(char *filename, int index, int cache_size)
{
FILE *f = fopen(filename, "r");
char *line;
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 0;
unsigned short flags, saved_flags = 0;
unsigned short flags = 0, saved_flags = 0;
struct all_addr addr, saved_addr;
int atnl, addrlen = 0, addr_dup;
if (!f)
{
my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
return 0;
}
while ((line = fgets(buff, MAXDNAME, f)))
eatspace(f);
while ((atnl = gettok(f, token)) != EOF)
{
char *token = strtok(line, " \t\n\r");
int addrlen, addr_dup = 0;
addr_dup = 0;
lineno++;
if (!token || (*token == '#'))
continue;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
domain_suffix = daemon->domain_suffix;
}
#else
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#endif
else
{
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
while (atnl == 0)
atnl = gettok(f, token);
continue;
}
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else
{
saved_flags = flags;
saved_addr = addr;
}
addr_count++;
/* rehash every 1000 names. */
if ((name_count - cache_size) > 1000)
{
rehash(name_count);
cache_size = name_count;
}
while (atnl == 0)
{
struct crec *cache;
int fqdn, nomem;
char *canon;
if ((atnl = gettok(f, token)) == EOF)
break;
fqdn = !!strchr(token, '.');
if ((canon = canonicalise(token, &nomem)))
{
/* If set, add a version of the name with a default domain appended */
if ((daemon->options & OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
{
strcpy(cache->name.sname, canon);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
addr_dup = 1;
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
name_count++;
}
free(canon);
}
else if (!nomem)
my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
}
}
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else
{
saved_flags = flags;
saved_addr = addr;
}
addr_count++;
/* rehash every 1000 names. */
if ((name_count - cache_size) > 1000)
{
rehash(name_count);
cache_size = name_count;
}
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
{
struct crec *cache;
int fqdn = !!strchr(token, '.');
if (canonicalise(token))
{
/* If set, add a version of the name with a default domain appended */
if ((opts & OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
addr_dup = 1;
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
name_count++;
}
}
else
my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
}
}
fclose(f);
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
return name_count;
}
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
void cache_reload(void)
{
struct crec *cache, **up, *tmp;
int i, total_size = daemon->cachesize;
struct hostsfile *ah;
cache_inserted = cache_live_freed = 0;
@@ -800,22 +896,125 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
up = &cache->hash_next;
}
if ((opts & OPT_NO_HOSTS) && !addn_hosts)
if ((daemon->options & OPT_NO_HOSTS) && !daemon->addn_hosts)
{
if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache"));
return;
}
if (!(opts & OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0, total_size);
while (addn_hosts)
if (!(daemon->options & OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size);
for (i = 0, ah = daemon->addn_hosts; ah; ah = ah->next)
{
total_size = read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index, total_size);
addn_hosts = addn_hosts->next;
}
if (i <= ah->index)
i = ah->index + 1;
if (ah->flags & AH_DIR)
ah->flags |= AH_INACTIVE;
else
ah->flags &= ~AH_INACTIVE;
}
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
{
struct stat buf;
if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
{
DIR *dir_stream;
struct dirent *ent;
/* don't read this as a file */
ah->flags |= AH_INACTIVE;
if (!(dir_stream = opendir(ah->fname)))
my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
ah->fname, strerror(errno));
else
{
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
struct hostsfile *ah1;
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
/* see if we have an existing record.
dir is ah->fname
file is ent->d_name
path to match is ah1->fname */
for (ah1 = daemon->addn_hosts; ah1; ah1 = ah1->next)
{
if (lendir < strlen(ah1->fname) &&
strstr(ah1->fname, ah->fname) == ah1->fname &&
ah1->fname[lendir] == '/' &&
strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
{
ah1->flags &= ~AH_INACTIVE;
break;
}
}
/* make new record */
if (!ah1)
{
if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
continue;
if (!(path = whine_malloc(lendir + lenfile + 2)))
{
free(ah1);
continue;
}
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, ent->d_name);
ah1->fname = path;
ah1->index = i++;
ah1->flags = AH_DIR;
ah1->next = daemon->addn_hosts;
daemon->addn_hosts = ah1;
}
/* inactivate record if not regular file */
if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
ah1->flags |= AH_INACTIVE;
}
closedir(dir_stream);
}
}
}
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size);
}
char *get_domain(struct in_addr addr)
{
struct cond_domain *c;
for (c = daemon->cond_domain; c; c = c->next)
if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return c->domain;
return daemon->domain_suffix;
}
#ifdef HAVE_DHCP
void cache_unhash_dhcp(void)
{
struct crec *cache, **up;
@@ -836,31 +1035,32 @@ void cache_unhash_dhcp(void)
void cache_add_dhcp_entry(char *host_name,
struct in_addr *host_address, time_t ttd)
{
struct crec *crec = NULL;
struct crec *crec = NULL, *aliasc;
unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
int in_hosts = 0;
if (!host_name)
return;
struct cname *a;
while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME)))
{
/* check all addresses associated with name */
if (crec->flags & F_HOSTS)
{
if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
/* if in hosts, don't need DHCP record */
in_hosts = 1;
if (crec->flags & F_CNAME)
my_syslog(LOG_WARNING,
_("%s is a CNAME, not giving it to the DHCP lease of %s"),
host_name, inet_ntoa(*host_address));
else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
{
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
my_syslog(LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address),
record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
return;
}
else
/* if in hosts, don't need DHCP record */
in_hosts = 1;
record_source(crec->uid), daemon->namebuff);
}
}
else if (!(crec->flags & F_DHCP))
{
@@ -896,9 +1096,34 @@ void cache_add_dhcp_entry(char *host_name,
crec->ttd = ttd;
crec->addr.addr.addr.addr4 = *host_address;
crec->name.namep = host_name;
crec->uid = uid++;
cache_hash(crec);
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(host_name, a->target))
{
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(sizeof(struct crec));
if (aliasc)
{
aliasc->flags = F_FORWARD | F_CONFIG | F_DHCP | F_CNAME;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.cache = crec;
aliasc->addr.cname.uid = crec->uid;
cache_hash(aliasc);
}
}
}
}
#endif
void dump_cache(time_t now)
{
@@ -988,28 +1213,34 @@ void dump_cache(time_t now)
}
}
static char *record_source(struct hostsfile *addn_hosts, int index)
char *record_source(int index)
{
char *source = HOSTSFILE;
while (addn_hosts)
{
if (addn_hosts->index == index)
{
source = addn_hosts->fname;
break;
}
addn_hosts = addn_hosts->next;
}
struct hostsfile *ah;
return source;
if (index == 0)
return HOSTSFILE;
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
return "<unknown>";
}
void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index)
void querystr(char *str, unsigned short type)
{
unsigned int i;
sprintf(str, "query[type=%d]", type);
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(str,"query[%s]", typestr[i].name);
}
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg)
{
char *source, *dest = addrbuff;
char *verb = "is";
char types[20];
if (!(daemon->options & OPT_LOG))
return;
@@ -1053,25 +1284,19 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
}
else if (flags & F_CNAME)
{
/* nasty abuse of IPV4 and IPV6 flags */
if (flags & F_IPV4)
dest = "<MX>";
else if (flags & F_IPV6)
dest = "<SRV>";
else if (flags & F_NXDOMAIN)
dest = "<TXT>";
else if (flags & F_BIGNAME)
dest = "<PTR>";
/* nasty abuse of NXDOMAIN and CNAME flags */
if (flags & F_NXDOMAIN)
dest = arg;
else
dest = "<CNAME>";
}
if (flags & F_DHCP)
if (flags & F_CONFIG)
source = "config";
else if (flags & F_DHCP)
source = "DHCP";
else if (flags & F_HOSTS)
source = record_source(addn_hosts, index);
else if (flags & F_CONFIG)
source = "config";
source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SERVER)
@@ -1081,16 +1306,7 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
}
else if (flags & F_QUERY)
{
unsigned int i;
if (type != 0)
{
sprintf(types, "query[type=%d]", type);
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(types,"query[%s]", typestr[i].name);
}
source = types;
source = arg;
verb = "from";
}
else

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,20 +10,23 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define VERSION "2.41"
#define VERSION "2.55"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 1280 /* default max EDNS.0 UDP packet from RFC2671 */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 10 seconds */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
#define MAXLEASES 150 /* maximum number of DHCP leases */
#define MAXLEASES 1000 /* maximum number of DHCP leases */
#define PING_WAIT 3 /* wait for ping address-in-use test */
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
@@ -37,26 +40,38 @@
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__)
# define LEASEFILE "/var/db/dnsmasq.leases"
#elif defined(__sun__)
# define LEASEFILE "/var/cache/dnsmasq.leases"
#else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
#ifndef LEASEFILE
# if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
# define LEASEFILE "/var/db/dnsmasq.leases"
# elif defined(__sun__) || defined (__sun)
# define LEASEFILE "/var/cache/dnsmasq.leases"
# else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
# endif
#endif
#if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
#else
# define CONFFILE "/etc/dnsmasq.conf"
#ifndef CONFFILE
# if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
# else
# define CONFFILE "/etc/dnsmasq.conf"
# endif
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
@@ -85,13 +100,6 @@
#endif
/* Get linux C library versions. */
#if defined(__linux__) && !defined(__UCLIBC__) && !defined(__uClinux__)
/*# include <libio.h> */
# include <features.h>
#endif
/* Follows system specific switches. If you run on a
new system, you may want to edit these.
May replace this with Autoconf one day.
@@ -101,9 +109,6 @@ HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
HAVE_SOLARIS_PRIVS
define for Solaris > 10 which can split privileges.
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
@@ -118,13 +123,15 @@ HAVE_BROKEN_RTC
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
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_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_DHCP
define this to get dnsmasq's DHCP server.
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
@@ -132,19 +139,6 @@ HAVE_ARC4RANDOM
define this if you have arc4random() to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_RANDOM
define this if you have the 4.2BSD random() function (and its
associated srandom() function), which is at least as good as (if not
better than) the rand() function.
HAVE_DEV_RANDOM
define this if you have the /dev/random device, which gives truly
random numbers but may run out of random numbers.
HAVE_DEV_URANDOM
define this if you have the /dev/urandom device, which gives
semi-random numbers when it runs out of truly random numbers.
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
@@ -157,9 +151,6 @@ NOTES:
For Linux you should define
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
HAVE_RANDOM
HAVE_DEV_RANDOM
HAVE_DEV_URANDOM
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
@@ -167,32 +158,37 @@ NOTES:
For *BSD systems you should define
HAVE_BSD_NETWORK
HAVE_SOCKADDR_SA_LEN
HAVE_RANDOM
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
HAVE_DEV_RANDOM - FreeBSD and NetBSD
(OpenBSD with hardware random number generator)
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
*/
/* platform independent options- uncomment to enable */
#define HAVE_DHCP
#define HAVE_TFTP
#define HAVE_SCRIPT
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_ISC_READER */
/* #define HAVE_DBUS */
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
#endif
/* Allow TFTP to be disabled with COPT=-DNO_TFTP */
/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
#ifdef NO_DHCP
#undef HAVE_DHCP
#endif
/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
#ifdef NO_SCRIPT
#undef HAVE_SCRIPT
#endif
/* platform dependent options. */
/* Must preceed __linux__ since uClinux defines __linux__ too. */
@@ -200,9 +196,6 @@ NOTES:
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
/* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new
@@ -215,13 +208,8 @@ NOTES:
#if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
# define HAVE_GETOPT_LONG
#else
# undef HAVE_GETOPT_LONG
#endif
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
# define NO_FORK
@@ -237,80 +225,43 @@ NOTES:
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
/* 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) && \
defined(__GLIBC_MINOR__) && (__GLIBC_MINOR__ < 2)
typedef unsigned long in_addr_t;
# define HAVE_BROKEN_SOCKADDR_IN6
#endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#elif defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__DragonFly__) || \
defined(__FreeBSD_kernel__)
#define HAVE_BSD_NETWORK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
#else
# undef HAVE_GETOPT_LONG
#endif
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#if !defined(__FreeBSD_kernel__)
# define HAVE_ARC4RANDOM
#endif
#define HAVE_SOCKADDR_SA_LEN
#elif defined(__APPLE__)
#define HAVE_BSD_NETWORK
#undef HAVE_GETOPT_LONG
#define HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_SOCKADDR_SA_LEN
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* This is not defined in Mac OS X arpa/nameserv.h */
#define IN6ADDRSZ 16
#elif defined(__NetBSD__)
#define HAVE_BSD_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#define HAVE_SOCKADDR_SA_LEN
#elif defined(__sun) || defined(__sun__)
#define HAVE_SOLARIS_NETWORK
/* only Solaris 10 does split privs. */
#if (SUNOS_VER >= 10)
# define HAVE_SOLARIS_PRIVS
# define HAVE_GETOPT_LONG
#endif
/* some CMSG stuff missing on early solaris */
#ifndef OSSH_ALIGNBYTES
# define OSSH_ALIGNBYTES (sizeof(int) - 1)
#endif
#ifndef __CMSG_ALIGN
# define __CMSG_ALIGN(p) (((u_int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES)
#endif
#ifndef CMSG_LEN
# define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
#endif
#ifndef CMSG_SPACE
# define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len))
#endif
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#undef HAVE_DEV_URANDOM
#undef HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define _XPG4_2
#define __EXTENSIONS__
#define ETHER_ADDR_LEN 6
#define ETHER_ADDR_LEN 6
#endif
/* Decide if we're going to support IPv6 */
@@ -334,3 +285,8 @@ typedef unsigned long in_addr_t;
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif
/* Can't do scripts without fork */
#ifdef NOFORK
# undef HAVE_SCRIPT
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,17 +10,52 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DBUS
#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus.h>
const char* introspection_xml =
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node name=\"" DNSMASQ_PATH "\">\n"
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
" <method name=\"Introspect\">\n"
" <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"" DNSMASQ_SERVICE "\">\n"
" <method name=\"ClearCache\">\n"
" </method>\n"
" <method name=\"GetVersion\">\n"
" <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
" </method>\n"
" <method name=\"SetServers\">\n"
" <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
" </method>\n"
" <signal name=\"DhcpLeaseAdded\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"DhcpLeaseDeleted\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
" <signal name=\"DhcpLeaseUpdated\">\n"
" <arg name=\"ipaddr\" type=\"s\"/>\n"
" <arg name=\"hwaddr\" type=\"s\"/>\n"
" <arg name=\"hostname\" type=\"s\"/>\n"
" </signal>\n"
" </interface>\n"
"</node>\n";
struct watch {
DBusWatch *watch;
struct watch *next;
@@ -118,7 +153,7 @@ static void dbus_read_servers(DBusMessage *message)
{
memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(stuct sockaddr_in6);
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
@@ -167,8 +202,11 @@ static void dbus_read_servers(DBusMessage *message)
if (!serv && (serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
memset(serv, 0, sizeof(struct server));
if (domain)
serv->domain = whine_malloc(strlen(domain)+1);
if (domain && !serv->domain)
{
free(serv);
@@ -179,7 +217,6 @@ static void dbus_read_servers(DBusMessage *message)
serv->next = daemon->servers;
daemon->servers = serv;
serv->flags = SERV_FROM_DBUS;
serv->sfd = NULL;
if (domain)
{
strcpy(serv->domain, domain);
@@ -227,7 +264,15 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
char *method = (char *)dbus_message_get_member(message);
if (strcmp(method, "GetVersion") == 0)
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
DBusMessage *reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
}
else if (strcmp(method, "GetVersion") == 0)
{
char *v = VERSION;
DBusMessage *reply = dbus_message_new_method_return(message);
@@ -281,7 +326,10 @@ char *dbus_init(void)
daemon->dbus = connection;
if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
dbus_connection_send(connection, message, NULL);
{
dbus_connection_send(connection, message, NULL);
dbus_message_unref(message);
}
return NULL;
}
@@ -296,11 +344,7 @@ void set_dbus_listeners(int *maxfdp,
if (dbus_watch_get_enabled(w->watch))
{
unsigned int flags = dbus_watch_get_flags(w->watch);
#if (DBUS_MINOR > 0)
int fd = dbus_watch_get_unix_fd(w->watch);
#else
int fd = dbus_watch_get_fd(w->watch);
#endif
bump_maxfd(fd, maxfdp);
@@ -323,11 +367,7 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
if (dbus_watch_get_enabled(w->watch))
{
unsigned int flags = 0;
#if (DBUS_MINOR > 0)
int fd = dbus_watch_get_unix_fd(w->watch);
#else
int fd = dbus_watch_get_fd(w->watch);
#endif
if (FD_ISSET(fd, rset))
flags |= DBUS_WATCH_READABLE;
@@ -350,4 +390,49 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
}
}
#ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
{
DBusConnection *connection = (DBusConnection *)daemon->dbus;
DBusMessage* message = NULL;
DBusMessageIter args;
char *action_str, *addr, *mac = daemon->namebuff;
unsigned char *p;
int i;
if (!connection)
return;
if (!hostname)
hostname = "";
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
if (action == ACTION_DEL)
action_str = "DhcpLeaseDeleted";
else if (action == ACTION_ADD)
action_str = "DhcpLeaseAdded";
else if (action == ACTION_OLD)
action_str = "DhcpLeaseUpdated";
else
return;
addr = inet_ntoa(lease->addr);
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
return;
dbus_message_iter_init_append(message, &args);
if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
dbus_connection_send(connection, message, NULL);
dbus_message_unref(message);
}
#endif
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,12 +10,14 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
struct iface_param {
struct in_addr relay, primary;
struct dhcp_context *current;
@@ -25,7 +27,7 @@ struct iface_param {
static int complete_context(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
void dhcp_init(void)
static int make_fd(int port)
{
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in saddr;
@@ -35,7 +37,7 @@ void dhcp_init(void)
#endif
if (fd == -1)
die (_("cannot create DHCP socket : %s"), NULL, EC_BADNET);
die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
if (!fix_fd(fd) ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
@@ -43,7 +45,7 @@ void dhcp_init(void)
#endif
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#elif defined(IP_RECVIF)
#else
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
#endif
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
@@ -51,12 +53,7 @@ void dhcp_init(void)
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's OK if they serve different networks.
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD.
OpenBSD <= 4.0 screws up IP_RECVIF when SO_REUSEPORT is set, but
OpenBSD <= 3.9 doesn't have IP_RECVIF anyway, so we just have to elide
this for OpenBSD 4.0, if you want more than one instance on oBSD4.0, tough. */
#ifndef OpenBSD4_0
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
if (daemon->options & OPT_NOWILD)
{
#ifdef SO_REUSEPORT
@@ -67,11 +64,10 @@ void dhcp_init(void)
if (rc == -1)
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
}
#endif
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = INADDR_ANY;
#ifdef HAVE_SOCKADDR_SA_LEN
saddr.sin_len = sizeof(struct sockaddr_in);
@@ -80,7 +76,20 @@ void dhcp_init(void)
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
daemon->dhcpfd = fd;
return fd;
}
void dhcp_init(void)
{
#if defined(HAVE_BSD_NETWORK)
int oneopt = 1;
#endif
daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
if (daemon->enable_pxe)
daemon->pxefd = make_fd(PXE_PORT);
else
daemon->pxefd = -1;
#if defined(HAVE_BSD_NETWORK)
/* When we're not using capabilities, we need to do this here before
@@ -103,8 +112,9 @@ void dhcp_init(void)
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
}
void dhcp_packet(time_t now)
void dhcp_packet(time_t now, int pxe_fd)
{
int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
struct dhcp_packet *mess;
struct dhcp_context *context;
struct iname *tmp;
@@ -117,14 +127,17 @@ void dhcp_packet(time_t now)
int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm;
#ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req;
#endif
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(HAVE_SOLARIS_NETWORK)
char control[CMSG_SPACE(sizeof(unsigned int))];
#elif defined(IP_RECVIF)
#elif defined(HAVE_BSD_NETWORK)
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
@@ -136,13 +149,30 @@ void dhcp_packet(time_t now)
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
do
while (1)
{
msg.msg_flags = 0;
while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
while ((sz = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
if (sz == -1)
return;
if (!(msg.msg_flags & MSG_TRUNC))
break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t)sz == daemon->dhcp_packet.iov_len)
{
if (!expand_buf(&daemon->dhcp_packet, sz + 100))
return;
}
else
{
expand_buf(&daemon->dhcp_packet, sz);
break;
}
}
while (sz != -1 && (msg.msg_flags & MSG_TRUNC) &&
expand_buf(&daemon->dhcp_packet, daemon->dhcp_packet.iov_len + 100));
/* expand_buf may have moved buffer */
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
@@ -152,9 +182,9 @@ void dhcp_packet(time_t now)
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
while ((sz = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
if (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
return;
#if defined (HAVE_LINUX_NETWORK)
@@ -162,45 +192,57 @@ void dhcp_packet(time_t now)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.p->ipi_ifindex;
if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
if (!(ifr.ifr_ifindex = iface_index) ||
ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
return;
#elif defined(IP_RECVIF)
#elif defined(HAVE_BSD_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
iface_index = *((unsigned int *)CMSG_DATA(cmptr));
#else
iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
#endif
if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
return;
{
union {
unsigned char *c;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.s->sdl_index;
}
#elif defined(HAVE_SOLARIS_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
{
union {
unsigned char *c;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = *(p.i);
}
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
return;
#ifdef HAVE_LINUX_NETWORK
/* ARP fiddling uses original interface even if we pretend to use a different one. */
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST))
unicast_dest = 1;
#endif
#else
/* fallback for systems without IP_RECVIF - allow only one interface
and assume packets arrive from it - yuk. */
{
struct iname *name;
for (name = daemon->if_names; name->isloop; name = name->next);
strcpy(ifr.ifr_name, name->name);
iface_index = if_nametoindex(name->name);
}
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
{
@@ -208,24 +250,20 @@ void dhcp_packet(time_t now)
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (!iface_check(AF_INET, (struct all_addr *)addrp, &ifr, &iface_index))
if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* interface may have been changed by alias in iface_check */
if (!addrp)
{
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1)
{
my_syslog(LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
/* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
break;
if (!context)
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
@@ -236,11 +274,32 @@ void dhcp_packet(time_t now)
parm.current = NULL;
parm.ind = iface_index;
/* interface may have been changed by alias in iface_check, make sure it gets priority in case
there is more than one address on the interface in the same subnet */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
{
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
{
struct in_addr netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
{
struct in_addr broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
}
}
}
if (!iface_enumerate(&parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform);
now, unicast_dest, &is_inform, pxe_fd);
lease_update_file(now);
lease_update_dns();
@@ -261,10 +320,15 @@ void dhcp_packet(time_t now)
dest.sin_len = sizeof(struct sockaddr_in);
#endif
if (mess->giaddr.s_addr)
if (pxe_fd)
{
if (mess->ciaddr.s_addr != 0)
dest.sin_addr = mess->ciaddr;
}
else if (mess->giaddr.s_addr)
{
/* Send to BOOTP relay */
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_port = htons(daemon->dhcp_server_port);
dest.sin_addr = mess->giaddr;
}
else if (mess->ciaddr.s_addr)
@@ -276,7 +340,7 @@ void dhcp_packet(time_t now)
if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
{
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_port = htons(daemon->dhcp_client_port);
dest.sin_addr = mess->ciaddr;
}
}
@@ -296,30 +360,29 @@ void dhcp_packet(time_t now)
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_port = htons(daemon->dhcp_client_port);
}
else
{
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
struct arpreq req;
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(DHCP_CLIENT_PORT);
*((struct sockaddr_in *)&req.arp_pa) = dest;
req.arp_ha.sa_family = mess->htype;
memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
strncpy(req.arp_dev, ifr.ifr_name, 16);
req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &req);
dest.sin_port = htons(daemon->dhcp_client_port);
memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
arp_req.arp_ha.sa_family = mess->htype;
memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
/* interface name already copied in */
arp_req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
}
#elif defined(HAVE_SOLARIS_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
{
/* broadcast to 255.255.255.255 (or mac address invalid) */
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_port = htons(daemon->dhcp_client_port);
/* note that we don't specify the interface here: that's done by the
IP_XMIT_IF sockopt lower down. */
IP_BOUND_IF sockopt lower down. */
}
else
{
@@ -329,7 +392,7 @@ void dhcp_packet(time_t now)
mysteriously. Bah. Fall back to broadcast for other net types. */
struct arpreq req;
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_port = htons(daemon->dhcp_client_port);
*((struct sockaddr_in *)&req.arp_pa) = dest;
req.arp_ha.sa_family = AF_UNSPEC;
memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
@@ -345,10 +408,10 @@ void dhcp_packet(time_t now)
#endif
#ifdef HAVE_SOLARIS_NETWORK
setsockopt(daemon->dhcpfd, IPPROTO_IP, IP_XMIT_IF, &iface_index, sizeof(iface_index));
setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
#endif
while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
while(sendmsg(fd, &msg, 0) == -1 && retry_send());
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
@@ -379,7 +442,7 @@ static int complete_context(struct in_addr local, int if_index,
{
strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
my_syslog(LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
}
context->netmask = netmask;
@@ -469,13 +532,15 @@ struct dhcp_context *narrow_context(struct dhcp_context *context,
if (!(tmp = address_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask))
if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask))
break;
}
@@ -508,7 +573,8 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotnee
for (; check; check = check->next)
{
if (check->net[0] != '#')
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
@@ -524,6 +590,22 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotnee
return 1;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now)
@@ -537,9 +619,10 @@ int address_allocate(struct dhcp_context *context,
int i, pass;
unsigned int j;
/* hash hwaddr */
/* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
dispersal even with similarly-valued "strings". */
for (j = 0, i = 0; i < hw_len; i++)
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
j += hwaddr[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
@@ -560,9 +643,16 @@ int address_allocate(struct dhcp_context *context,
if (addr.s_addr == d->router.s_addr)
break;
/* Addresses which end in .255 and .0 are broken in Windows even when using
supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
then 192.168.0.255 is a valid IP address, but not for Windows as it's
in the class C range. See KB281579. We therefore don't allocate these
addresses to avoid hard-to-diagnose problems. Thanks Bill. */
if (!d &&
!lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
!config_find_by_address(daemon->dhcp_conf, addr) &&
(!IN_CLASSC(ntohl(addr.s_addr)) ||
((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
{
struct ping_result *r, *victim = NULL;
int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
@@ -635,6 +725,19 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
return 0;
}
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
{
struct hwaddr_config *conf_addr;
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
if (conf_addr->wildcard_mask == 0 &&
conf_addr->hwaddr_len == len &&
(conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
return 1;
return 0;
}
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
@@ -642,8 +745,10 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
unsigned char *hwaddr, int hw_len,
int hw_type, char *hostname)
{
struct dhcp_config *config;
int count, new;
struct dhcp_config *config, *candidate;
struct hwaddr_config *conf_addr;
if (clid)
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_CLID)
@@ -663,11 +768,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
config->hwaddr_len == hw_len &&
(config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
memcmp(config->hwaddr, hwaddr, hw_len) == 0 &&
if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
is_addr_in_context(context, config))
return config;
@@ -677,17 +778,21 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
hostname_isequal(config->hostname, hostname) &&
is_addr_in_context(context, config))
return config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask != 0 &&
config->hwaddr_len == hw_len &&
(config->hwaddr_type == hw_type || config->hwaddr_type == 0) &&
is_addr_in_context(context, config) &&
memcmp_masked(config->hwaddr, hwaddr, hw_len, config->wildcard_mask))
return config;
return NULL;
/* use match with fewest wildcast octets */
for (candidate = NULL, count = 0, config = configs; config; config = config->next)
if (is_addr_in_context(context, config))
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
if (conf_addr->wildcard_mask != 0 &&
conf_addr->hwaddr_len == hw_len &&
(conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
(new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
{
count = new;
candidate = config;
}
return candidate;
}
void dhcp_read_ethers(void)
@@ -706,7 +811,7 @@ void dhcp_read_ethers(void)
if (!f)
{
my_syslog(LOG_ERR, _("failed to read %s:%s"), ETHERSFILE, strerror(errno));
my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
return;
}
@@ -720,6 +825,7 @@ void dhcp_read_ethers(void)
/* cannot have a clid */
if (config->flags & CONFIG_NAME)
free(config->hostname);
free(config->hwaddr);
free(config);
}
else
@@ -728,12 +834,14 @@ void dhcp_read_ethers(void)
while (fgets(buff, MAXDNAME, f))
{
char *host = NULL;
lineno++;
while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
buff[strlen(buff)-1] = 0;
if ((*buff == '#') || (*buff == '+'))
if ((*buff == '#') || (*buff == '+') || (*buff == 0))
continue;
for (ip = buff; *ip && !isspace((int)*ip); ip++);
@@ -741,7 +849,7 @@ void dhcp_read_ethers(void)
*ip = 0;
if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
{
my_syslog(LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
continue;
}
@@ -754,7 +862,7 @@ void dhcp_read_ethers(void)
{
if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
{
my_syslog(LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);
continue;
}
@@ -766,35 +874,50 @@ void dhcp_read_ethers(void)
}
else
{
if (!canonicalise(ip) || strip_hostname(ip))
int nomem;
if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
{
my_syslog(LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
if (!nomem)
my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno);
free(host);
continue;
}
flags = CONFIG_NAME;
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
break;
}
if (config && (config->flags & CONFIG_FROM_ETHERS))
{
my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno);
continue;
}
if (!config)
{
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
config->hwaddr_len == ETHER_ADDR_LEN &&
(config->hwaddr_type == ARPHRD_ETHER || config->hwaddr_type == 0) &&
memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
break;
{
struct hwaddr_config *conf_addr = config->hwaddr;
if (conf_addr &&
conf_addr->next == NULL &&
conf_addr->wildcard_mask == 0 &&
conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
(conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
break;
}
if (!config)
{
if (!(config = whine_malloc(sizeof(struct dhcp_config))))
continue;
config->flags = CONFIG_FROM_ETHERS;
config->wildcard_mask = 0;
config->hwaddr = NULL;
config->domain = NULL;
config->netid = NULL;
config->next = daemon->dhcp_conf;
daemon->dhcp_conf = config;
}
@@ -803,26 +926,34 @@ void dhcp_read_ethers(void)
if (flags & CONFIG_NAME)
{
if ((config->hostname = whine_malloc(strlen(ip)+1)))
strcpy(config->hostname, ip);
else
config->flags &= ~CONFIG_NAME;
config->hostname = host;
host = NULL;
}
if (flags & CONFIG_ADDR)
config->addr = addr;
}
config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
config->hwaddr_len = ETHER_ADDR_LEN;
config->hwaddr_type = ARPHRD_ETHER;
config->flags |= CONFIG_NOCLID;
if (!config->hwaddr)
config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
if (config->hwaddr)
{
memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
config->hwaddr->hwaddr_type = ARPHRD_ETHER;
config->hwaddr->wildcard_mask = 0;
config->hwaddr->next = NULL;
}
count++;
free(host);
}
fclose(f);
my_syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
}
void check_dhcp_hosts(int fatal)
@@ -847,20 +978,14 @@ void check_dhcp_hosts(int fatal)
die(_("duplicate IP address %s in dhcp-config directive."),
inet_ntoa(cp->addr), EC_BADCONF);
else
my_syslog(LOG_ERR, _("duplicate IP address %s in %s."),
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
/* split off domain part */
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
{
if (fatal)
die(_("illegal domain %s in dhcp-config directive."), domain, EC_BADCONF);
else
my_syslog(LOG_ERR, _("illegal domain %s in %s."), domain, daemon->dhcp_hosts_file);
free(configs->hostname);
configs->flags &= ~CONFIG_NAME;
}
configs->domain = domain;
}
}
}
@@ -896,12 +1021,12 @@ void dhcp_update_configs(struct dhcp_config *configs)
crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
if (!crec)
continue; /* should be never */
my_syslog(LOG_WARNING, _("%s has more then one address in hostsfile, using %s for DHCP"),
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
}
if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
my_syslog(LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
else
{
@@ -913,29 +1038,44 @@ void dhcp_update_configs(struct dhcp_config *configs)
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and
it gets stripped. */
it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
so check here that the domain name is legal as a hostname.
NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
char *host_from_dns(struct in_addr addr)
{
struct crec *lookup;
char *hostname = NULL;
if (daemon->port == 0)
return NULL; /* DNS disabled. */
lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
if (lookup && (lookup->flags & F_HOSTS))
{
hostname = daemon->dhcp_buff;
strncpy(hostname, cache_get_name(lookup), 256);
hostname[255] = 0;
if (strip_hostname(hostname))
hostname = NULL;
char *dot, *hostname = cache_get_name(lookup);
dot = strchr(hostname, '.');
if (dot && strlen(dot+1) != 0)
{
char *d2 = get_domain(addr);
if (!d2 || !hostname_isequal(dot+1, d2))
return NULL; /* wrong domain */
}
if (!legal_hostname(hostname))
return NULL;
strncpy(daemon->dhcp_buff, hostname, 256);
daemon->dhcp_buff[255] = 0;
strip_hostname(daemon->dhcp_buff);
return daemon->dhcp_buff;
}
return hostname;
return NULL;
}
/* return illegal domain or NULL if OK */
/* return domain or NULL if none. */
char *strip_hostname(char *hostname)
{
char *dot = strchr(hostname, '.');
@@ -944,9 +1084,11 @@ char *strip_hostname(char *hostname)
return NULL;
*dot = 0; /* truncate */
if (*(dot+1) && (!daemon->domain_suffix || !hostname_isequal(dot+1, daemon->domain_suffix)))
if (strlen(dot+1) != 0)
return dot+1;
return NULL;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -33,10 +33,6 @@ static char *compile_opts =
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_ISC_READER
"no-"
#endif
"ISC-leasefile "
#ifndef HAVE_DBUS
"no-"
#endif
@@ -45,11 +41,20 @@ static char *compile_opts =
"no-"
#endif
"I18N "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
"no-scripts "
#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP";
static volatile pid_t pid = 0;
static volatile int pipewrite;
@@ -57,18 +62,28 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void poll_resolv(void);
static void fatal_event(struct event_desc *ev);
int main (int argc, char **argv)
{
int bind_fallback = 0;
int bad_capabilities = 0;
time_t now, last = 0;
time_t now;
struct sigaction sigact;
struct iname *if_tmp;
int piperead, pipefd[2];
struct passwd *ent_pw;
int piperead, pipefd[2], err_pipe[2];
struct passwd *ent_pw = NULL;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
uid_t script_uid = 0;
gid_t script_gid = 0;
#endif
struct group *gp = NULL;
long i, max_fd = sysconf(_SC_OPEN_MAX);
char *baduser = NULL;
int log_err;
#if defined(HAVE_LINUX_NETWORK)
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
#endif
#ifdef LOCALEDIR
setlocale(LC_ALL, "");
@@ -99,15 +114,13 @@ int main (int argc, char **argv)
daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ?
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
#ifdef HAVE_DHCP
if (!daemon->lease_file)
{
if (daemon->dhcp)
daemon->lease_file = LEASEFILE;
}
#ifndef HAVE_ISC_READER
else if (!daemon->dhcp)
die(_("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h"), NULL, EC_BADCONF);
#endif
/* Close any file descriptors we inherited apart from std{in|out|err} */
@@ -128,7 +141,7 @@ int main (int argc, char **argv)
#endif
#ifndef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif
@@ -137,25 +150,20 @@ int main (int argc, char **argv)
die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
#endif
rand_init();
now = dnsmasq_time();
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
int c;
struct iname *tmp;
for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
if (!tmp->isloop)
c++;
if (c != 1)
die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL, EC_BADCONF);
#endif
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. */
lease_init(now);
dhcp_init();
}
#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
@@ -175,13 +183,13 @@ int main (int argc, char **argv)
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
}
else if ((daemon->port != 0 || (daemon->options & OPT_TFTP)) &&
else if ((daemon->port != 0 || daemon->tftp_interfaces || daemon->tftp_unlimited) &&
!(daemon->listeners = create_wildcard_listeners()))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
if (daemon->port != 0)
cache_init();
if (daemon->options & OPT_DBUS)
#ifdef HAVE_DBUS
{
@@ -197,108 +205,190 @@ int main (int argc, char **argv)
if (daemon->port != 0)
pre_allocate_sfds();
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
/* Note getpwnam returns static storage */
if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser)
{
if ((ent_pw = getpwnam(daemon->scriptuser)))
{
script_uid = ent_pw->pw_uid;
script_gid = ent_pw->pw_gid;
}
else
baduser = daemon->scriptuser;
}
#endif
if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
baduser = daemon->username;
else if (daemon->groupname && !(gp = getgrnam(daemon->groupname)))
baduser = daemon->groupname;
if (baduser)
die(_("unknown user or group: %s"), baduser, EC_BADCONF);
/* implement group defaults, "dip" if available, or group associated with uid */
if (!daemon->group_set && !gp)
{
if (!(gp = getgrnam(CHGRP)) && ent_pw)
gp = getgrgid(ent_pw->pw_gid);
/* for error message */
if (gp)
daemon->groupname = gp->gr_name;
}
#if defined(HAVE_LINUX_NETWORK)
/* determine capability API version here, while we can still
call safe_malloc */
if (ent_pw && ent_pw->pw_uid != 0)
{
int capsize = 1; /* for header version 1 */
hdr = safe_malloc(sizeof(*hdr));
/* find version supported by kernel */
memset(hdr, 0, sizeof(*hdr));
capget(hdr, NULL);
if (hdr->version != LINUX_CAPABILITY_VERSION_1)
{
/* if unknown version, use largest supported version (3) */
if (hdr->version != LINUX_CAPABILITY_VERSION_2)
hdr->version = LINUX_CAPABILITY_VERSION_3;
capsize = 2;
}
data = safe_malloc(sizeof(*data) * capsize);
memset(data, 0, sizeof(*data) * capsize);
}
#endif
/* Use a pipe to carry signals and other events back to the event loop
in a race-free manner */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[0]) || !fix_fd(pipefd[1]))
die(_("cannot create pipe: %s"), NULL, EC_MISC);
in a race-free manner and another to carry errors to daemon-invoking process */
safe_pipe(pipefd, 1);
piperead = pipefd[0];
pipewrite = pipefd[1];
/* prime the pipe to load stuff first time. */
send_event(pipewrite, EVENT_RELOAD, 0);
err_pipe[1] = -1;
if (!(daemon->options & OPT_DEBUG))
{
FILE *pidfile;
int nullfd;
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
if (chdir("/") != 0)
die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
#ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK))
{
pid_t pid;
if ((pid = fork()) == -1 )
die(_("cannot fork into background: %s"), NULL, EC_MISC);
/* pipe to carry errors back to original process.
When startup is complete we close this and the process terminates. */
safe_pipe(err_pipe, 0);
if ((pid = fork()) == -1)
/* fd == -1 since we've not forked, never returns. */
send_event(-1, EVENT_FORK_ERR, errno);
if (pid != 0)
_exit(EC_GOOD);
{
struct event_desc ev;
/* close our copy of write-end */
close(err_pipe[1]);
/* check for errors after the fork */
if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1))
fatal_event(&ev);
_exit(EC_GOOD);
}
close(err_pipe[0]);
/* NO calls to die() from here on. */
setsid();
pid = fork();
if (pid != 0 && pid != -1)
if ((pid = fork()) == -1)
send_event(err_pipe[1], EVENT_FORK_ERR, errno);
if (pid != 0)
_exit(0);
}
#endif
if (chdir("/") != 0)
die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
/* write pidfile _after_ forking ! */
if (daemon->runfile && (pidfile = fopen(daemon->runfile, "w")))
{
fprintf(pidfile, "%d\n", (int) getpid());
fclose(pidfile);
if (daemon->runfile)
{
FILE *pidfile;
/* only complain if started as root */
if ((pidfile = fopen(daemon->runfile, "w")))
{
fprintf(pidfile, "%d\n", (int) getpid());
fclose(pidfile);
}
else if (getuid() == 0)
{
send_event(err_pipe[1], EVENT_PIDFILE, errno);
_exit(0);
}
}
/* open stdout etc to /dev/null */
nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
#ifndef NO_FORK
daemon->helperfd = create_helper(pipewrite, max_fd);
#endif
log_err = log_start(ent_pw, err_pipe[1]);
if (!(daemon->options & OPT_DEBUG))
{
/* open stdout etc to /dev/null */
int nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
if (daemon->dhcp && daemon->lease_change_command)
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
/* before here, we should only call die(), after here, only call syslog() */
log_start(ent_pw);
if (!(daemon->options & OPT_DEBUG))
if (!(daemon->options & OPT_DEBUG) && getuid() == 0)
{
/* UID changing, etc */
if (daemon->groupname || ent_pw)
{
gid_t dummy;
struct group *gp;
/* change group for /etc/ppp/resolv.conf otherwise get the group for "nobody" */
if ((daemon->groupname && (gp = getgrnam(daemon->groupname))) ||
(ent_pw && (gp = getgrgid(ent_pw->pw_gid))))
{
/* remove all supplimentary groups */
setgroups(0, &dummy);
setgid(gp->gr_gid);
}
}
int bad_capabilities = 0;
gid_t dummy;
/* remove all supplimentary groups */
if (gp &&
(setgroups(0, &dummy) == -1 ||
setgid(gp->gr_gid) == -1))
{
send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
_exit(0);
}
if (ent_pw && ent_pw->pw_uid != 0)
{
#if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
cap_user_header_t hdr = safe_malloc(sizeof(*hdr));
cap_user_data_t data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
bad_capabilities = errno;
#elif defined(HAVE_SOLARIS_PRIVS)
#elif defined(HAVE_SOLARIS_NETWORK)
/* http://developers.sun.com/solaris/articles/program_privileges.html */
priv_set_t *priv_set;
@@ -318,25 +408,34 @@ int main (int argc, char **argv)
if (priv_set)
priv_freeset(priv_set);
#elif defined(HAVE_SOLARIS_NETWORK)
bad_capabilities = ENOTSUP;
#endif
if (bad_capabilities == 0)
if (bad_capabilities != 0)
{
/* finally drop root */
setuid(ent_pw->pw_uid);
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
capset(hdr, data);
#endif
send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
_exit(0);
}
/* finally drop root */
if (setuid(ent_pw->pw_uid) == -1)
{
send_event(err_pipe[1], EVENT_USER_ERR, errno);
_exit(0);
}
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
if (capset(hdr, data) == -1)
{
send_event(err_pipe[1], EVENT_CAP_ERR, errno);
_exit(0);
}
#endif
}
}
@@ -364,6 +463,10 @@ int main (int argc, char **argv)
}
#endif
if (log_err != 0)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"),
daemon->log_file, strerror(log_err));
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
@@ -384,6 +487,7 @@ int main (int argc, char **argv)
if (daemon->max_logs != 0)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
@@ -392,23 +496,26 @@ int main (int argc, char **argv)
{
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
my_syslog(LOG_INFO,
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, lease time %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
}
#endif
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
my_syslog(LOG_INFO, "TFTP %s%s %s",
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
daemon->options & OPT_TFTP_SECURE ? _("secure mode") : "");
@@ -436,20 +543,16 @@ int main (int argc, char **argv)
if (daemon->tftp_max > max_fd)
{
daemon->tftp_max = max_fd;
my_syslog(LOG_WARNING,
my_syslog(MS_TFTP | LOG_WARNING,
_("restricting maximum simultaneous TFTP transfers to %d"),
daemon->tftp_max);
}
}
#endif
if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
{
if (bad_capabilities)
my_syslog(LOG_WARNING, _("warning: setting capabilities failed: %s"), strerror(bad_capabilities));
my_syslog(LOG_WARNING, _("running as root"));
}
/* finished start-up - release original process */
if (err_pipe[1] != -1)
close(err_pipe[1]);
if (daemon->port != 0)
check_servers();
@@ -488,11 +591,18 @@ int main (int argc, char **argv)
set_dbus_listeners(&maxfd, &rset, &wset, &eset);
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
if (daemon->pxefd != -1)
{
FD_SET(daemon->pxefd, &rset);
bump_maxfd(daemon->pxefd, &maxfd);
}
}
#endif
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
@@ -502,7 +612,8 @@ int main (int argc, char **argv)
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
#ifndef NO_FORK
#ifdef HAVE_DHCP
# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
if (!helper_buf_empty())
@@ -510,11 +621,12 @@ int main (int argc, char **argv)
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
#else
# else
/* need this for other side-effects */
while (do_script_run(now));
# endif
#endif
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
@@ -529,28 +641,26 @@ int main (int argc, char **argv)
check_log_writer(&wset);
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
if (last == 0 || difftime(now, last) > 1.0 || difftime(now, last) < -1.0)
{
last = now;
#ifdef HAVE_ISC_READER
if (daemon->lease_file && !daemon->dhcp)
load_dhcp(now);
#endif
if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL))
poll_resolv();
}
if (FD_ISSET(piperead, &rset))
async_event(piperead, now);
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast();
#endif
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
if (daemon->last_resolv == 0 ||
difftime(now, daemon->last_resolv) > 1.0 ||
difftime(now, daemon->last_resolv) < -1.0)
{
/* poll_resolv doesn't need to reload first time through, since
that's queued anyway. */
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
}
if (FD_ISSET(piperead, &rset))
async_event(piperead, now);
#ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */
@@ -571,12 +681,19 @@ int main (int argc, char **argv)
check_tftp_listeners(&rset, now);
#endif
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(now);
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
if (FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(now, 0);
if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
dhcp_packet(now, 1);
}
#ifndef NO_FORK
# ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write();
# endif
#endif
}
@@ -628,11 +745,51 @@ void send_event(int fd, int event, int data)
ev.event = event;
ev.data = data;
/* pipe is non-blocking and struct event_desc is smaller than
PIPE_BUF, so this either fails or writes everything */
while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
/* error pipe, debug mode. */
if (fd == -1)
fatal_event(&ev);
else
/* pipe is non-blocking and struct event_desc is smaller than
PIPE_BUF, so this either fails or writes everything */
while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
}
static void fatal_event(struct event_desc *ev)
{
errno = ev->data;
switch (ev->event)
{
case EVENT_DIE:
exit(0);
case EVENT_FORK_ERR:
die(_("cannot fork into background: %s"), NULL, EC_MISC);
case EVENT_PIPE_ERR:
die(_("failed to create helper: %s"), NULL, EC_MISC);
case EVENT_CAP_ERR:
die(_("setting capabilities failed: %s"), NULL, EC_MISC);
case EVENT_USER_ERR:
case EVENT_HUSER_ERR:
die(_("failed to change user-id to %s: %s"),
ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
EC_MISC);
case EVENT_GROUP_ERR:
die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
case EVENT_PIDFILE:
die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
case EVENT_LOG_ERR:
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
}
}
static void async_event(int pipe, time_t now)
{
pid_t p;
@@ -649,7 +806,9 @@ static void async_event(int pipe, time_t now)
reload_servers(daemon->resolv_files->name);
check_servers();
}
#ifdef HAVE_DHCP
rerun_scripts();
#endif
break;
case EVENT_DUMP:
@@ -658,11 +817,13 @@ static void async_event(int pipe, time_t now)
break;
case EVENT_ALARM:
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
lease_prune(NULL, now);
lease_update_file(now);
}
#endif
break;
case EVENT_CHILD:
@@ -688,11 +849,14 @@ static void async_event(int pipe, time_t now)
break;
case EVENT_EXEC_ERR:
my_syslog(LOG_ERR, _("failed to execute %s: %s"), daemon->lease_change_command, strerror(ev.data));
my_syslog(LOG_ERR, _("failed to execute %s: %s"),
daemon->lease_change_command, strerror(ev.data));
break;
case EVENT_PIPE_ERR:
my_syslog(LOG_ERR, _("failed to create helper: %s"), strerror(ev.data));
/* necessary for fatal errors in helper */
case EVENT_HUSER_ERR:
case EVENT_DIE:
fatal_event(&ev);
break;
case EVENT_REOPEN:
@@ -709,7 +873,7 @@ static void async_event(int pipe, time_t now)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
#ifndef NO_FORK
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
@@ -725,6 +889,9 @@ static void async_event(int pipe, time_t now)
if (daemon->lease_stream)
fclose(daemon->lease_stream);
if (daemon->runfile)
unlink(daemon->runfile);
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
flush_log();
@@ -732,7 +899,7 @@ static void async_event(int pipe, time_t now)
}
}
static void poll_resolv()
void poll_resolv(int force, int do_reload, time_t now)
{
struct resolvc *res, *latest;
struct stat statbuf;
@@ -740,19 +907,37 @@ static void poll_resolv()
/* 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. */
if (daemon->port == 0 || (daemon->options & OPT_NO_POLL))
return;
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (force)
{
res->mtime = 0;
continue;
}
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
if (res->mtime != 0)
{
/* existing file evaporated, force selection of the latest
file even if its mtime hasn't changed since we last looked */
poll_resolv(1, do_reload, now);
return;
}
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (force || (statbuf.st_mtime != res->mtime))
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
@@ -769,8 +954,8 @@ static void poll_resolv()
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
if (daemon->options & OPT_RELOAD)
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
if ((daemon->options & OPT_RELOAD) && do_reload)
clear_cache_and_reload(now);
}
else
{
@@ -787,8 +972,9 @@ static void poll_resolv()
void clear_cache_and_reload(time_t now)
{
if (daemon->port != 0)
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
cache_reload();
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
if (daemon->options & OPT_ETHERS)
@@ -800,6 +986,7 @@ void clear_cache_and_reload(time_t now)
lease_update_file(now);
lease_update_dns();
}
#endif
}
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
@@ -828,7 +1015,15 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
FD_SET(serverfdp->fd, set);
bump_maxfd(serverfdp->fd, maxfdp);
}
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0)
{
FD_SET(daemon->randomsocks[i].fd, set);
bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
}
for (listener = daemon->listeners; listener; listener = listener->next)
{
/* only listen for queries if we have resources */
@@ -865,17 +1060,24 @@ static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
static void check_dns_listeners(fd_set *set, time_t now)
{
struct serverfd *serverfdp;
struct listener *listener;
struct listener *listener;
int i;
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, set))
reply_query(serverfdp, now);
reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
if (daemon->port != 0 && !daemon->osport)
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount != 0 &&
FD_ISSET(daemon->randomsocks[i].fd, set))
reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
for (listener = daemon->listeners; listener; listener = listener->next)
{
if (listener->fd != -1 && FD_ISSET(listener->fd, set))
receive_query(listener, now);
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
tftp_request(listener, now);
@@ -942,11 +1144,13 @@ static void check_dns_listeners(fd_set *set, time_t now)
dst_addr_4.s_addr = 0;
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!(daemon->options & OPT_DEBUG))
alarm(CHILD_LIFETIME);
#endif
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next)
s->tcpfd = -1;
@@ -986,7 +1190,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
}
}
#ifdef HAVE_DHCP
int make_icmp_sock(void)
{
int fd;
@@ -1109,5 +1313,6 @@ int icmp_ping(struct in_addr addr)
return gotreply;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2008 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,21 +10,41 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (C) 2000-2008 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2010 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
# define _LARGEFILE_SOURCE 1
# define _FILE_OFFSET_BITS 64
#endif
/* Get linux C library versions and define _GNU_SOURCE for kFreeBSD. */
#if defined(__linux__) || defined(__GLIBC__)
# define _GNU_SOURCE
# include <features.h>
#endif
/* Need these defined early */
#if defined(__sun) || defined(__sun__)
# define _XPG4_2
# define __EXTENSIONS__
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef __APPLE__
/* need this before arpa/nameser.h */
# define BIND_8_COMPAT
# include <nameser.h>
# include <arpa/nameser_compat.h>
#else
# include <arpa/nameser.h>
#endif
#include <arpa/nameser.h>
/* and this. */
#include <getopt.h>
@@ -42,10 +62,9 @@
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#if defined(HAVE_SOLARIS_NETWORK)
#include <sys/sockio.h>
# include <sys/sockio.h>
#endif
#include <sys/select.h>
#include <sys/wait.h>
@@ -53,6 +72,10 @@
#include <sys/un.h>
#include <limits.h>
#include <net/if.h>
#if defined(HAVE_SOLARIS_NETWORK) && !defined(ifr_mtu)
/* Some solaris net/if./h omit this. */
# define ifr_mtu ifr_ifru.ifru_metric
#endif
#include <unistd.h>
#include <stdio.h>
#include <string.h>
@@ -60,12 +83,13 @@
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <stddef.h>
#include <time.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__)
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -84,10 +108,15 @@
#if defined(HAVE_LINUX_NETWORK)
#include <linux/capability.h>
/* There doesn't seem to be a universally-available
userpace header for this. */
userpace header for these. */
extern int capset(cap_user_header_t header, cap_user_data_t data);
extern int capget(cap_user_header_t header, cap_user_data_t data);
#define LINUX_CAPABILITY_VERSION_1 0x19980330
#define LINUX_CAPABILITY_VERSION_2 0x20071026
#define LINUX_CAPABILITY_VERSION_3 0x20080522
#include <sys/prctl.h>
#elif defined(HAVE_SOLARIS_PRIVS)
#elif defined(HAVE_SOLARIS_NETWORK)
#include <priv.h>
#endif
@@ -109,6 +138,14 @@ struct event_desc {
#define EVENT_KILLED 8
#define EVENT_EXEC_ERR 9
#define EVENT_PIPE_ERR 10
#define EVENT_USER_ERR 11
#define EVENT_CAP_ERR 12
#define EVENT_PIDFILE 13
#define EVENT_HUSER_ERR 14
#define EVENT_GROUP_ERR 15
#define EVENT_DIE 16
#define EVENT_LOG_ERR 17
#define EVENT_FORK_ERR 18
/* Exit codes. */
#define EC_GOOD 0
@@ -126,38 +163,43 @@ struct event_desc {
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV (1<<0)
#define OPT_FILTER (1<<1)
#define OPT_LOG (1<<2)
#define OPT_SELFMX (1<<3)
#define OPT_NO_HOSTS (1<<4)
#define OPT_NO_POLL (1<<5)
#define OPT_DEBUG (1<<6)
#define OPT_ORDER (1<<7)
#define OPT_NO_RESOLV (1<<8)
#define OPT_EXPAND (1<<9)
#define OPT_LOCALMX (1<<10)
#define OPT_NO_NEG (1<<11)
#define OPT_NODOTS_LOCAL (1<<12)
#define OPT_NOWILD (1<<13)
#define OPT_ETHERS (1<<14)
#define OPT_RESOLV_DOMAIN (1<<15)
#define OPT_NO_FORK (1<<16)
#define OPT_AUTHORITATIVE (1<<17)
#define OPT_LOCALISE (1<<18)
#define OPT_DBUS (1<<19)
#define OPT_BOOTP_DYNAMIC (1<<20)
#define OPT_NO_PING (1<<21)
#define OPT_LEASE_RO (1<<22)
#define OPT_ALL_SERVERS (1<<23)
#define OPT_RELOAD (1<<24)
#define OPT_TFTP (1<<25)
#define OPT_TFTP_SECURE (1<<26)
#define OPT_TFTP_NOBLOCK (1<<27)
#define OPT_LOG_OPTS (1<<28)
#define OPT_TFTP_APREF (1<<29)
#define OPT_NO_OVERRIDE (1<<30)
#define OPT_NO_REBIND (1<<31)
#define OPT_BOGUSPRIV (1u<<0)
#define OPT_FILTER (1u<<1)
#define OPT_LOG (1u<<2)
#define OPT_SELFMX (1u<<3)
#define OPT_NO_HOSTS (1u<<4)
#define OPT_NO_POLL (1u<<5)
#define OPT_DEBUG (1u<<6)
#define OPT_ORDER (1u<<7)
#define OPT_NO_RESOLV (1u<<8)
#define OPT_EXPAND (1u<<9)
#define OPT_LOCALMX (1u<<10)
#define OPT_NO_NEG (1u<<11)
#define OPT_NODOTS_LOCAL (1u<<12)
#define OPT_NOWILD (1u<<13)
#define OPT_ETHERS (1u<<14)
#define OPT_RESOLV_DOMAIN (1u<<15)
#define OPT_NO_FORK (1u<<16)
#define OPT_AUTHORITATIVE (1u<<17)
#define OPT_LOCALISE (1u<<18)
#define OPT_DBUS (1u<<19)
#define OPT_DHCP_FQDN (1u<<20)
#define OPT_NO_PING (1u<<21)
#define OPT_LEASE_RO (1u<<22)
#define OPT_ALL_SERVERS (1u<<23)
#define OPT_RELOAD (1u<<24)
#define OPT_LOCAL_REBIND (1u<<25)
#define OPT_TFTP_SECURE (1u<<26)
#define OPT_TFTP_NOBLOCK (1u<<27)
#define OPT_LOG_OPTS (1u<<28)
#define OPT_TFTP_APREF (1u<<29)
#define OPT_NO_OVERRIDE (1u<<30)
#define OPT_NO_REBIND (1u<<31)
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
#define MS_TFTP LOG_USER
#define MS_DHCP LOG_DAEMON
struct all_addr {
union {
@@ -175,7 +217,7 @@ struct bogus_addr {
/* dns doctor param */
struct doctor {
struct in_addr in, out, mask;
struct in_addr in, end, out, mask;
struct doctor *next;
};
@@ -186,6 +228,12 @@ struct mx_srv_record {
struct mx_srv_record *next;
};
struct naptr {
char *name, *replace, *regexp, *services, *flags;
unsigned int order, pref;
struct naptr *next;
};
struct txt_record {
char *name, *txt;
unsigned short class, len;
@@ -197,6 +245,11 @@ struct ptr_record {
struct ptr_record *next;
};
struct cname {
char *alias, *target;
struct cname *next;
};
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
@@ -250,19 +303,7 @@ struct crec {
union mysockaddr {
struct sockaddr sa;
struct sockaddr_in in;
#ifdef HAVE_BROKEN_SOCKADDR_IN6
/* early versions of glibc don't include sin6_scope_id in sockaddr_in6
but latest kernels _require_ it to be set. The choice is to have
dnsmasq fail to compile on back-level libc or fail to run
on latest kernels with IPv6. Or to do this: sorry that it's so gross. */
struct my_sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
uint16_t sin6_port; /* transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 traffic class & flow info */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
} in6;
#elif defined(HAVE_IPV6)
#if defined(HAVE_IPV6)
struct sockaddr_in6 in6;
#endif
};
@@ -278,6 +319,8 @@ union mysockaddr {
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
struct serverfd {
int fd;
@@ -286,6 +329,11 @@ struct serverfd {
struct serverfd *next;
};
struct randfd {
int fd;
unsigned short refcount, family;
};
struct server {
union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE+1];
@@ -299,7 +347,8 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok;
int tftp_ok, mtu;
char *name;
struct irec *next;
};
@@ -326,8 +375,11 @@ struct resolvc {
};
/* adn-hosts parms from command-line */
#define AH_DIR 1
#define AH_INACTIVE 2
struct hostsfile {
struct hostsfile *next;
int flags;
char *fname;
int index; /* matches to cache entries for logging */
};
@@ -336,9 +388,13 @@ struct frec {
union mysockaddr source;
struct all_addr dest;
struct server *sentto; /* NULL means free */
struct randfd *rfd4;
#ifdef HAVE_IPV6
struct randfd *rfd6;
#endif
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall;
int fd, forwardall, norebind;
unsigned int crc;
time_t time;
struct frec *next;
@@ -367,9 +423,9 @@ struct dhcp_lease {
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr;
unsigned char *vendorclass, *userclass;
unsigned int vendorclass_len, userclass_len;
struct in_addr addr, override, giaddr;
unsigned char *extradata;
unsigned int extradata_len, extradata_size;
int last_interface;
struct dhcp_lease *next;
};
@@ -384,27 +440,37 @@ struct dhcp_netid_list {
struct dhcp_netid_list *next;
};
struct tag_if {
struct dhcp_netid_list *set;
struct dhcp_netid *tag;
struct tag_if *next;
};
struct hwaddr_config {
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
unsigned int wildcard_mask;
struct hwaddr_config *next;
};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
char *hostname;
struct dhcp_netid netid;
char *hostname, *domain;
struct dhcp_netid_list *netid;
struct in_addr addr;
time_t decline_time;
unsigned int lease_time, wildcard_mask;
unsigned int lease_time;
struct hwaddr_config *hwaddr;
struct dhcp_config *next;
};
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_HWADDR 4
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
@@ -413,7 +479,12 @@ struct dhcp_config {
struct dhcp_opt {
int opt, len, flags;
unsigned char *val, *vendor_class;
union {
int encap;
unsigned int wildcard_mask;
unsigned char *vendor_class;
} u;
unsigned char *val;
struct dhcp_netid *netid;
struct dhcp_opt *next;
};
@@ -421,9 +492,15 @@ struct dhcp_opt {
#define DHOPT_ADDR 1
#define DHOPT_STRING 2
#define DHOPT_ENCAPSULATE 4
#define DHOPT_VENDOR_MATCH 8
#define DHOPT_ENCAP_MATCH 8
#define DHOPT_FORCE 16
#define DHOPT_BANK 32
#define DHOPT_ENCAP_DONE 64
#define DHOPT_MATCH 128
#define DHOPT_VENDOR 256
#define DHOPT_HEX 512
#define DHOPT_VENDOR_MATCH 1024
#define DHOPT_RFC3925 2048
struct dhcp_boot {
char *file, *sname;
@@ -432,12 +509,19 @@ struct dhcp_boot {
struct dhcp_boot *next;
};
struct pxe_service {
unsigned short CSA, type;
char *menu, *basename;
struct in_addr server;
struct dhcp_netid *netid;
struct pxe_service *next;
};
#define MATCH_VENDOR 1
#define MATCH_USER 2
#define MATCH_CIRCUIT 3
#define MATCH_REMOTE 4
#define MATCH_SUBSCRIBER 5
#define MATCH_OPTION 6
/* vendorclass, userclass, remote-id or cicuit-id */
struct dhcp_vendor {
@@ -455,12 +539,16 @@ struct dhcp_mac {
struct dhcp_mac *next;
};
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge {
char iface[IF_NAMESIZE];
struct dhcp_bridge *alias, *next;
};
#endif
struct cond_domain {
char *domain;
struct in_addr start, end;
struct cond_domain *next;
};
struct dhcp_context {
unsigned int lease_time, addr_epoch;
@@ -468,6 +556,7 @@ struct dhcp_context {
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
int flags;
char *interface;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
@@ -475,6 +564,7 @@ struct dhcp_context {
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
typedef unsigned char u8;
@@ -509,13 +599,31 @@ struct tftp_transfer {
int sockfd;
time_t timeout;
int backoff;
unsigned int block, blocksize;
unsigned int block, blocksize, expansion;
off_t offset;
struct sockaddr_in peer;
char opt_blocksize, opt_transize;
char opt_blocksize, opt_transize, netascii, carrylf;
struct tftp_file *file;
struct tftp_transfer *next;
};
struct addr_list {
struct in_addr addr;
struct addr_list *next;
};
struct interface_list {
char *interface;
struct interface_list *next;
};
struct tftp_prefix {
char *interface;
char *prefix;
struct tftp_prefix *next;
};
extern struct daemon {
/* datastuctures representing the command-line and
config file arguments. All set (including defaults)
@@ -523,14 +631,19 @@ extern struct daemon {
unsigned int options;
struct resolvc default_resolv, *resolv_files;
time_t last_resolv;
struct mx_srv_record *mxnames;
struct naptr *naptr;
struct txt_record *txt;
struct ptr_record *ptr;
struct cname *cnames;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
char *username, *groupname;
char *username, *groupname, *scriptuser;
int group_set, osport;
char *domain_suffix;
struct cond_domain *cond_domain;
char *runfile;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
@@ -540,51 +653,65 @@ extern struct daemon {
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port;
unsigned long local_ttl, neg_ttl;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts;
struct dhcp_opt *dhcp_opts, *dhcp_match;
struct dhcp_vendor *dhcp_vendors;
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *force_broadcast;
struct pxe_service *pxe_services;
struct tag_if *tag_if;
struct addr_list *override_relays;
int override;
int enable_pxe;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
char *dhcp_hosts_file, *dhcp_opts_file;
int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
unsigned int min_leasetime;
struct doctor *doctors;
unsigned short edns_pktsz;
char *tftp_prefix;
struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
int tftp_unlimited;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
unsigned int local_answer, queries_forwarded;
struct frec *frec_list;
struct serverfd *sfds;
struct irec *interfaces;
struct listener *listeners;
struct server *last_server;
time_t forwardtime;
int forwardcount;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
struct randfd *rfd_save; /* " " */
pid_t tcp_pids[MAX_PROCS];
struct randfd randomsocks[RANDOM_SOCKS];
int v6pktinfo;
/* DHCP state */
int dhcpfd, helperfd;
#ifdef HAVE_LINUX_NETWORK
int dhcpfd, helperfd, pxefd;
#if defined(HAVE_LINUX_NETWORK)
int netlinkfd;
#else
#elif defined(HAVE_BSD_NETWORK)
int dhcp_raw_fd, dhcp_icmp_fd;
#endif
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
struct ping_result *ping_results;
FILE *lease_stream;
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge *bridges;
#endif
/* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */
@@ -600,8 +727,9 @@ extern struct daemon {
/* cache.c */
void cache_init(void);
void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index);
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg);
char *record_source(int index);
void querystr(char *str, unsigned short type);
struct crec *cache_find_by_addr(struct crec *crecp,
struct all_addr *addr, time_t now,
unsigned short prot);
@@ -611,11 +739,12 @@ void cache_end_insert(void);
void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts);
void cache_reload(void);
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
char *get_domain(struct in_addr addr);
/* rfc1035.c */
unsigned short extract_request(HEADER *header, size_t qlen,
@@ -623,7 +752,8 @@ unsigned short extract_request(HEADER *header, size_t qlen,
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind);
size_t answer_request(HEADER *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
@@ -636,11 +766,13 @@ size_t resize_packet(HEADER *header, size_t plen,
unsigned char *pheader, size_t hlen);
/* util.c */
void rand_init(void);
unsigned short rand16(void);
int legal_char(char c);
int canonicalise(char *s);
int legal_hostname(char *c);
char *canonicalise(char *s, int *nomem);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
void *safe_malloc(size_t size);
void safe_pipe(int *fd, int read_noblock);
void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
@@ -661,7 +793,7 @@ int read_write(int fd, unsigned char *packet, int size, int rw);
/* log.c */
void die(char *message, char *arg1, int exit_code);
void log_start(struct passwd *ent_pw);
int log_start(struct passwd *ent_pw, int errfd);
int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...);
void set_log_writer(fd_set *set, int *maxfdp);
@@ -670,11 +802,11 @@ void flush_log(void);
/* option.c */
void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(unsigned char opt);
char *option_string(unsigned char opt, int *is_ip, int *is_name);
void reread_dhcp(void);
/* forward.c */
void reply_query(struct serverfd *sfd, time_t now);
void reply_query(int fd, int family, time_t now);
void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
@@ -682,22 +814,23 @@ void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
/* network.c */
int indextoname(int fd, int index, char *name);
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
int random_sock(int family);
void pre_allocate_sfds(void);
int reload_servers(char *fname);
void check_servers(void);
int enumerate_interfaces();
struct listener *create_wildcard_listeners(void);
struct listener *create_bound_listeners(void);
int iface_check(int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp);
int iface_check(int family, struct all_addr *addr, char *name, int *indexp);
int fix_fd(int fd);
struct in_addr get_ifaddr(char *intr);
/* dhcp.c */
#ifdef HAVE_DHCP
void dhcp_init(void);
void dhcp_packet(time_t now);
void dhcp_packet(time_t now, int pxe_fd);
struct dhcp_context *address_available(struct dhcp_context *context,
struct in_addr addr,
struct dhcp_netid *netids);
@@ -708,6 +841,8 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
@@ -719,16 +854,18 @@ void check_dhcp_hosts(int fatal);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(char *hostname);
char *host_from_dns(struct in_addr addr);
char *get_domain(struct in_addr addr);
#endif
/* lease.c */
#ifdef HAVE_DHCP
void lease_update_file(time_t now);
void lease_update_dns();
void lease_init(time_t now);
struct dhcp_lease *lease_allocate(struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name,
char *suffix, int auth);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
@@ -738,21 +875,24 @@ void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(void);
int do_script_run(time_t now);
void rerun_scripts(void);
#endif
/* rfc2131.c */
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform);
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
#endif
/* dnsmasq.c */
#ifdef HAVE_DHCP
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
#endif
void send_event(int fd, int event, int data);
void clear_cache_and_reload(time_t now);
/* isc.c */
#ifdef HAVE_ISC_READER
void load_dhcp(time_t now);
#endif
void poll_resolv(int force, int do_reload, time_t now);
/* netlink.c */
#ifdef HAVE_LINUX_NETWORK
@@ -775,11 +915,14 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
char *dbus_init(void);
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset);
void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset);
# ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
#endif
/* helper.c */
#ifndef NO_FORK
int create_helper(int log_fd, long max_fd);
#if defined(HAVE_DHCP) && !defined(NO_FORK)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
void helper_write(void);
void queue_script(int action, struct dhcp_lease *lease,
char *hostname, time_t now);

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,20 +10,19 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
static struct frec *frec_list = NULL;
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
static unsigned short get_id(unsigned int crc);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
@@ -66,15 +65,15 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
if (to->sa.sa_family == AF_INET)
{
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
struct in_pktinfo p;
p.ipi_ifindex = 0;
p.ipi_spec_dst = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
*a = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(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;
@@ -83,11 +82,12 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
else
#ifdef HAVE_IPV6
{
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
pkt->ipi6_addr = source->addr.addr6;
struct in6_pktinfo p;
p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
p.ipi6_addr = source->addr.addr6;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = IPV6_PKTINFO;
cmptr->cmsg_type = daemon->v6pktinfo;
cmptr->cmsg_level = IPV6_LEVEL;
}
#else
@@ -112,7 +112,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
}
static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain)
unsigned short qtype, char *qdomain, int *type, char **domain, int *norebind)
{
/* If the query ends in the domain in one of our servers, set
@@ -154,38 +154,44 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
char *matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen &&
hostname_isequal(matchstart, serv->domain) &&
domainlen >= matchlen &&
(domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
(domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_HAS_DOMAIN;
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
if (serv->flags & SERV_NO_REBIND)
*norebind = 1;
else if (domainlen >= matchlen)
{
if (sflag & qtype)
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
}
else
flags = 0;
}
}
}
if (flags == 0 && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception for NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR;
@@ -196,9 +202,13 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
if (flags == F_NXDOMAIN || flags == F_NOERR)
logflags = F_NEG | qtype;
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, 0, NULL, 0);
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
}
else if ((*type) & SERV_USE_RESOLV)
{
*type = 0; /* use normal servers for this domain */
*domain = NULL;
}
return flags;
}
@@ -207,7 +217,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
HEADER *header, size_t plen, time_t now, struct frec *forward)
{
char *domain = NULL;
int type = 0;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned short flags = 0;
@@ -235,7 +245,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (!flags && !(forward = get_new_frec(now, NULL)))
/* table full - server failure. */
@@ -243,30 +253,41 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (forward)
{
/* force unchanging id for signed packets */
int is_sign;
find_pseudoheader(header, plen, NULL, NULL, &is_sign);
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(is_sign, forward->orig_id, crc);
forward->new_id = get_id(crc);
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
forward->norebind = norebind;
header->id = htons(forward->new_id);
/* In strict_order mode, or when using domain specific servers
always try servers in the order specified in resolv.conf,
/* In strict_order mode, always try servers in the order
specified in resolv.conf, if a domain is given
always try all the available servers,
otherwise, use the one last known to work. */
if (type != 0 || (daemon->options & OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server))
if (type == 0)
{
if (daemon->options & OPT_ORDER)
start = daemon->servers;
else if (!(start = daemon->last_server) ||
daemon->forwardcount++ > FORWARD_TEST ||
difftime(now, daemon->forwardtime) > FORWARD_TIME)
{
start = daemon->servers;
forward->forwardall = 1;
daemon->forwardcount = 0;
daemon->forwardtime = now;
}
}
else
{
start = daemon->servers;
forward->forwardall = 1;
if (!(daemon->options & OPT_ORDER))
forward->forwardall = 1;
}
}
}
@@ -290,7 +311,34 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
(type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
!(start->flags & SERV_LITERAL_ADDRESS))
{
if (sendto(start->sfd->fd, (char *)header, plen, 0,
int fd;
/* find server socket to use, may need to get random one. */
if (start->sfd)
fd = start->sfd->fd;
else
{
#ifdef HAVE_IPV6
if (start->addr.sa.sa_family == AF_INET6)
{
if (!forward->rfd6 &&
!(forward->rfd6 = allocate_rfd(AF_INET6)))
break;
daemon->rfd_save = forward->rfd6;
fd = forward->rfd6->fd;
}
else
#endif
{
if (!forward->rfd4 &&
!(forward->rfd4 = allocate_rfd(AF_INET)))
break;
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
}
if (sendto(fd, (char *)header, plen, 0,
&start->addr.sa,
sa_len(&start->addr)) == -1)
{
@@ -307,13 +355,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
strcpy(daemon->namebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&start->addr.in.sin_addr, 0,
NULL, 0);
(struct all_addr *)&start->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&start->addr.in6.sin6_addr, 0,
NULL, 0);
(struct all_addr *)&start->addr.in6.sin6_addr, NULL);
#endif
start->queries++;
forwarded = 1;
@@ -336,7 +382,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->sentto = NULL; /* cancel */
free_frec(forward); /* cancel */
}
/* could not send on, return empty answer or address if known for whole domain */
@@ -350,15 +396,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n)
struct server *server, size_t n, int check_rebind)
{
unsigned char *pheader, *sizep;
int munged = 0, is_sign;
size_t plen;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
{
@@ -404,9 +450,9 @@ static size_t process_reply(HEADER *header, time_t now,
header->rcode = NOERROR;
}
if (extract_addresses(header, n, daemon->namebuff, now))
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected"));
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
}
}
@@ -428,7 +474,7 @@ static size_t process_reply(HEADER *header, time_t now,
}
/* sets new last_server */
void reply_query(struct serverfd *sfd, time_t now)
void reply_query(int fd, int family, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
@@ -436,91 +482,106 @@ void reply_query(struct serverfd *sfd, time_t now)
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
size_t nn;
struct server *server;
/* packet buffer overwritten */
daemon->srv_save = NULL;
/* Determine the address of the server replying so that we can mark that as good */
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
serveraddr.sa.sa_family = family;
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET6)
serveraddr.in6.sin6_flowinfo = 0;
#endif
/* spoof check: answer must come from known server, */
for (server = daemon->servers; server; server = server->next)
if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
sockaddr_isequal(&server->addr, &serveraddr))
break;
header = (HEADER *)daemon->packet;
if (n >= (int)sizeof(HEADER) && header->qr &&
(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
if (!server ||
n < (int)sizeof(HEADER) || !header->qr ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
return;
server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
!(daemon->options & OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
struct server *server = forward->sentto;
unsigned char *pheader;
size_t plen;
int is_sign;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
!(daemon->options & OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
if (!is_sign)
{
unsigned char *pheader;
size_t plen;
int is_sign;
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
struct server *last_server;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
if (!is_sign)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
}
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
server = last_server;
break;
}
}
if (!(daemon->options & OPT_ALL_SERVERS))
daemon->last_server = server;
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
int check_rebind = !forward->norebind;
if (!(daemon->options & OPT_NO_REBIND))
check_rebind = 0;
if ((forward->sentto->flags & SERV_TYPE) == 0)
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind)))
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
struct server *last_server;
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
server = last_server;
break;
}
}
if (!(daemon->options & OPT_ALL_SERVERS))
daemon->last_server = server;
}
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
if ((nn = process_reply(header, now, server, (size_t)n)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
forward->sentto = NULL; /* cancel */
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
free_frec(forward); /* cancel */
}
}
void receive_query(struct listener *listen, time_t now)
{
HEADER *header = (HEADER *)daemon->packet;
@@ -601,21 +662,37 @@ void receive_query(struct listener *listen, time_t now)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (listen->family == AF_INET)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *((unsigned int *)CMSG_DATA(cmptr));
#else
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
{
union {
unsigned char *c;
unsigned int *i;
struct in_addr *a;
#ifndef HAVE_SOLARIS_NETWORK
struct sockaddr_dl *s;
#endif
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *(p.i);
#else
if_index = p.s->sdl_index;
#endif
}
}
#endif
@@ -623,29 +700,24 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
dst_addr.addr.addr6 = p.p->ipi6_addr;
if_index = p.p->ipi6_ifindex;
}
}
#endif
/* enforce available interface configuration */
if (if_index == 0)
return;
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
#else
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (!iface_check(listen->family, &dst_addr, &ifr, &if_index))
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
return;
if (listen->family == AF_INET &&
@@ -658,13 +730,17 @@ void receive_query(struct listener *listen, time_t now)
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
char types[20];
querystr(types, type);
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&source_addr.in.sin_addr, type, NULL, 0);
(struct all_addr *)&source_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&source_addr.in6.sin6_addr, type, NULL, 0);
(struct all_addr *)&source_addr.in6.sin6_addr, types);
#endif
}
@@ -690,7 +766,7 @@ void receive_query(struct listener *listen, time_t now)
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
{
int size = 0;
int size = 0, norebind = 0;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
@@ -719,13 +795,17 @@ unsigned char *tcp_request(int confd, time_t now,
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
{
char types[20];
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, qtype, NULL, 0);
(struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, qtype, NULL, 0);
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif
}
}
@@ -745,7 +825,7 @@ unsigned char *tcp_request(int confd, time_t now,
char *domain = NULL;
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
@@ -761,7 +841,7 @@ unsigned char *tcp_request(int confd, time_t now,
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
while (1)
{
{
if (!firstsendto)
firstsendto = last_server;
else
@@ -812,11 +892,11 @@ unsigned char *tcp_request(int confd, time_t now,
strcpy(daemon->namebuff, "query");
if (last_server->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in.sin_addr, 0, NULL, 0);
(struct all_addr *)&last_server->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in6.sin6_addr, 0, NULL, 0);
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
#endif
/* There's no point in updating the cache, since this process will exit and
@@ -826,7 +906,7 @@ unsigned char *tcp_request(int confd, time_t now,
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m);
m = process_reply(header, now, last_server, (unsigned int)m, (daemon->options & OPT_NO_REBIND) && !norebind );
break;
}
@@ -854,34 +934,103 @@ static struct frec *allocate_frec(time_t now)
if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
{
f->next = frec_list;
f->next = daemon->frec_list;
f->time = now;
f->sentto = NULL;
frec_list = f;
f->rfd4 = NULL;
#ifdef HAVE_IPV6
f->rfd6 = NULL;
#endif
daemon->frec_list = f;
}
return f;
}
static struct randfd *allocate_rfd(int family)
{
static int finger = 0;
int i;
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
for (i = 0; i < RANDOM_SOCKS; i++)
if (daemon->randomsocks[i].refcount == 0)
{
if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
break;
daemon->randomsocks[i].refcount = 1;
daemon->randomsocks[i].family = family;
return &daemon->randomsocks[i];
}
/* No free ones or cannot get new socket, grab an existing one */
for (i = 0; i < RANDOM_SOCKS; i++)
{
int j = (i+finger) % RANDOM_SOCKS;
if (daemon->randomsocks[j].refcount != 0 &&
daemon->randomsocks[j].family == family &&
daemon->randomsocks[j].refcount != 0xffff)
{
finger = j;
daemon->randomsocks[j].refcount++;
return &daemon->randomsocks[j];
}
}
return NULL; /* doom */
}
static void free_frec(struct frec *f)
{
if (f->rfd4 && --(f->rfd4->refcount) == 0)
close(f->rfd4->fd);
f->rfd4 = NULL;
f->sentto = NULL;
#ifdef HAVE_IPV6
if (f->rfd6 && --(f->rfd6->refcount) == 0)
close(f->rfd6->fd);
f->rfd6 = NULL;
#endif
}
/* if wait==NULL return a free or older than TIMEOUT record.
else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. */
when the oldest in-use record will expire. Impose an absolute
limit of 4*TIMEOUT before we wipe things (for random sockets) */
struct frec *get_new_frec(time_t now, int *wait)
{
struct frec *f, *oldest;
struct frec *f, *oldest, *target;
int count;
if (wait)
*wait = 0;
for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
if (!f->sentto)
target = f;
else
{
f->time = now;
return f;
if (difftime(now, f->time) >= 4*TIMEOUT)
{
free_frec(f);
target = f;
}
if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
}
else if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
if (target)
{
target->time = now;
return target;
}
/* can't find empty one, use oldest if there is one
and it's older than timeout */
@@ -896,7 +1045,7 @@ struct frec *get_new_frec(time_t now, int *wait)
if (!wait)
{
oldest->sentto = 0;
free_frec(oldest);
oldest->time = now;
}
return oldest;
@@ -922,7 +1071,7 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc)
{
struct frec *f;
for(f = frec_list; f; f = f->next)
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
return f;
@@ -936,7 +1085,7 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
{
struct frec *f;
for(f = frec_list; f; f = f->next)
for(f = daemon->frec_list; f; f = f->next)
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
@@ -951,9 +1100,9 @@ void server_gone(struct server *server)
{
struct frec *f;
for (f = frec_list; f; f = f->next)
for (f = daemon->frec_list; f; f = f->next)
if (f->sentto && f->sentto == server)
f->sentto = NULL;
free_frec(f);
if (daemon->last_server == server)
daemon->last_server = NULL;
@@ -962,22 +1111,12 @@ void server_gone(struct server *server)
daemon->srv_save = NULL;
}
/* return unique random ids.
For signed packets we can't change the ID without breaking the
signing, so we keep the same one. In this case force is set, and this
routine degenerates into killing any conflicting forward record. */
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
/* return unique random ids. */
static unsigned short get_id(unsigned int crc)
{
unsigned short ret = 0;
if (force)
{
struct frec *f = lookup_frec(force_id, crc);
if (f)
f->sentto = NULL; /* free */
ret = force_id;
}
else do
do
ret = rand16();
while (lookup_frec(ret, crc));

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -28,15 +28,16 @@
main process.
*/
#ifndef NO_FORK
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
static void my_setenv(const char *name, const char *value, int *error);
static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
struct script_data
{
unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, uclass_len, vclass_len;
struct in_addr addr;
unsigned char clid_len, hostname_len, ed_len;
struct in_addr addr, giaddr;
unsigned int remaining_time;
#ifdef HAVE_BROKEN_RTC
unsigned int length;
@@ -50,22 +51,18 @@ struct script_data
static struct script_data *buf = NULL;
static size_t bytes_in_buf = 0, buf_size = 0;
int create_helper(int event_fd, long max_fd)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{
pid_t pid;
int i, pipefd[2];
struct sigaction sigact;
if (!daemon->dhcp || !daemon->lease_change_command)
return -1;
/* create the pipe through which the main program sends us commands,
then fork our process. By now it's too late to die(), we just log
any failure via the main process. */
then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{
send_event(event_fd, EVENT_PIPE_ERR, errno);
return -1;
send_event(err_fd, EVENT_PIPE_ERR, errno);
_exit(0);
}
if (pid != 0)
@@ -82,8 +79,30 @@ int create_helper(int event_fd, long max_fd)
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
/* close all the sockets etc, we don't need them here */
for (max_fd--; max_fd > 0; max_fd--)
if (!(daemon->options & OPT_DEBUG) && uid != 0)
{
gid_t dummy;
if (setgroups(0, &dummy) == -1 ||
setgid(gid) == -1 ||
setuid(uid) == -1)
{
if (daemon->options & OPT_NO_FORK)
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_HUSER_ERR, errno);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0);
/* return error */
send_event(err_fd, EVENT_HUSER_ERR, errno);
}
_exit(0);
}
}
/* close all the sockets etc, we don't need them here. This closes err_fd, so that
main process can return. */
for (max_fd--; max_fd >= 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
close(max_fd);
@@ -94,6 +113,7 @@ int create_helper(int event_fd, long max_fd)
struct script_data data;
char *p, *action_str, *hostname = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff;
unsigned char *end, *alloc_buff = NULL;
int err = 0;
/* we read zero bytes when pipe closed: this is our signal to exit */
@@ -112,16 +132,16 @@ int create_helper(int event_fd, long max_fd)
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet */
if (!read_write(pipefd[0], buf, data.clid_len, 1))
/* and CLID into packet, avoid overwrite from bad data */
if ((data.clid_len > daemon->packet_buff_sz) || !read_write(pipefd[0], buf, data.clid_len, 1))
continue;
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{
@@ -132,17 +152,25 @@ int create_helper(int event_fd, long max_fd)
/* and expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u ", data.length);
sprintf(daemon->dhcp_buff2, "%u", data.length);
#else
sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires);
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
#endif
if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1))
/* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len) > daemon->packet_buff_sz &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len)))
continue;
if (!read_write(pipefd[0], buf,
data.hostname_len + data.ed_len, 1))
continue;
/* possible fork errors are all temporary resource problems */
while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2);
free(alloc_buff);
if (pid == -1)
continue;
@@ -185,46 +213,44 @@ int create_helper(int event_fd, long max_fd)
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
if (data.vclass_len != 0)
{
buf[data.vclass_len - 1] = 0; /* don't trust zero-term */
/* cannot have = chars in env - truncate if found . */
if ((p = strchr((char *)buf, '=')))
*p = 0;
my_setenv("DNSMASQ_VENDOR_CLASS", (char *)buf, &err);
buf += data.vclass_len;
}
if (data.uclass_len != 0)
{
unsigned char *end = buf + data.uclass_len;
buf[data.uclass_len - 1] = 0; /* don't trust zero-term */
for (i = 0; buf < end;)
{
size_t len = strlen((char *)buf) + 1;
if ((p = strchr((char *)buf, '=')))
*p = 0;
if (strlen((char *)buf) != 0)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i++);
my_setenv(daemon->dhcp_buff2, (char *)buf, &err);
}
buf += len;
}
}
sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
if (data.hostname_len != 0)
{
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!canonicalise(hostname))
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
*dot = 0;
}
buf += data.hostname_len;
}
end = buf + data.ed_len;
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
if (data.action != ACTION_DEL)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
@@ -252,57 +278,58 @@ int create_helper(int event_fd, long max_fd)
static void my_setenv(const char *name, const char *value, int *error)
{
if (*error == 0)
{
#if defined(HAVE_SOLARIS_NETWORK) && !defined(HAVE_SOLARIS_PRIVS)
/* old Solaris is missing setenv..... */
char *p;
if (!(p = malloc(strlen(name) + strlen(value) + 2)))
*error = ENOMEM;
else
{
strcpy(p, name);
strcat(p, "=");
strcat(p, value);
if (putenv(p) != 0)
*error = errno;
}
#else
if (setenv(name, value, 1) != 0)
*error = errno;
#endif
}
if (*error == 0 && setenv(name, value, 1) != 0)
*error = errno;
}
static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err)
{
unsigned char *next;
if (!buf || (buf == end))
return NULL;
for (next = buf; *next != 0; next++)
if (next == end)
return NULL;
if (next != buf)
{
char *p;
/* No "=" in value */
if ((p = strchr((char *)buf, '=')))
*p = 0;
my_setenv(env, (char *)buf, err);
}
return next + 1;
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{
unsigned char *p;
size_t size;
unsigned int i, hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
/* no script */
if (daemon->helperfd == -1)
return;
if (lease->vendorclass)
vclass_len = lease->vendorclass_len;
if (lease->userclass)
uclass_len = lease->userclass_len;
if (lease->extradata)
ed_len = lease->extradata_len;
if (lease->clid)
clid_len = lease->clid_len;
if (hostname)
hostname_len = strlen(hostname) + 1;
size = sizeof(struct script_data) + clid_len + vclass_len + uclass_len + hostname_len;
size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
if (size > buf_size)
{
struct script_data *new;
/* start with resonable size, will almost never need extending. */
/* start with reasonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200;
@@ -318,24 +345,13 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
buf->hwaddr_len = lease->hwaddr_len;
buf->hwaddr_type = lease->hwaddr_type;
buf->clid_len = clid_len;
buf->vclass_len = vclass_len;
buf->uclass_len = uclass_len;
buf->ed_len = ed_len;
buf->hostname_len = hostname_len;
buf->addr = lease->addr;
buf->giaddr = lease->giaddr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
buf->interface[0] = 0;
#ifdef HAVE_LINUX_NETWORK
if (lease->last_interface != 0)
{
struct ifreq ifr;
ifr.ifr_ifindex = lease->last_interface;
if (ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) != -1)
strncpy(buf->interface, ifr.ifr_name, IF_NAMESIZE);
}
#else
if (lease->last_interface != 0)
if_indextoname(lease->last_interface, buf->interface);
#endif
if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface))
buf->interface[0] = 0;
#ifdef HAVE_BROKEN_RTC
buf->length = lease->length;
@@ -350,23 +366,16 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
memcpy(p, lease->clid, clid_len);
p += clid_len;
}
if (vclass_len != 0)
if (hostname_len != 0)
{
memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len;
memcpy(p, hostname, hostname_len);
p += hostname_len;
}
if (uclass_len != 0)
if (ed_len != 0)
{
memcpy(p, lease->userclass, uclass_len);
p += uclass_len;
memcpy(p, lease->extradata, ed_len);
p += ed_len;
}
/* substitute * for space */
for (i = 0; i < hostname_len; i++)
if ((daemon->options & OPT_LEASE_RO) && hostname[i] == ' ')
*(p++) = '*';
else
*(p++) = hostname[i];
bytes_in_buf = p - (unsigned char *)buf;
}

252
src/isc.c
View File

@@ -1,252 +0,0 @@
/* dnsmasq is Copyright (c) 2000-2007 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, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Code in this file is based on contributions by John Volpe. */
#include "dnsmasq.h"
#ifdef HAVE_ISC_READER
#define MAXTOK 50
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(time_t now)
{
char *hostname = daemon->namebuff;
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(daemon->lease_file, &statbuf) == -1)
{
if (!logged_lease)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), daemon->lease_file, strerror(errno));
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 (daemon->lease_file, "r")))
{
my_syslog (LOG_ERR, _("failed to load %s: %s"), daemon->lease_file, strerror(errno));
return;
}
my_syslog (LOG_INFO, _("reading %s"), daemon->lease_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;
my_syslog(LOG_ERR, _("bad name in %s"), daemon->lease_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 const 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 (!daemon->domain_suffix || hostname_isequal(dot+1, daemon->domain_suffix))
{
my_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 = whine_malloc(sizeof(struct isc_lease))))
{
lease->expires = ttd;
lease->addr = host_address;
lease->fqdn = NULL;
lease->next = leases;
if (!(lease->name = whine_malloc(strlen(hostname)+1)))
free(lease);
else
{
leases = lease;
strcpy(lease->name, hostname);
if (daemon->domain_suffix &&
(lease->fqdn = whine_malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2)))
{
strcpy(lease->fqdn, hostname);
strcat(lease->fqdn, ".");
strcat(lease->fqdn, daemon->domain_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)
{
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires);
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,12 +10,14 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
static struct dhcp_lease *leases = NULL, *old_leases = NULL;
static int dns_dirty, file_dirty, leases_left;
@@ -27,11 +29,12 @@ void lease_init(time_t now)
int clid_len, hw_len, hw_type;
FILE *leasestream;
/* These two each hold a DHCP option max size 255
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
leases_left = daemon->dhcp_max;
if (daemon->options & OPT_LEASE_RO)
@@ -40,14 +43,20 @@ void lease_init(time_t now)
initial state of the database. If leasefile-ro is
set without a script, we just do without any
lease database. */
if (!daemon->lease_change_command)
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command)
{
file_dirty = dns_dirty = 0;
return;
strcpy(daemon->dhcp_buff, daemon->lease_change_command);
strcat(daemon->dhcp_buff, " init");
leasestream = popen(daemon->dhcp_buff, "r");
}
strcpy(daemon->dhcp_buff, daemon->lease_change_command);
strcat(daemon->dhcp_buff, " init");
leasestream = popen(daemon->dhcp_buff, "r");
else
#endif
{
file_dirty = dns_dirty = 0;
return;
}
}
else
{
@@ -57,7 +66,7 @@ void lease_init(time_t now)
if (!leasestream)
die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
/* a+ mode lease pointer at end. */
/* a+ mode leaves pointer at end. */
rewind(leasestream);
}
@@ -98,19 +107,14 @@ void lease_init(time_t now)
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0)
{
char *p;
/* unprotect spaces */
for (p = strchr(daemon->dhcp_buff, '*'); p; p = strchr(p, '*'))
*p = ' ';
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
}
lease_set_hostname(lease, daemon->dhcp_buff, 0);
/* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */
lease->new = lease->changed = 0;
}
#ifdef HAVE_SCRIPT
if (!daemon->lease_stream)
{
int rc = 0;
@@ -131,6 +135,7 @@ void lease_init(time_t now)
die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
}
}
#endif
/* Some leases may have expired */
file_dirty = 0;
@@ -151,9 +156,9 @@ void lease_update_from_configs(void)
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
(config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, daemon->domain_suffix, 1);
lease_set_hostname(lease, config->hostname, 1);
else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, daemon->domain_suffix, 1); /* updates auth flag only */
lease_set_hostname(lease, name, 1); /* updates auth flag only */
}
static void ourprintf(int *errp, char *format, ...)
@@ -171,7 +176,6 @@ void lease_update_file(time_t now)
struct dhcp_lease *lease;
time_t next_event;
int i, err = 0;
char *p;
if (file_dirty != 0 && daemon->lease_stream)
{
@@ -197,15 +201,8 @@ void lease_update_file(time_t now)
}
ourprintf(&err, " %s ", inet_ntoa(lease->addr));
/* substitute * for space: "*" is an illegal name, as is " " */
if (lease->hostname)
for (p = lease->hostname; *p; p++)
ourprintf(&err, "%c", *p == ' ' ? '*' : *p);
else
ourprintf(&err, "*");
ourprintf(&err, " ");
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
@@ -235,7 +232,7 @@ void lease_update_file(time_t now)
if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
next_event = LEASE_RETRY + now;
my_syslog(LOG_ERR, _("failed to write %s: %s (retry in %us)"),
my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"),
daemon->lease_file, strerror(err),
(unsigned int)difftime(next_event, now));
}
@@ -254,8 +251,11 @@ void lease_update_dns(void)
for (lease = leases; lease; lease = lease->next)
{
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
}
dns_dirty = 0;
@@ -412,11 +412,33 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int auth)
static void kill_name(struct dhcp_lease *lease)
{
/* run script to say we lost our old name */
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
free(lease->old_hostname);
/* If we know the fqdn, pass that. The helper will derive the
unqualified name from it, free the unqulaified name here. */
if (lease->fqdn)
{
lease->old_hostname = lease->fqdn;
free(lease->hostname);
}
else
lease->old_hostname = lease->hostname;
lease->hostname = lease->fqdn = NULL;
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
{
struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
if (lease->hostname && name && hostname_isequal(lease->hostname, name))
{
lease->auth_name = auth;
@@ -433,44 +455,47 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
if (name)
{
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
if (lease_tmp->hostname && hostname_isequal(lease_tmp->hostname, name))
{
if (lease_tmp->auth_name && !auth)
return;
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
free(lease_tmp->old_hostname);
lease_tmp->old_hostname = lease_tmp->hostname;
lease_tmp->hostname = NULL;
if (lease_tmp->fqdn)
{
new_fqdn = lease_tmp->fqdn;
lease_tmp->fqdn = NULL;
}
break;
}
if (!new_name && (new_name = whine_malloc(strlen(name) + 1)))
strcpy(new_name, name);
if (suffix && !new_fqdn && (new_fqdn = whine_malloc(strlen(name) + strlen(suffix) + 2)))
if ((new_name = whine_malloc(strlen(name) + 1)))
{
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
strcat(new_fqdn, suffix);
char *suffix = get_domain(lease->addr);
strcpy(new_name, name);
if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
{
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
strcat(new_fqdn, suffix);
}
}
/* Depending on mode, we check either unqualified name or FQDN. */
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
{
if (daemon->options & OPT_DHCP_FQDN)
{
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
continue;
}
else
{
if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
continue;
}
if (lease_tmp->auth_name && !auth)
{
free(new_name);
free(new_fqdn);
return;
}
kill_name(lease_tmp);
break;
}
}
if (lease->hostname)
{
/* run script to say we lost our old name */
free(lease->old_hostname);
lease->old_hostname = lease->hostname;
}
kill_name(lease);
free(lease->fqdn);
lease->hostname = new_name;
lease->fqdn = new_fqdn;
lease->auth_name = auth;
@@ -506,6 +531,13 @@ int do_script_run(time_t now)
{
struct dhcp_lease *lease;
#ifdef HAVE_DBUS
/* If we're going to be sending DBus signals, but the connection is not yet up,
delay everything until it is. */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
return 0;
#endif
if (old_leases)
{
lease = old_leases;
@@ -513,7 +545,7 @@ int do_script_run(time_t now)
/* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname)
{
#ifndef NO_FORK
#ifdef HAVE_SCRIPT
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname);
@@ -522,16 +554,18 @@ int do_script_run(time_t now)
}
else
{
#ifndef NO_FORK
queue_script(ACTION_DEL, lease, lease->hostname, now);
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
#endif
#ifdef HAVE_DBUS
emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname);
#endif
old_leases = lease->next;
free(lease->hostname);
free(lease->fqdn);
free(lease->old_hostname);
free(lease->clid);
free(lease->vendorclass);
free(lease->userclass);
free(lease->extradata);
free(lease);
return 1;
@@ -542,7 +576,7 @@ int do_script_run(time_t now)
for (lease = leases; lease; lease = lease->next)
if (lease->old_hostname)
{
#ifndef NO_FORK
#ifdef HAVE_SCRIPT
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname);
@@ -554,23 +588,27 @@ int do_script_run(time_t now)
if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
{
#ifndef NO_FORK
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
#ifdef HAVE_SCRIPT
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname, now);
#endif
#ifdef HAVE_DBUS
emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname);
#endif
lease->new = lease->changed = lease->aux_changed = 0;
/* these are used for the "add" call, then junked, since they're not in the database */
free(lease->vendorclass);
lease->vendorclass = NULL;
/* this is used for the "add" call, then junked, since they're not in the database */
free(lease->extradata);
lease->extradata = NULL;
free(lease->userclass);
lease->userclass = NULL;
return 1;
}
return 0; /* nothing to do */
}
#endif

139
src/log.c
View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -30,7 +30,8 @@
/* defaults in case we die() before we log_start() */
static int log_fac = LOG_DAEMON;
static int log_stderr = 0;
static int log_stderr = 0;
static int echo_stderr = 0;
static int log_fd = -1;
static int log_to_file = 0;
static int entries_alloced = 0;
@@ -50,9 +51,11 @@ static struct log_entry *entries = NULL;
static struct log_entry *free_entries = NULL;
void log_start(struct passwd *ent_pw)
int log_start(struct passwd *ent_pw, int errfd)
{
log_stderr = !!(daemon->options & OPT_DEBUG);
int ret = 0;
echo_stderr = !!(daemon->options & OPT_DEBUG);
if (daemon->log_fac != -1)
log_fac = daemon->log_fac;
@@ -65,13 +68,22 @@ void log_start(struct passwd *ent_pw)
{
log_to_file = 1;
daemon->max_logs = 0;
if (strcmp(daemon->log_file, "-") == 0)
{
log_stderr = 1;
echo_stderr = 0;
log_fd = dup(STDERR_FILENO);
}
}
max_logs = daemon->max_logs;
if (!log_reopen(daemon->log_file))
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
{
send_event(errfd, EVENT_LOG_ERR, errno);
_exit(0);
}
/* if queuing is inhibited, make sure we allocate
the one required buffer now. */
if (max_logs == 0)
@@ -85,43 +97,43 @@ void log_start(struct passwd *ent_pw)
change the ownership here so that the file is always owned by
the dnsmasq user. Then logrotate can just copy the owner.
Failure of the chown call is OK, (for instance when started as non-root) */
if (log_to_file && ent_pw && ent_pw->pw_uid != 0 && fchown(log_fd, ent_pw->pw_uid, -1) != 0)
my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), daemon->log_file, strerror(errno));
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0 &&
fchown(log_fd, ent_pw->pw_uid, -1) != 0)
ret = errno;
return ret;
}
int log_reopen(char *log_file)
{
if (log_fd != -1)
close(log_fd);
/* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
{
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
return log_fd != -1;
}
else
if (!log_stderr)
{
if (log_fd != -1)
close(log_fd);
/* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
else
{
#ifdef HAVE_SOLARIS_NETWORK
/* Solaris logging is "different", /dev/log is not unix-domain socket.
Just leave log_fd == -1 and use the vsyslog call for everything.... */
/* Solaris logging is "different", /dev/log is not unix-domain socket.
Just leave log_fd == -1 and use the vsyslog call for everything.... */
# define _PATH_LOG "" /* dummy */
log_fd = -1;
return 1;
#else
{
int flags;
log_fd = socket(AF_UNIX, connection_type, 0);
if (log_fd == -1)
return 0;
/* if max_logs is zero, leave the socket blocking */
if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
}
int flags;
log_fd = socket(AF_UNIX, connection_type, 0);
/* if max_logs is zero, leave the socket blocking */
if (log_fd != -1 && max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
#endif
return 1;
}
}
return log_fd != -1;
}
static void free_entry(void)
@@ -240,6 +252,10 @@ static void log_write(void)
}
}
/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
DNS, DHCP and TFTP services.
*/
void my_syslog(int priority, const char *format, ...)
{
va_list ap;
@@ -248,10 +264,23 @@ void my_syslog(int priority, const char *format, ...)
char *p;
size_t len;
pid_t pid = getpid();
char *func = "";
if (log_stderr)
if ((LOG_FACMASK & priority) == MS_TFTP)
func = "-tftp";
else if ((LOG_FACMASK & priority) == MS_DHCP)
func = "-dhcp";
#ifdef LOG_PRI
priority = LOG_PRI(priority);
#else
/* Solaris doesn't have LOG_PRI */
priority &= LOG_PRIMASK;
#endif
if (echo_stderr)
{
fprintf(stderr, "dnsmasq: ");
fprintf(stderr, "dnsmasq%s: ", func);
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
@@ -297,8 +326,9 @@ void my_syslog(int priority, const char *format, ...)
p = entry->payload;
if (!log_to_file)
p += sprintf(p, "<%d>", priority | log_fac);
p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, (int)pid);
p += sprintf(p, "%.15s dnsmasq%s[%d]: ", ctime(&time_now) + 4, func, (int)pid);
len = p - entry->payload;
va_start(ap, format);
len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
@@ -368,14 +398,19 @@ void check_log_writer(fd_set *set)
void flush_log(void)
{
/* block until queue empty */
if (log_fd != -1)
/* write until queue empty */
while (log_fd != -1)
{
int flags;
if ((flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags & ~O_NONBLOCK);
struct timespec waiter;
log_write();
close(log_fd);
if (!entries)
{
close(log_fd);
break;
}
waiter.tv_sec = 0;
waiter.tv_nsec = 1000000; /* 1 ms */
nanosleep(&waiter, NULL);
}
}
@@ -386,11 +421,13 @@ void die(char *message, char *arg1, int exit_code)
if (!arg1)
arg1 = errmess;
log_stderr = 1; /* print as well as log when we die.... */
fputc('\n', stderr); /* prettyfy startup-script message */
if (!log_stderr)
{
echo_stderr = 1; /* print as well as log when we die.... */
fputc('\n', stderr); /* prettyfy startup-script message */
}
my_syslog(LOG_CRIT, message, arg1, errmess);
log_stderr = 0;
echo_stderr = 0;
my_syslog(LOG_CRIT, _("FAILED to start up"));
flush_log();

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -31,6 +31,7 @@
#endif
static struct iovec iov;
static u32 netlink_pid;
static void nl_err(struct nlmsghdr *h);
static void nl_routechange(struct nlmsghdr *h);
@@ -38,6 +39,7 @@ static void nl_routechange(struct nlmsghdr *h);
void netlink_init(void)
{
struct sockaddr_nl addr;
socklen_t slen = sizeof(addr);
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
@@ -59,48 +61,63 @@ void netlink_init(void)
}
}
if (daemon->netlinkfd == -1)
if (daemon->netlinkfd == -1 ||
getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == 1)
die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
/* save pid assigned by bind() and retrieved by getsockname() */
netlink_pid = addr.nl_pid;
iov.iov_len = 200;
iov.iov_len = 100;
iov.iov_base = safe_malloc(iov.iov_len);
}
static ssize_t netlink_recv(void)
{
struct msghdr msg;
struct sockaddr_nl nladdr;
ssize_t rc;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
while (1)
{
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && errno == EINTR);
/* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a
big buffer and pray in that case. */
if (rc == -1 && errno == EOPNOTSUPP)
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
/* make buffer big enough */
if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
{
if (!expand_buf(&iov, 2000))
return -1;
break;
/* Very new Linux kernels return the actual size needed, older ones always return truncated size */
if ((size_t)rc == iov.iov_len)
{
if (expand_buf(&iov, rc + 100))
continue;
}
else
expand_buf(&iov, rc);
}
if (rc == -1 || !(msg.msg_flags & MSG_TRUNC))
break;
if (!expand_buf(&iov, iov.iov_len + 100))
return -1;
}
/* finally, read it for real */
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
/* read it for real */
msg.msg_flags = 0;
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
/* Make sure this is from the kernel */
if (rc == -1 || nladdr.nl_pid == 0)
break;
}
/* discard stuff which is truncated at this point (expand_buf() may fail) */
if (msg.msg_flags & MSG_TRUNC)
{
rc = -1;
errno = ENOMEM;
}
return rc;
}
@@ -141,13 +158,20 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
while (1)
{
if ((len = netlink_recv()) == -1)
return 0;
{
if (errno == ENOBUFS)
{
sleep(1);
goto again;
}
return 0;
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else if (h->nlmsg_seq != seq)
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
nl_routechange(h); /* May be multicast arriving async */
else if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else if (h->nlmsg_type == NLMSG_DONE)
{
#ifdef HAVE_IPV6
@@ -208,10 +232,17 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
}
}
void netlink_multicast(void)
{
ssize_t len;
struct nlmsghdr *h;
int flags;
/* don't risk blocking reading netlink messages here. */
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
return;
if ((len = netlink_recv()) != -1)
{
@@ -221,11 +252,15 @@ void netlink_multicast(void)
else
nl_routechange(h);
}
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
}
static void nl_err(struct nlmsghdr *h)
{
struct nlmsgerr *err = NLMSG_DATA(h);
if (err->error != 0)
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
}
@@ -234,18 +269,35 @@ static void nl_err(struct nlmsghdr *h)
If this happens and we still have a DNS packet in the buffer, we re-send it.
This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. */
failing. Note that we only accept these messages from the kernel (pid == 0) */
static void nl_routechange(struct nlmsghdr *h)
{
if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
{
struct rtmsg *rtm = NLMSG_DATA(h);
if (rtm->rtm_type == RTN_UNICAST &&
rtm->rtm_scope == RT_SCOPE_LINK)
while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
int fd;
if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
return;
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save)
{
if (daemon->srv_save->sfd)
fd = daemon->srv_save->sfd->fd;
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
fd = daemon->rfd_save->fd;
else
return;
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,60 +10,66 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
int iface_check(int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp)
#ifdef HAVE_LINUX_NETWORK
int indextoname(int fd, int index, char *name)
{
struct ifreq ifr;
if (index == 0)
return 0;
ifr.ifr_ifindex = index;
if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
return 0;
strncpy(name, ifr.ifr_name, IF_NAMESIZE);
return 1;
}
#else
int indextoname(int fd, int index, char *name)
{
if (index == 0 || !if_indextoname(index, name))
return 0;
return 1;
}
#endif
int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
{
struct iname *tmp;
int ret = 1;
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (indexp)
{
#if defined(__FreeBSD__) || defined(__DragonFly__)
/* One form of bridging on FreeBSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(ifr->ifr_name, alias->iface, IF_NAMESIZE) == 0)
{
int newindex;
if (!(newindex = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr->ifr_name);
return 0;
}
else
{
*indexp = newindex;
strncpy(ifr->ifr_name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
#endif
}
if (daemon->if_names || (addr && daemon->if_addrs))
{
#ifdef HAVE_DHCP
struct dhcp_context *range;
#endif
ret = 0;
#ifdef HAVE_DHCP
for (range = daemon->dhcp; range; range = range->next)
if (range->interface && strcmp(range->interface, name) == 0)
ret = 1;
#endif
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr->ifr_name) == 0))
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
@@ -82,9 +88,40 @@ int iface_check(int family, struct all_addr *addr,
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr->ifr_name) == 0))
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = 0;
if (indexp)
{
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(name, alias->iface, IF_NAMESIZE) == 0)
{
int newindex;
if (!(newindex = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
return 0;
}
else
{
*indexp = newindex;
strncpy(name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
}
return ret;
}
@@ -92,27 +129,22 @@ static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask)
{
struct irec *iface;
int fd;
int fd, mtu = 0, loopback;
struct ifreq ifr;
int dhcp_ok = 1;
int tftp_ok = daemon->tftp_unlimited;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
struct interface_list *ir = NULL;
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
return 1;
#ifdef HAVE_LINUX_NETWORK
ifr.ifr_ifindex = if_index;
#endif
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
#ifdef HAVE_LINUX_NETWORK
ioctl(fd, SIOCGIFNAME, &ifr) == -1 ||
#else
!if_indextoname(if_index, ifr.ifr_name) ||
#endif
!indextoname(fd, if_index, ifr.ifr_name) ||
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
{
if (fd != -1)
@@ -123,12 +155,17 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
return 0;
}
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
close(fd);
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if (daemon->if_names && (ifr.ifr_flags & IFF_LOOPBACK))
if (daemon->if_names && loopback)
{
struct iname *lo;
for (lo = daemon->if_names; lo; lo = lo->next)
@@ -149,26 +186,45 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
}
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, &ifr, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
dhcp_ok = 0;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, &ifr, NULL))
return 1;
#ifdef HAVE_TFTP
/* implement wierd TFTP service rules */
if (addr->sa.sa_family == AF_INET)
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, ifr.ifr_name) == 0)
{
tftp_ok = 1;
break;
}
#endif
if (!ir)
{
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
return 1;
#ifdef HAVE_DHCP
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
tftp_ok = 0;
#endif
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
return 1;
#endif
}
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
{
iface->addr = *addr;
iface->netmask = netmask;
iface->dhcp_ok = dhcp_ok;
iface->tftp_ok = tftp_ok;
iface->mtu = mtu;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
@@ -217,7 +273,6 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
}
int enumerate_interfaces(void)
{
#ifdef HAVE_IPV6
@@ -270,16 +325,35 @@ static int create_ipv6_listener(struct listener **link, int port)
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
!fix_fd(fd) ||
!fix_fd(tcpfd) ||
#ifdef IPV6_RECVPKTINFO
setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1 ||
#else
setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
#endif
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
listen(tcpfd, 5) == -1 ||
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return 0;
/* The API changed around Linux 2.6.14 but the old ABI is still supported:
handle all combinations of headers and kernel.
OpenWrt note that this fixes the problem addressed by your very broken patch. */
daemon->v6pktinfo = IPV6_PKTINFO;
#ifdef IPV6_RECVPKTINFO
# ifdef IPV6_2292PKTINFO
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
{
if (errno == ENOPROTOOPT && setsockopt(fd, IPV6_LEVEL, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
daemon->v6pktinfo = IPV6_2292PKTINFO;
else
return 0;
}
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
return 0;
# endif
#else
if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1)
return 0;
#endif
l = safe_malloc(sizeof(struct listener));
l->fd = fd;
l->tcpfd = tcpfd;
@@ -334,7 +408,7 @@ struct listener *create_wildcard_listeners(void)
}
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -366,8 +440,11 @@ struct listener *create_bound_listeners(void)
{
struct listener *listeners = NULL;
struct irec *iface;
int opt = 1;
int rc, opt = 1;
#ifdef HAVE_IPV6
static int dad_count = 0;
#endif
for (iface = daemon->interfaces; iface; iface = iface->next)
{
struct listener *new = safe_malloc(sizeof(struct listener));
@@ -377,6 +454,7 @@ struct listener *create_bound_listeners(void)
new->tftpfd = -1;
new->tcpfd = -1;
new->fd = -1;
listeners = new;
if (daemon->port != 0)
{
@@ -396,32 +474,39 @@ struct listener *create_bound_listeners(void)
die(_("failed to set IPV6 options on listening socket: %s"), NULL, EC_BADNET);
}
#endif
if (bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
while(1)
{
if ((rc = bind(new->fd, &iface->addr.sa, sa_len(&iface->addr))) != -1)
break;
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6 && (errno == ENODEV || errno == EADDRNOTAVAIL))
/* An interface may have an IPv6 address which is still undergoing DAD.
If so, the bind will fail until the DAD completes, so we try over 20 seconds
before failing. */
if (iface->addr.sa.sa_family == AF_INET6 && (errno == ENODEV || errno == EADDRNOTAVAIL) &&
dad_count++ < DAD_WAIT)
{
close(new->tcpfd);
close(new->fd);
free(new);
new = NULL;
sleep(1);
continue;
}
else
#endif
{
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
break;
}
else if (listen(new->tcpfd, 5) == -1)
if (rc == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
{
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
}
#ifdef HAVE_TFTP
if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
if (iface->addr.sa.sa_family == AF_INET && iface->tftp_ok)
{
short save = iface->addr.in.sin_port;
iface->addr.in.sin_port = htons(TFTP_PORT);
@@ -434,13 +519,70 @@ struct listener *create_bound_listeners(void)
}
#endif
if (new)
listeners = new;
}
return listeners;
}
/* return a UDP socket bound to a random port, have to cope with straying into
occupied port nos and reserved ones. */
int random_sock(int family)
{
int fd;
if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
{
union mysockaddr addr;
unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port;
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
memset(&addr, 0, sizeof(addr));
addr.sa.sa_family = family;
/* don't loop forever if all ports in use. */
if (fix_fd(fd))
while(tries--)
{
unsigned short port = rand16();
if (daemon->min_port != 0)
port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));
if (family == AF_INET)
{
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = port;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
}
#ifdef HAVE_IPV6
else
{
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = port;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(struct sockaddr_in6);
#endif
}
#endif
if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
return fd;
if (errno != EADDRINUSE && errno != EACCES)
break;
}
close(fd);
}
return -1;
}
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
{
union mysockaddr addr_copy = *addr;
@@ -460,8 +602,8 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
return 0;
#if defined(SO_BINDTODEVICE)
if (strlen(intname) != 0 &&
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, sizeof(intname)) == -1)
if (intname[0] != 0 &&
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1)
return 0;
#endif
@@ -473,6 +615,25 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
struct serverfd *sfd;
int errsave;
/* when using random ports, servers which would otherwise use
the INADDR_ANY/port0 socket have sfd set to NULL */
if (!daemon->osport && intname[0] == 0)
{
errno = 0;
if (addr->sa.sa_family == AF_INET &&
addr->in.sin_addr.s_addr == INADDR_ANY &&
addr->in.sin_port == htons(0))
return NULL;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
addr->in6.sin6_port == htons(0))
return NULL;
#endif
}
/* may have a suitable one already */
for (sfd = daemon->sfds; sfd; sfd = sfd->next )
if (sockaddr_isequal(&sfd->source_addr, addr) &&
@@ -536,12 +697,13 @@ void pre_allocate_sfds(void)
}
for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
!allocate_sfd(&srv->source_addr, srv->interface) &&
errno != 0 &&
(daemon->options & OPT_NOWILD))
{
prettyprint_addr(&srv->addr, daemon->namebuff);
if (strlen(srv->interface) != 0)
prettyprint_addr(&srv->source_addr, daemon->namebuff);
if (srv->interface[0] != 0)
{
strcat(daemon->namebuff, " ");
strcat(daemon->namebuff, srv->interface);
@@ -558,11 +720,15 @@ void check_servers(void)
struct server *new, *tmp, *ret = NULL;
int port = 0;
/* interface may be new since startup */
if (!(daemon->options & OPT_NOWILD))
enumerate_interfaces();
for (new = daemon->servers; new; new = tmp)
{
tmp = new->next;
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)))
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
@@ -585,7 +751,9 @@ void check_servers(void)
}
/* Do we need a socket set? */
if (!new->sfd && !(new->sfd = allocate_sfd(&new->source_addr, new->interface)))
if (!new->sfd &&
!(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
errno != 0)
{
my_syslog(LOG_WARNING,
_("ignoring nameserver %s - cannot make/bind socket: %s"),
@@ -599,25 +767,30 @@ void check_servers(void)
new->next = ret;
ret = new;
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS))
if (!(new->flags & SERV_NO_REBIND))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (new->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
}
else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else
s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
else if (strlen(new->interface) != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
daemon->servers = ret;
@@ -744,16 +917,19 @@ struct in_addr get_ifaddr(char *intr)
{
struct listener *l;
struct ifreq ifr;
struct sockaddr_in ret;
ret.sin_addr.s_addr = -1;
for (l = daemon->listeners; l && l->family != AF_INET; l = l->next);
strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
if (!l || ioctl(l->fd, SIOCGIFADDR, &ifr) == -1)
((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = -1;
if (l && ioctl(l->fd, SIOCGIFADDR, &ifr) != -1)
memcpy(&ret, &ifr.ifr_addr, sizeof(ret));
return ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
return ret.sin_addr;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,8 +10,8 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
@@ -21,8 +21,14 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp,
unsigned long ttl, unsigned int *offset, unsigned short type,
unsigned short class, char *format, ...);
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
char *name, int isExtract)
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0;
@@ -31,19 +37,47 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
if (isExtract)
*cp = 0;
while ((l = *p++))
{
unsigned int label_type = l & 0xc0;
while (1)
{
unsigned int label_type;
if (!CHECK_LEN(header, p, plen, 1))
return 0;
if ((l = *p++) == 0)
/* end marker */
{
/* check that there are the correct no of bytes after the name */
if (!CHECK_LEN(header, p, plen, extrabytes))
return 0;
if (isExtract)
{
if (cp != (unsigned char *)name)
cp--;
*cp = 0; /* terminate: lose final period */
}
else if (*cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
return retvalue;
}
label_type = l & 0xc0;
if (label_type == 0xc0) /* pointer */
{
if ((size_t)(p - (unsigned char *)header) >= plen)
if (!CHECK_LEN(header, p, plen, 1))
return 0;
/* get offset */
l = (l&0x3f) << 8;
l |= *p++;
if (l >= plen)
return 0;
if (!p1) /* first jump, save location to go back to */
p1 = p;
@@ -74,7 +108,7 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
/* output is \[x<hex>/siz]. which is digs+9 chars */
if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
return 0;
if ((size_t)(p - (unsigned char *)header + ((count-1)>>3)) >= plen)
if (!CHECK_LEN(header, p, plen, (count-1)>>3))
return 0;
*cp++ = '\\';
@@ -98,12 +132,14 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
{ /* label_type = 0 -> label. */
if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
return 0;
if ((size_t)(p - (unsigned char *)header) >= plen)
if (!CHECK_LEN(header, p, plen, l))
return 0;
for(j=0; j<l; j++, p++)
if (isExtract)
{
if (legal_char(*p))
unsigned char c = *p;
if (isascii(c) && !iscntrl(c) && c != '.')
*cp++ = *p;
else
return 0;
@@ -132,26 +168,7 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
else if (*cp != 0 && *cp++ != '.')
retvalue = 2;
}
if ((unsigned int)(p - (unsigned char *)header) >= plen)
return 0;
}
if (isExtract)
{
if (cp != (unsigned char *)name)
cp--;
*cp = 0; /* terminate: lose final period */
}
else if (*cp != 0)
retvalue = 2;
if (p1) /* we jumped via compression */
*pp = p1;
else
*pp = p;
return retvalue;
}
/* Max size of input string (for IPv6) is 75 chars.) */
@@ -227,7 +244,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
if (*name == '\\' && *(name+1) == '[' &&
(*(name+2) == 'x' || *(name+2) == 'X'))
{
for (j = 0, cp1 = name+3; *cp1 && isxdigit(*cp1) && j < 32; cp1++, j++)
for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
{
char xdig[2];
xdig[0] = *cp1;
@@ -261,15 +278,17 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
return 0;
}
static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen)
static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
{
while(1)
{
unsigned int label_type = (*ansp) & 0xc0;
unsigned int label_type;
if ((unsigned int)(ansp - (unsigned char *)header) >= plen)
if (!CHECK_LEN(header, ansp, plen, 1))
return NULL;
label_type = (*ansp) & 0xc0;
if (label_type == 0xc0)
{
/* pointer for compression. */
@@ -283,6 +302,9 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
/* Extended label type */
unsigned int count;
if (!CHECK_LEN(header, ansp, plen, 2))
return NULL;
if (((*ansp++) & 0x3f) != 1)
return NULL; /* we only understand bitstrings */
@@ -296,12 +318,17 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
else
{ /* label type == 0 Bottom six bits is length */
unsigned int len = (*ansp++) & 0x3f;
if (!ADD_RDLEN(header, ansp, plen, len))
return NULL;
if (len == 0)
break; /* zero length label marks the end. */
ansp += len;
}
}
if (!CHECK_LEN(header, ansp, plen, extrabytes))
return NULL;
return ansp;
}
@@ -313,12 +340,10 @@ static unsigned char *skip_questions(HEADER *header, size_t plen)
for (q = ntohs(header->qdcount); q != 0; q--)
{
if (!(ansp = skip_name(ansp, header, plen)))
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
ansp += 4; /* class and type */
}
if ((unsigned int)(ansp - (unsigned char *)header) > plen)
return NULL;
return ansp;
}
@@ -329,13 +354,12 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
for (i = 0; i < count; i++)
{
if (!(ansp = skip_name(ansp, header, plen)))
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
ansp += 8; /* type, class, TTL */
GETSHORT(rdlen, ansp);
if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen)
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
ansp += rdlen;
}
return ansp;
@@ -355,7 +379,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
for (q = ntohs(header->qdcount); q != 0; q--)
{
if (!extract_name(header, plen, &p, name, 1))
if (!extract_name(header, plen, &p, name, 1, 4))
return crc; /* bad packet */
for (p1 = (unsigned char *)name; *p1; p1++)
@@ -381,7 +405,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
}
p += 4;
if ((unsigned int)(p - (unsigned char *)header) > plen)
if (!CHECK_LEN(header, p, plen, 0))
return crc; /* bad packet */
}
@@ -393,12 +417,13 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
{
unsigned char *ansp = skip_questions(header, plen);
/* if packet is malformed, just return as-is. */
if (!ansp)
return 0;
return plen;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen)))
return 0;
return plen;
/* restore pseudoheader */
if (pheader && ntohs(header->arcount) == 0)
@@ -432,7 +457,7 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
if (!(ansp = skip_name(ansp, header, plen)))
if (!(ansp = skip_name(ansp, header, plen, 4)))
return NULL;
GETSHORT(type, ansp);
@@ -458,7 +483,7 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
for (i = 0; i < arcount; i++)
{
unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen)))
if (!(ansp = skip_name(ansp, header, plen, 10)))
return NULL;
GETSHORT(type, ansp);
@@ -466,9 +491,8 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if ((size_t)(ansp + rdlen - (unsigned char *)header) > plen)
if (!ADD_RDLEN(header, ansp, plen, rdlen))
return NULL;
ansp += rdlen;
if (type == T_OPT)
{
if (len)
@@ -489,26 +513,31 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
/* is addr in the non-globally-routed IP space? */
static int private_net(struct in_addr addr)
static int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen, char *name)
{
int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--)
{
if (!(p = skip_name(p, header, qlen)))
if (name && (daemon->options & OPT_LOG))
{
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0;
}
else if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -516,36 +545,69 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A))
if (qclass == C_IN && qtype == T_A)
{
struct doctor *doctor;
struct in_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
/* alignment */
memcpy(&addr, p, INADDRSZ);
for (doctor = daemon->doctors; 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);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
memcpy(p, &addr, INADDRSZ);
break;
}
{
if (doctor->end.s_addr == 0)
{
if (!is_same_net(doctor->in, addr, doctor->mask))
continue;
}
else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
continue;
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
memcpy(p, &addr, INADDRSZ);
break;
}
}
else if (qtype == T_TXT && name && (daemon->options & OPT_LOG))
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, rdlen))
return 0;
while ((p1 - p) < rdlen)
{
unsigned int i, len = *p1;
unsigned char *p2 = p1;
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
if (isprint(*(p2+1)))
{
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_DEBUG, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, len);
*p1 = len;
p1 += len+1;
}
}
p += rdlen;
if ((size_t)(p - (unsigned char *)header) > qlen)
return 0; /* bad packet */
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
}
return p;
}
static int find_soa(HEADER *header, size_t qlen)
static int find_soa(HEADER *header, size_t qlen, char *name)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -554,12 +616,12 @@ static int find_soa(HEADER *header, size_t qlen)
/* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--)
{
if (!(p = skip_name(p, header, qlen)))
if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -574,10 +636,10 @@ static int find_soa(HEADER *header, size_t qlen)
minttl = ttl;
/* MNAME */
if (!(p = skip_name(p, header, qlen)))
if (!(p = skip_name(p, header, qlen, 0)))
return 0;
/* RNAME */
if (!(p = skip_name(p, header, qlen)))
if (!(p = skip_name(p, header, qlen, 20)))
return 0;
p += 16; /* SERIAL REFRESH RETRY EXPIRE */
@@ -585,15 +647,12 @@ static int find_soa(HEADER *header, size_t qlen)
if (ttl < minttl)
minttl = ttl;
}
else
p += rdlen;
if ((size_t)(p - (unsigned char *)header) > qlen)
else if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
}
/* rewrite addresses in additioal section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen))
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
return 0;
if (!found_soa)
@@ -605,8 +664,8 @@ static int find_soa(HEADER *header, size_t qlen)
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int is_sign, int check_rebind)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -615,11 +674,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
cache_start_insert();
/* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
if (daemon->doctors)
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || (daemon->options & OPT_LOG))
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, name);
}
/* go through the questions. */
@@ -633,7 +692,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
unsigned long cttl = ULONG_MAX, attl;
namep = p;
if (!extract_name(header, qlen, &p, name, 1))
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -661,13 +720,18 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
{
unsigned char *tmp = namep;
/* the loop body overwrites the original name, so get it back here. */
if (!extract_name(header, qlen, &tmp, name, 1) ||
!(res = extract_name(header, qlen, &p1, name, 0)))
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -677,7 +741,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{
if (!extract_name(header, qlen, &p1, name, 1))
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
if (aqtype == T_CNAME)
@@ -692,7 +756,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
}
p1 = endrr;
if ((size_t)(p1 - (unsigned char *)header) > qlen)
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
}
}
@@ -702,7 +766,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
@@ -737,12 +801,17 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
for (j = ntohs(header->ancount); j != 0; j--)
{
if (!(res = extract_name(header, qlen, &p1, name, 0)))
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
return 0; /* bad packet */
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -763,20 +832,23 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (attl < cttl)
cttl = attl;
if (!extract_name(header, qlen, &p1, name, 1))
if (!extract_name(header, qlen, &p1, name, 1, 0))
return 0;
goto cname_loop1;
}
else
{
found = 1;
/* copy address into aligned storage */
if (!CHECK_LEN(header, p1, qlen, addrlen))
return 0; /* bad packet */
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if ((daemon->options & OPT_NO_REBIND) &&
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4))
private_net(addr.addr.addr4, !(daemon->options & OPT_LOCAL_REBIND)))
return 1;
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
@@ -790,7 +862,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
}
p1 = endrr;
if ((size_t)(p1 - (unsigned char *)header) > qlen)
if (!CHECK_LEN(header, p1, qlen, 0))
return 0; /* bad packet */
}
}
@@ -800,7 +872,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
@@ -839,7 +911,7 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
return 0; /* must be exactly one query. */
if (!extract_name(header, qlen, &p, name, 1))
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -953,7 +1025,7 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
for (i = ntohs(header->ancount); i != 0; i--)
{
if (!extract_name(header, qlen, &p, name, 1))
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -962,19 +1034,25 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A)
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
{
/* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */
cache_start_insert();
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
cache_end_insert();
return 1;
}
{
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
for (baddrp = baddr; baddrp; baddrp = baddrp->next)
if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
{
/* Found a bogus address. Insert that info here, since there no SOA record
to get the ttl from in the normal processing */
cache_start_insert();
cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
cache_end_insert();
return 1;
}
}
p += rdlen;
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0;
}
return 0;
@@ -1044,6 +1122,16 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
memcpy(p, sval, usval);
p += usval;
break;
case 'z':
sval = va_arg(ap, char *);
usval = sval ? strlen(sval) : 0;
if (usval > 255)
usval = 255;
*p++ = (unsigned char)usval;
memcpy(p, sval, usval);
p += usval;
break;
}
va_end(ap); /* clean up variable argument pointer */
@@ -1063,6 +1151,22 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
return 1;
}
static unsigned long crec_ttl(struct crec *crecp, time_t now)
{
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
if (crecp->flags & (F_IMMORTAL | F_DHCP))
return daemon->local_ttl;
/* Return the Max TTL value if it is lower then the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
else
return daemon->max_ttl;
}
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(HEADER *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now)
@@ -1127,7 +1231,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
nameoffset = p - (unsigned char *)header;
/* now extract name as .-concatenated string into name */
if (!extract_name(header, qlen, &p, name, 1))
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -1145,7 +1249,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_TXT, t->class, "t", t->len, t->txt))
@@ -1184,7 +1288,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, 0, NULL, 0);
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
@@ -1196,7 +1300,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_BIGNAME, name, NULL, 0, NULL, 0);
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name) &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1220,7 +1324,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
@@ -1229,18 +1333,11 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
auth = 0;
if (!dryrun)
{
unsigned long ttl;
/* Return 0 ttl for DHCP entries, which might change
before the lease expires. */
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
record_source(crecp->uid));
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
0, daemon->addn_hosts, crecp->uid);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL,
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL,
T_PTR, C_IN, "d", cache_get_name(crecp)))
anscount++;
}
@@ -1248,14 +1345,14 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
else if (is_arpa == F_IPV4 &&
(daemon->options & OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4))
private_net(addr.addr.addr4, 1))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
nxdomain = 1;
if (!dryrun)
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN,
name, &addr, 0, NULL, 0);
name, &addr, NULL);
}
}
@@ -1273,18 +1370,41 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype != type && qtype != T_ANY)
continue;
/* Check for "A for A" queries */
if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
/* Check for "A for A" queries; be rather conservative
about what looks like dotted-quad. */
if (qtype == T_A)
{
ans = 1;
if (!dryrun)
char *cp;
unsigned int i, a;
int x;
for (cp = name, i = 0, a = 0; *cp; i++)
{
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, 0, NULL, 0);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
if (!isdigit(*cp) || (x = strtol(cp, &cp, 10)) > 255)
{
i = 5;
break;
}
a = (a << 8) + x;
if (*cp == '.')
cp++;
}
if (i == 4)
{
ans = 1;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
}
continue;
}
continue;
}
/* interface name stuff */
@@ -1302,10 +1422,10 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, 0, NULL, 0);
log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
else
{
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, 0, NULL, 0);
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
@@ -1347,8 +1467,9 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
{
if (!dryrun)
{
log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset,
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
anscount++;
}
@@ -1364,7 +1485,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
if (!dryrun)
log_query(crecp->flags, name, NULL, 0, NULL, 0);
log_query(crecp->flags, name, NULL, NULL);
}
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
{
@@ -1381,17 +1502,11 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
unsigned long ttl;
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
0, daemon->addn_hosts, crecp->uid);
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN,
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
type == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
@@ -1410,7 +1525,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
{
@@ -1427,7 +1542,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_MX, C_IN, "sd", 1,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
@@ -1447,7 +1562,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0);
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
@@ -1463,9 +1578,27 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
{
ans = 1;
if (!dryrun)
log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0);
log_query(F_CONFIG | F_NEG, name, NULL, NULL);
}
}
if (qtype == T_NAPTR || qtype == T_ANY)
{
struct naptr *na;
for (na = daemon->naptr; na; na = na->next)
if (hostname_isequal(name, na->name))
{
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
NULL, T_NAPTR, C_IN, "sszzzd",
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
anscount++;
}
}
}
if (qtype == T_MAILB)
ans = 1, nxdomain = 1;
@@ -1474,7 +1607,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
{
ans = 1;
if (!dryrun)
log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
log_query(F_CONFIG | F_NEG, name, &addr, NULL);
}
}
@@ -1501,7 +1634,6 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
{
unsigned long ttl;
#ifdef HAVE_IPV6
int type = crecp->flags & F_IPV4 ? T_A : T_AAAA;
#else
@@ -1510,12 +1642,8 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (crecp->flags & F_NEG)
continue;
if (crecp->flags & (F_IMMORTAL | F_DHCP))
ttl = daemon->local_ttl;
else
ttl = crecp->ttd - now;
if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN,
if (add_resource_record(header, limit, NULL, rec->offset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
addncount++;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -10,15 +10,15 @@
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_TFTP
static struct tftp_file *check_tftp_fileperm(ssize_t *len);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err_oops(char *packet, char *file);
@@ -45,24 +45,30 @@ void tftp_request(struct listener *listen, time_t now)
char *filename, *mode, *p, *end, *opt;
struct sockaddr_in addr, peer;
struct msghdr msg;
struct cmsghdr *cmptr;
struct iovec iov;
struct ifreq ifr;
int is_err = 1, if_index = 0;
int is_err = 1, if_index = 0, mtu = 0, special = 0;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
struct tftp_transfer *transfer;
int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtu = IP_PMTUDISC_DONT;
int mtuflag = IP_PMTUDISC_DONT;
#endif
char namebuff[IF_NAMESIZE];
char *name;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(HAVE_SOLARIS_NETWORK)
char control[CMSG_SPACE(sizeof(unsigned int))];
#else
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
@@ -85,52 +91,104 @@ void tftp_request(struct listener *listen, time_t now)
return;
if (daemon->options & OPT_NOWILD)
addr = listen->iface->addr.in;
{
addr = listen->iface->addr.in;
mtu = listen->iface->mtu;
name = listen->iface->name;
}
else
{
struct cmsghdr *cmptr;
int check;
struct interface_list *ir;
addr.sin_addr.s_addr = 0;
#if defined(HAVE_LINUX_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
if (!(ifr.ifr_ifindex = if_index) ||
ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1)
return;
#elif defined(HAVE_SOLARIS_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
union {
unsigned char *c;
struct in_addr *a;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *(p.i);
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *((unsigned int *)CMSG_DATA(cmptr));
#else
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
{
union {
unsigned char *c;
struct in_addr *a;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = p.s->sdl_index;
}
#endif
if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
if (!indextoname(listen->tftpfd, if_index, namebuff) ||
addr.sin_addr.s_addr == 0)
return;
name = namebuff;
check = iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index);
/* wierd TFTP service override */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
break;
if (!ir)
{
if (!daemon->tftp_unlimited || !check)
return;
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return;
#endif
if (addr.sin_addr.s_addr == 0)
return;
if (!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr,
&ifr, &if_index))
return;
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
}
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
/* wierd TFTP interfaces disable special options. */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
special = 1;
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -151,15 +209,17 @@ void tftp_request(struct listener *listen, time_t now)
transfer->backoff = 1;
transfer->block = 1;
transfer->blocksize = 512;
transfer->offset = 0;
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
transfer->netascii = transfer->carrylf = 0;
/* if we have a nailed-down range, iterate until we find a free one. */
while (1)
{
if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
!fix_fd(transfer->sockfd))
{
@@ -170,7 +230,7 @@ void tftp_request(struct listener *listen, time_t now)
addr.sin_port = htons(port);
continue;
}
my_syslog(LOG_ERR, _("unable to get free port for TFTP"));
my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
}
free_transfer(transfer);
return;
@@ -184,48 +244,59 @@ void tftp_request(struct listener *listen, time_t now)
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
strcasecmp(mode, "octet") != 0)
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
else
{
if (strcasecmp(mode, "netascii") == 0)
transfer->netascii = 1;
while ((opt = next(&p, end)))
{
if (strcasecmp(opt, "blksize") == 0 &&
(opt = next(&p, end)) &&
!(daemon->options & OPT_TFTP_NOBLOCK))
if (strcasecmp(opt, "blksize") == 0)
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
transfer->opt_blocksize = 1;
transfer->block = 0;
if ((opt = next(&p, end)) &&
(special || !(daemon->options & OPT_TFTP_NOBLOCK)))
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
/* 32 bytes for IP, UDP and TFTP headers */
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
transfer->blocksize = (unsigned)mtu - 32;
transfer->opt_blocksize = 1;
transfer->block = 0;
}
}
if (strcasecmp(opt, "tsize") == 0 && next(&p, end))
else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
{
transfer->opt_transize = 1;
transfer->block = 0;
}
}
strcpy(daemon->namebuff, "/");
if (daemon->tftp_prefix)
{
if (daemon->tftp_prefix[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
strncat(daemon->namebuff, "/", MAXDNAME);
/* cope with backslashes from windows boxen. */
while ((p = strchr(filename, '\\')))
*p = '/';
if (daemon->options & OPT_TFTP_APREF)
strcpy(daemon->namebuff, "/");
if (prefix)
{
if (prefix[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
if (prefix[strlen(prefix)-1] != '/')
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
if (!special && (daemon->options & OPT_TFTP_APREF))
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), MAXDNAME);
strncat(daemon->namebuff, "/", MAXDNAME);
strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
/* remove unique-directory if it doesn't exist */
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
@@ -243,11 +314,10 @@ void tftp_request(struct listener *listen, time_t now)
}
else if (filename[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, filename, MAXDNAME);
daemon->namebuff[MAXDNAME-1] = 0;
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len)))
if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
{
if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
@@ -263,13 +333,13 @@ void tftp_request(struct listener *listen, time_t now)
free_transfer(transfer);
else
{
my_syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
transfer->next = daemon->tftp_trans;
daemon->tftp_trans = transfer;
}
}
static struct tftp_file *check_tftp_fileperm(ssize_t *len)
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file;
@@ -279,7 +349,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len)
int fd = -1;
/* trick to ban moving out of the subtree */
if (daemon->tftp_prefix && strstr(namebuff, "/../"))
if (prefix && strstr(namebuff, "/../"))
goto perm;
if ((fd = open(namebuff, O_RDONLY)) == -1)
@@ -306,7 +376,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len)
goto perm;
}
/* in secure mode, must be owned by user running dnsmasq */
else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
else if (!special && (daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
goto perm;
/* If we're doing many tranfers from the same file, only
@@ -378,7 +448,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
/* Got ack, ensure we take the (re)transmit path */
transfer->timeout = now;
transfer->backoff = 0;
transfer->block++;
if (transfer->block++ != 0)
transfer->offset += transfer->blocksize - transfer->expansion;
}
else if (ntohs(mess->op) == OP_ERR)
{
@@ -396,7 +467,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
*(q++) = *r;
*q = 0;
}
my_syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err,
inet_ntoa(transfer->peer.sin_addr));
@@ -427,7 +498,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (len != 0)
my_syslog(LOG_ERR, _("TFTP failed sending %s to %s"),
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
len = 0;
}
@@ -486,8 +557,7 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
mess->op = htons(OP_ERR);
mess->err = htons(err);
ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
if (err != ERR_FNF)
my_syslog(LOG_ERR, "TFTP %s", mess->message);
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message);
return ret;
}
@@ -532,24 +602,52 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
unsigned char data[];
} *mess = (struct datamess *)packet;
off_t offset = transfer->blocksize * (transfer->block - 1);
size_t size = transfer->file->size - offset;
size_t size = transfer->file->size - transfer->offset;
if (offset > transfer->file->size)
if (transfer->offset > transfer->file->size)
return 0; /* finished */
if (size > transfer->blocksize)
size = transfer->blocksize;
lseek(transfer->file->fd, offset, SEEK_SET);
mess->op = htons(OP_DATA);
mess->block = htons((unsigned short)(transfer->block));
if (!read_write(transfer->file->fd, mess->data, size, 1))
if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
!read_write(transfer->file->fd, mess->data, size, 1))
return -1;
else
return size + 4;
transfer->expansion = 0;
/* Map '\n' to CR-LF in netascii mode */
if (transfer->netascii)
{
size_t i;
int newcarrylf;
for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
{
if (size == transfer->blocksize)
{
transfer->expansion++;
if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
}
else
size++; /* room in this block */
/* make space and insert CR */
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
mess->data[i] = '\r';
i++;
}
transfer->carrylf = newcarrylf;
}
return size + 4;
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2010 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
@@ -9,12 +9,14 @@
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Some code in this file contributed by Rob Funk. */
/* The SURF random number generator was taken from djbdns-1.05, by
Daniel J Bernstein, which is public domain. */
#include "dnsmasq.h"
@@ -22,117 +24,184 @@
#include <sys/times.h>
#endif
/* Prefer arc4random(3) over random(3) over rand(3) */
/* Also prefer /dev/urandom over /dev/random, to preserve the entropy pool */
#ifdef HAVE_ARC4RANDOM
# define rand() arc4random()
# define srand(s) (void)0
# define RANDFILE (NULL)
#else
# ifdef HAVE_RANDOM
# define rand() random()
# define srand(s) srandom(s)
# endif
# ifdef HAVE_DEV_URANDOM
# define RANDFILE "/dev/urandom"
# else
# ifdef HAVE_DEV_RANDOM
# define RANDFILE "/dev/random"
# else
# define RANDFILE (NULL)
# endif
# endif
#ifdef LOCALEDIR
#include <idna.h>
#endif
#ifdef HAVE_ARC4RANDOM
void rand_init(void)
{
return;
}
unsigned short rand16(void)
{
static int been_seeded = 0;
const char *randfile = RANDFILE;
return (unsigned short) (arc4random() >> 15);
}
#else
/* SURF random number generator */
typedef unsigned int uint32;
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
void rand_init()
{
int fd = open(RANDFILE, O_RDONLY);
if (! been_seeded)
{
int fd, n = 0;
unsigned int c = 0, seed = 0, badseed;
char sbuf[sizeof(seed)];
char *s;
struct timeval now;
if (fd == -1 ||
!read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) ||
!read_write(fd, (unsigned char *)&in, sizeof(in), 1))
die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
close(fd);
}
/* get the bad seed as a backup */
/* (but we'd rather have something more random) */
gettimeofday(&now, NULL);
badseed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16);
fd = open(randfile, O_RDONLY);
if (fd < 0)
seed = badseed;
else
{
s = (char *) &seed;
while ((c < sizeof(seed)) &&
((n = read(fd, sbuf, sizeof(seed)) > 0)))
{
memcpy(s, sbuf, n);
s += n;
c += n;
}
if (n < 0)
seed = badseed;
close(fd);
}
#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
srand(seed);
been_seeded = 1;
static void surf(void)
{
uint32 t[12]; uint32 x; uint32 sum = 0;
int r; int i; int loop;
for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
for (i = 0;i < 8;++i) out[i] = seed[24 + i];
x = t[11];
for (loop = 0;loop < 2;++loop) {
for (r = 0;r < 16;++r) {
sum += 0x9e3779b9;
MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
}
/* Some rand() implementations have less randomness in low bits
* than in high bits, so we only pay attention to the high ones.
* But most implementations don't touch the high bit, so we
* ignore that one.
*/
return( (unsigned short) (rand() >> 15) );
for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
}
}
int legal_char(char c)
unsigned short rand16(void)
{
/* check for legal char a-z A-Z 0-9 -
(also / , used for RFC2317 and _ used in windows queries
and space, for DNS-SD stuff) */
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '/' || c == '_' || c == ' ')
return 1;
return 0;
static int outleft = 0;
if (!outleft) {
if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
surf();
outleft = 8;
}
return (unsigned short) out[--outleft];
}
int canonicalise(char *s)
#endif
static int check_name(char *in)
{
/* check for legal chars and remove trailing .
/* remove trailing .
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(s);
size_t dotgap = 0, l = strlen(in);
char c;
int nowhite = 0;
if (l == 0 || l > MAXDNAME) return 0;
if (s[l-1] == '.')
if (in[l-1] == '.')
{
if (l == 1) return 0;
s[l-1] = 0;
in[l-1] = 0;
}
while ((c = *s))
for (; (c = *in); in++)
{
if (c == '.')
dotgap = 0;
else if (!legal_char(c) || (++dotgap > MAXLABEL))
else if (++dotgap > MAXLABEL)
return 0;
else if (isascii(c) && iscntrl(c))
/* iscntrl only gives expected results for ascii */
return 0;
#ifndef LOCALEDIR
else if (!isascii(c))
return 0;
#endif
else if (c != ' ')
nowhite = 1;
s++;
}
return nowhite;
if (!nowhite)
return 0;
return 1;
}
/* Hostnames have a more limited valid charset than domain names
so check for legal char a-z A-Z 0-9 - _
Note that this may receive a FQDN, so only check the first label
for the tighter criteria. */
int legal_hostname(char *name)
{
char c;
if (!check_name(name))
return 0;
for (; (c = *name); name++)
/* check for legal char a-z A-Z 0-9 - _ . */
{
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '_')
continue;
/* end of hostname part */
if (c == '.')
return 1;
return 0;
}
return 1;
}
char *canonicalise(char *in, int *nomem)
{
char *ret = NULL;
#ifdef LOCALEDIR
int rc;
#endif
if (nomem)
*nomem = 0;
if (!check_name(in))
return NULL;
#ifdef LOCALEDIR
if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS)
{
if (ret)
free(ret);
if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
{
my_syslog(LOG_ERR, _("failed to allocate memory"));
*nomem = 1;
}
return NULL;
}
#else
if ((ret = whine_malloc(strlen(in)+1)))
strcpy(ret, in);
else if (nomem)
*nomem = 1;
#endif
return ret;
}
unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
@@ -162,6 +231,14 @@ void *safe_malloc(size_t size)
return ret;
}
void safe_pipe(int *fd, int read_noblock)
{
if (pipe(fd) == -1 ||
!fix_fd(fd[1]) ||
(read_noblock && !fix_fd(fd[0])))
die(_("cannot create pipe: %s"), NULL, EC_MISC);
}
void *whine_malloc(size_t size)
{
void *ret = malloc(size);
@@ -332,14 +409,19 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
return i;
}
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
{
int i;
for (i = len - 1; i >= 0; i--, mask = mask >> 1)
if (!(mask & 1) && a[i] != b[i])
return 0;
return 1;
int i, count;
for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1)
if (!(mask & 1))
{
if (a[i] == b[i])
count++;
else
return 0;
}
return count;
}
/* _note_ may copy buffer */
@@ -429,3 +511,4 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
}
return 1;
}