Compare commits

..

17 Commits
v2.41 ... v2.59

Author SHA1 Message Date
Simon Kelley
74c95c2542 import of dnsmasq-2.59.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
7de060b08d import of dnsmasq-2.58.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
572b41eb50 import of dnsmasq-2.57.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
28866e9567 import of dnsmasq-2.56.tar.gz 2012-01-05 17:31:15 +00:00
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
78 changed files with 23304 additions and 13927 deletions

1
.new Normal file
View File

@@ -0,0 +1 @@
shlibs:Depends=libc6 (>= 2.1)

3
Android.mk Normal file
View File

@@ -0,0 +1,3 @@
ifneq ($(TARGET_SIMULATOR),true)
include $(call all-subdir-makefiles)
endif

3434
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

50
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?
@@ -295,7 +303,7 @@ A: Yes, new releases of dnsmasq are always announced through
Q: What does the dhcp-authoritative option do?
A: See http://www.isc.org/index.pl?/sw/dhcp/authoritative.php - that's
A: See http://www.isc.org/files/auth.html - that's
for the ISC daemon, but the same applies to dnsmasq.
Q: Why does my Gentoo box pause for a minute before getting a new
@@ -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?
@@ -335,7 +354,7 @@ A: Yes, from version-2.21. The support is only available running under
If a physical interface has more than one IP address or aliases
with extra IP addresses, then any dhcp-ranges corresponding to
these addresses can be used for address allocation. So if an
interface has addresses 192.168.1.0/24 and 192.68.2.0/24 and there
interface has addresses 192.168.1.0/24 and 192.168.2.0/24 and there
are DHCP ranges 192.168.1.100-192.168.1.200 and
192.168.2.100-192.168.2.200 then both ranges would be used for host
connected to the physical interface. A more typical use might be to
@@ -362,7 +381,7 @@ A: Probably the nameserver is an authoritative nameserver for a
Q: Does the dnsmasq DHCP server probe addresses before allocating
them, as recommended in RFC2131?
A: Yes, dynmaically allocated IP addresses are checked by sending an
A: Yes, dynamically allocated IP addresses are checked by sending an
ICMP echo request (ping). If a reply is received, then dnsmasq
assumes that the address is in use, and attempts to allocate an
different address. The wait for a reply is between two and three
@@ -387,17 +406,18 @@ 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?
Q: Where can I get .rpms Suitable for openSUSE/SLES?
A: Dnsmasq is in Suse itself, and the latest releases are also
available at ftp://ftp.suse.com/pub/people/ug/
A: Dnsmasq is in openSUSE itself, and the latest releases are also
available at http://download.opensuse.org/repositories/network/
Q: Can I run dnsmasq in a Linux vserver?
@@ -437,6 +457,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-2011 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,45 @@
# 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
DBUS_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
DBUS_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
IDN_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
IDN_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
CT_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
CT_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
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 conntrack.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="$(DBUS_CFLAGS) $(IDN_CFLAGS) $(CT_CFLAGS)" \
BUILD_LIBS="$(DBUS_LIBS) $(IDN_LIBS) $(CT_LIBS) $(SUNOS_LIBS)" \
-f ../Makefile dnsmasq
clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
@@ -55,26 +62,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="$(DBUS_CFLAGS) $(CT_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DBUS_LIBS) $(CT_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

18
bld/Android.mk Normal file
View File

@@ -0,0 +1,18 @@
LOCAL_PATH := external/dnsmasq/src
#########################
include $(CLEAR_VARS)
LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
forward.c helper.c lease.c log.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c
LOCAL_MODULE := dnsmasq
LOCAL_C_INCLUDES := external/dnsmasq/src
LOCAL_CFLAGS := -O2 -g -W -Wall -D__ANDROID__ -DNO_IPV6 -DNO_TFTP -DNO_SCRIPT
LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
include $(BUILD_EXECUTABLE)

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,15 @@
<?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>
</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>

54
contrib/conntrack/README Normal file
View File

@@ -0,0 +1,54 @@
Linux iptables includes that ability to mark individual network packets
with a "firewall mark". Additionally there is a component called
"conntrack" which tries to string sequences of related packets together
into a "connection" (it even relates sequences of UDP and ICMP packets).
There is a related mark for a connection called a "connection mark".
Marks can be copied freely between the firewall and connection marks
Using these two features it become possible to tag all related traffic
in arbitrary ways, eg authenticated users, traffic from a particular IP,
port, etc. Unfortunately any kind of "proxy" breaks this relationship
because network packets go in one side of the proxy and a completely new
connection comes out of the other side. However, sometimes, we want to
maintain that relationship through the proxy and continue the connection
mark on packets upstream of our proxy
DNSMasq includes such a feature enabled by the --conntrack
option. This allows, for example, using iptables to mark traffic from
a particular IP, and that mark to be persisted to requests made *by*
DNSMasq. Such a feature could be useful for bandwidth accounting,
captive portals and the like. Note a similar feature has been
implemented in Squid 2.2
As an example consider the following iptables rules:
1) iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
2) iptables -t mangle -A PREROUTING -m mark --mark 0 -s 192.168.111.137
-j MARK --set-mark 137
3) iptables -t mangle -A PREROUTING -j CONNMARK --save-mark
4) iptables -t mangle -A OUTPUT -m mark ! --mark 0 -j CONNMARK --save-mark
1-3) are all applied to the PREROUTING table and affect all packets
entering the firewall.
1) copies any existing connection mark into the firewall mark. 2) Checks
the packet not already marked and if not applies an arbitrary mark based
on IP address. 3) Saves the firewall mark back to the connection mark
(which will persist it across related packets)
4) is applied to the OUTPUT table, which is where we first see packets
generated locally. DNSMasq will have already copied the firewall mark
from the request, across to the new packet, and so all that remains is
for iptables to copy it to the connection mark so it's persisted across
packets.
Note: iptables can be quite confusing to the beginner. The following
diagram is extremely helpful in understanding the flows
http://linux-ip.net/nf/nfk-traversal.png
Additionally the following URL contains a useful "starting guide" on
linux connection tracking/marking
http://home.regit.org/netfilter-en/netfilter-connmark/

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

16
contrib/systemd/README Normal file
View File

@@ -0,0 +1,16 @@
Hello,
I created a systemd service file for dnsmasq.
systemd is a sysvinit replacement (see [1] for more information).
One of the goals of systemd is to encourage standardization between different
distributions. This means, while I also submitted a ticket in Debian GNU/Linux,
I would like to ask you to accept this service file as the upstream
distributor, so that other distributions can use the same service file and
dont have to ship their own.
Please include this file in your next release (just like in init script).
[1] http://en.wikipedia.org/wiki/Systemd

View File

@@ -0,0 +1,12 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
ExecStartPre=/usr/sbin/dnsmasq --test
ExecStart=/usr/sbin/dnsmasq -k
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

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

@@ -0,0 +1,23 @@
.TH DHCP_LEASE_TIME 1
.SH NAME
dhcp_lease_time \- Query remaining time of a lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_lease_time <address>
.SH "DESCRIPTION"
Send a DHCPINFORM message to a dnsmasq server running on the local host
and print (to stdout) the time remaining in any lease for the given
address. The time is given as string printed to stdout.
If an error occurs or no lease exists for the given address,
nothing is sent to stdout a message is sent to stderr and a
non-zero error code is returned.
Requires dnsmasq 2.40 or later and may not work with other DHCP servers.
The address argument is a dotted-quad IP addresses and mandatory.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -0,0 +1,35 @@
.TH DHCP_RELEASE 1
.SH NAME
dhcp_release \- Release a DHCP lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_release <interface> <address> <MAC address> <client_id>
.SH "DESCRIPTION"
A utility which forces the DHCP server running on this machine to release a
DHCP lease.
.PP
Send a DHCPRELEASE message via the specified interface to tell the
local DHCP server to delete a particular lease.
The interface argument is the interface in which a DHCP
request _would_ be received if it was coming from the client,
rather than being faked up here.
The address argument is a dotted-quad IP addresses and mandatory.
The MAC address is colon separated hex, and is mandatory. It may be
prefixed by an address-type byte followed by -, eg
10-11:22:33:44:55:66
but if the address-type byte is missing it is assumed to be 1, the type
for ethernet. This encoding is the one used in dnsmasq lease files.
The client-id is optional. If it is "*" then it treated as being missing.
.SH NOTES
MUST be run as root - will fail otherwise.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -178,7 +178,7 @@ static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
static struct in_addr find_interface(struct in_addr client, int fd, int index)
static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index)
{
struct sockaddr_nl addr;
struct nlmsghdr *h;

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

@@ -7,8 +7,8 @@
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# uneccessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link uneccessarily.
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
# Never forward plain names (without a dot or domain part)
#domain-needed
@@ -48,7 +48,7 @@
# non-public domains.
#server=/localnet/192.168.0.1
# Example of routing PTR queries to nameservers: this will send all
# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver 10.1.2.3
#server=/3.168.192.in-addr.arpa/10.1.2.3
@@ -57,21 +57,21 @@
#local=/localnet/
# Add domains which you want to force to an IP address here.
# The example below send any host in doubleclick.net to a local
# webserver.
#address=/doubleclick.net/127.0.0.1
# The example below send any host in double-click.net to a local
# web-server.
#address=/double-click.net/127.0.0.1
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# You can control how dnsmasq talks to a server: this forces
# 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.
@@ -90,7 +90,7 @@
#listen-address=
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP on it.
# disable DHCP and TFTP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
@@ -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,17 +141,30 @@
# 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 explicit 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
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissble to give name,adddress and MAC in any order
# do not matter, it's permissible to give name, address and MAC in any
# order.
# Always allocate the host with ethernet address 11:22:33:44:55:66
# Always allocate the host with Ethernet address 11:22:33:44:55:66
# The IP address 192.168.0.60
#dhcp-host=11:22:33:44:55:66,192.168.0.60
@@ -153,10 +172,18 @@
# 11:22:33:44:55:66 to be "fred"
#dhcp-host=11:22:33:44:55:66,fred
# Always give the host with ethernet address 11:22:33:44:55:66
# Always give the host with Ethernet address 11:22:33:44:55:66
# 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
@@ -174,41 +201,41 @@
# it asks for a DHCP lease.
#dhcp-host=judge
# Never offer DHCP service to a machine whose ethernet
# Never offer DHCP service to a machine whose Ethernet
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with ethernet
# Ignore any client-id presented by the machine with Ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,net:red
# the machine with Ethernet address 11:22:33:44:55:66
#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
# any machine with Ethernet address starting 11:22:33:
#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
# or /etc/ethers. Equivalent to ISC "deny unknown-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
@@ -218,11 +245,11 @@
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.
@@ -236,7 +263,7 @@
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
@@ -262,20 +289,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......
@@ -284,10 +311,10 @@
# Send RFC-3442 classless static routes (note the netmask encoding)
#dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
# Send vendor-class specific options encapsulated in DHCP option 43.
# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to 0.0.0.0 for PXEClients.
#dhcp-option=vendor:PXEClient,1,0.0.0.0
@@ -304,7 +331,7 @@
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
@@ -315,36 +342,97 @@
# 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.)
#dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# 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
# Set the root directory for files availble via FTP.
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Make the TFTP server more secure: with this set, only files owned by
# 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
# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fasion. This facility can be used to
# load balance the tftp load among a set of servers.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
@@ -357,16 +445,16 @@
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slighest chance that you might end up accidentally configuring a DHCP
# the slightest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
# http://www.isc.org/files/auth.html
#dhcp-authoritative
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.
# if there is one.
#dhcp-script=/bin/echo
# Set the cachesize here.
@@ -395,7 +483,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 +514,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 +547,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

@@ -1,9 +1,17 @@
<HTML>
<HEAD>
<TITLE> Dnsmasq - a DNS forwarder for NAT firewalls.</TITLE>
<link rel="icon"
href="http://www.thekelleys.org.uk/dnsmasq/images/favicon.ico">
</HEAD>
<BODY BGCOLOR="WHITE">
<H1 ALIGN=center>Dnsmasq</H1>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td>
<td align="middle" valign="middle"><h1>Dnsmasq</h1></td>
<td align="right" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td></tr>
</table>
Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
server. It is designed to provide DNS and, optionally, DHCP, to a
small network. It can serve the names of local machines which are
@@ -11,7 +19,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 +97,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.

12
logo/README Normal file
View File

@@ -0,0 +1,12 @@
Dnsmasq logo, contributed by Justin Clift.
The source format is Inkscape SVG vector format, which is scalable and
easy to export to other formats. For convenience I've included a 56x31
png export and a 16x16 ico suitable for use as a web favicon.
Simon Kelley, 22/10/2010

BIN
logo/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
logo/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

157
logo/icon.svg Normal file
View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
width="56"
height="31"
viewBox="0 0 56 31"
enable-background="new 0 0 72.833 46.667"
xml:space="preserve"
id="svg2"
inkscape:version="0.47 r22583"
sodipodi:docname="dnsmasq_icon.svg"
inkscape:export-filename="/x/centos_home/jc/workspace/git_repos/libvirt-media/libvirt-media/png/dnsmasq_icon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata27"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs25"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 23.3335 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="72.833 : 23.3335 : 1"
inkscape:persp3d-origin="36.4165 : 15.555667 : 1"
id="perspective4857" />
<filter
id="filter3802"
inkscape:label="filter1"
color-interpolation-filters="sRGB" /><linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient4929"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" />
<linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient5798"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" /><linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient5812"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" /><filter
id="filter6262"
inkscape:label="Drop shadow"
width="1.5"
height="1.5"
x="-0.25"
y="-0.25"
color-interpolation-filters="sRGB"><feGaussianBlur
id="feGaussianBlur6264"
in="SourceAlpha"
stdDeviation="2.500000"
result="blur" /><feColorMatrix
id="feColorMatrix6266"
result="bluralpha"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.500000 0 " /><feOffset
id="feOffset6268"
in="bluralpha"
dx="2.700000"
dy="2.600000"
result="offsetBlur" /><feMerge
id="feMerge6270"><feMergeNode
id="feMergeNode6272"
in="offsetBlur" /><feMergeNode
id="feMergeNode6274"
in="SourceGraphic" /></feMerge></filter></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1568"
inkscape:window-height="1076"
id="namedview23"
showgrid="false"
inkscape:zoom="8"
inkscape:cx="31.966768"
inkscape:cy="21.211869"
inkscape:window-x="567"
inkscape:window-y="328"
inkscape:window-maximized="0"
inkscape:current-layer="layer1"
inkscape:showpageshadow="false"
showborder="true" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="dnsmasq"
style="display:inline"
transform="translate(5.2838057,-15.545371)"><g
id="g3790"
transform="matrix(0.8183832,0,0,0.8183832,65.304897,9.8747678)"
style="filter:url(#filter6262)"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><g
transform="translate(-91.018462,1.0687099)"
id="g9">
<path
style="fill:#6700ad"
inkscape:connector-curvature="0"
id="path11"
d="M 54.997,12.151 C 50.083,9.132 43.29,7.266 35.791,7.266 c -7.5,0 -14.29,1.866 -19.204,4.885 -4.915,3.016 -7.956,7.184 -7.956,11.789 0,4.604 3.041,8.772 7.956,11.788 4.914,3.02 11.704,-4.271 19.204,-4.271 7.499,0 14.292,7.291 19.206,4.271 4.914,-3.016 7.955,-7.185 7.955,-11.788 0,-4.606 -3.041,-8.773 -7.955,-11.789 z M 24.996,24.318 c -2.698,0 -4.885,-0.922 -4.885,-2.061 0,-1.14 2.187,-2.063 4.885,-2.063 2.697,0 4.885,0.924 4.885,2.063 0,1.139 -2.188,2.061 -4.885,2.061 z m 21.501,0.191 c -2.686,0 -4.861,-0.856 -4.861,-1.912 0,-1.054 2.176,-1.911 4.861,-1.911 2.685,0 4.863,0.857 4.863,1.911 0,1.056 -2.178,1.912 -4.863,1.912 z" />
<path
style="fill:none;stroke:#ffb616;stroke-width:1.85353255"
inkscape:connector-curvature="0"
id="path13"
d="M 54.997,12.151 C 50.083,9.132 43.29,7.266 35.791,7.266 c -7.5,0 -14.29,1.866 -19.204,4.885 -4.915,3.016 -7.956,7.184 -7.956,11.789 0,4.604 3.041,8.772 7.956,11.788 4.914,3.02 11.704,-4.271 19.204,-4.271 7.499,0 14.292,7.291 19.206,4.271 4.914,-3.016 7.955,-7.185 7.955,-11.788 0,-4.606 -3.041,-8.773 -7.955,-11.789 z M 24.996,24.318 c -2.698,0 -4.885,-0.922 -4.885,-2.061 0,-1.14 2.187,-2.063 4.885,-2.063 2.697,0 4.885,0.924 4.885,2.063 0,1.139 -2.188,2.061 -4.885,2.061 z m 21.501,0.191 c -2.686,0 -4.861,-0.856 -4.861,-1.912 0,-1.054 2.176,-1.911 4.861,-1.911 2.685,0 4.863,0.857 4.863,1.911 0,1.056 -2.178,1.912 -4.863,1.912 z" />
</g><g
transform="translate(-91.018462,1.0687099)"
id="Layer_2">
<linearGradient
y2="32.715599"
x2="32.937"
y1="-8.8144999"
x1="30.564501"
gradientUnits="userSpaceOnUse"
id="SVGID_3_">
<stop
id="stop17"
style="stop-color:#FFFFFF;stop-opacity:0.73"
offset="0" />
<stop
id="stop19"
style="stop-color:#FFFFFF;stop-opacity:0"
offset="1" />
</linearGradient>
<path
inkscape:connector-curvature="0"
style="fill:url(#linearGradient5812)"
id="path21"
d="m 54.1,15.361 c -0.924,1.078 -2.782,1.265 -3.857,1.06 C 38,14.083 22.75,12.75 16.027,23.031 14.858,24.819 11.992,25.39 10.293,23.887 8.631,22.417 13.105,15.804 17.646,13.033 22.194,10.252 28.474,8.53 35.41,8.53 c 6.936,0 13.215,1.722 17.756,4.502 0.731,0.442 1.627,1.52 0.934,2.329 z" />
</g></g></g></svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1712
po/de.po

File diff suppressed because it is too large Load Diff

1742
po/es.po

File diff suppressed because it is too large Load Diff

1611
po/fi.po

File diff suppressed because it is too large Load Diff

1592
po/fr.po

File diff suppressed because it is too large Load Diff

1268
po/id.po

File diff suppressed because it is too large Load Diff

1611
po/it.po

File diff suppressed because it is too large Load Diff

1541
po/no.po

File diff suppressed because it is too large Load Diff

1812
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1550
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -221,7 +221,7 @@ triggering dial-on-demand internet links.
Sending SIGHUP to the dnsmasq process will cause it to empty its cache and
then re-load <TT>/etc/hosts</TT> and <TT>/etc/resolv.conf</TT>.
<P> Sending SIGUSR1 (killall -10 dnsmasq) to the dnsmasq process will
cause to to write cache usage statisticss to the log, typically
cause to write cache usage statisticss to the log, typically
<TT>/var/log/syslog</TT> or <TT>/var/log/messages</TT>.
<P> The <TT>log-queries</TT> option tells dnsmasq to verbosely log the queries
it is handling and causes SIGUSR1 to trigger a complete dump of the

160
src/bpf.c
View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2011 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,7 +28,71 @@ static struct iovec ifreq = {
.iov_len = 0
};
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
#ifndef SA_SIZE
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
int arp_enumerate(void *parm, int (*callback)())
{
int mib[6];
size_t needed;
char *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl;
int rc;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
#ifdef RTF_LLINFO
mib[5] = RTF_LLINFO;
#else
mib[5] = 0;
#endif
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
return 0;
while (1)
{
if (!expand_buf(&ifconf, needed))
return 0;
if ((rc = sysctl(mib, 6, ifconf.iov_base, &needed, NULL, 0)) == 0 ||
errno != ENOMEM)
break;
needed += needed / 8;
}
if (rc == -1)
return 0;
for (next = ifconf.iov_base ; next < (char *)ifconf.iov_base + needed; next += rtm->rtm_msglen)
{
rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
return 0;
}
return 1;
}
#endif
int iface_enumerate(int family, void *parm, int (*callback)())
{
char *ptr;
struct ifreq *ifr;
@@ -37,6 +101,13 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
int lastlen = 0;
size_t len = 0;
if (family == AF_UNSPEC)
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
return arp_enumerate(parm, callback);
#else
return 0; /* need code for Solaris and MacOS*/
#endif
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
@@ -63,55 +134,62 @@ 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;
ifr = (struct ifreq *)ifreq.iov_base;
memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
if (ifr->ifr_addr.sa_family == family)
{
struct in_addr addr, netmask, broadcast;
broadcast.s_addr = 0;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
continue;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*ipv4_callback)(addr,
(int)if_nametoindex(ifr->ifr_name),
netmask, broadcast,
parm)))
goto err;
}
#ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
{
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
/* voodoo to clear interface field in address */
if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
if (family == AF_INET)
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
struct in_addr addr, netmask, broadcast;
broadcast.s_addr = 0;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
continue;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*callback)(addr,
(int)if_nametoindex(ifr->ifr_name),
netmask, broadcast,
parm)))
goto err;
}
#ifdef HAVE_IPV6
else if (family == AF_INET6)
{
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
/* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
if (!((*callback)(addr,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name), 0,
parm)))
goto err;
}
if (!((*ipv6_callback)(addr,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name),
parm)))
goto err;
}
#endif
}
}
ret = 1;
@@ -126,7 +204,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 +249,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 +290,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-2011 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);
@@ -72,7 +75,7 @@ void cache_init(void)
struct crec *crecp;
int i;
if (daemon->options & OPT_LOG)
if (option_bool(OPT_LOG))
addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize/10;
@@ -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_NAMEP)
return crecp->name.namep;
return crecp->name.sname;
@@ -359,11 +362,9 @@ 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);
/* CONFIG bit no needed except for logging */
flags &= ~F_CONFIG;
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */
if (insert_error)
@@ -392,8 +393,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++;
}
@@ -488,7 +500,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
/* first search, look for relevant entries and push to top of list
also free anything which has expired */
struct crec *next, **up, **insert = NULL, **chainp = &ans;
int ins_flags = 0;
unsigned short ins_flags = 0;
for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
{
@@ -516,19 +528,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 +642,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_NAMEP | 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 (option_bool(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 +893,50 @@ 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 (option_bool(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)
{
total_size = read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index, total_size);
addn_hosts = addn_hosts->next;
}
if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size);
daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
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
struct in_addr a_record_from_hosts(char *name, time_t now)
{
struct crec *crecp = NULL;
struct in_addr ret;
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
if (crecp->flags & F_HOSTS)
return *(struct in_addr *)&crecp->addr;
my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
ret.s_addr = 0;
return ret;
}
void cache_unhash_dhcp(void)
{
struct crec *cache, **up;
@@ -836,31 +957,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;
unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
struct crec *crec = NULL, *aliasc;
unsigned short flags = F_NAMEP | 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(MS_DHCP | 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,
my_syslog(MS_DHCP | 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 +1018,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_NAMEP | 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)
{
@@ -918,12 +1065,15 @@ void dump_cache(time_t now)
serv->flags &= ~SERV_COUNTED;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)))
if (!(serv->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
int port;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
queries += serv1->queries;
@@ -933,11 +1083,11 @@ void dump_cache(time_t now)
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries);
}
if ((daemon->options & (OPT_DEBUG | OPT_LOG)))
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
int i;
my_syslog(LOG_DEBUG, "Host Address Flags Expires");
my_syslog(LOG_INFO, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
@@ -983,35 +1133,41 @@ void dump_cache(time_t now)
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
my_syslog(LOG_DEBUG, daemon->namebuff);
my_syslog(LOG_INFO, daemon->namebuff);
}
}
}
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 int flags, char *name, struct all_addr *addr, char *arg)
{
char *source, *dest = addrbuff;
char *verb = "is";
char types[20];
if (!(daemon->options & OPT_LOG))
if (!option_bool(OPT_LOG))
return;
if (addr)
@@ -1052,26 +1208,16 @@ 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>";
else
dest = "<CNAME>";
}
dest = "<CNAME>";
else if (flags & F_RRNAME)
dest = arg;
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 +1227,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
@@ -1099,6 +1236,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
if (strlen(name) == 0)
name = ".";
my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2011 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.59"
#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 20 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,61 +40,46 @@
# 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"
# elif defined(__ANDROID__)
# define LEASEFILE "/data/misc/dhcp/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 NAMESERVER_PORT 53
#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 EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
/* A small collection of RR-types which are missing on some platforms */
#ifndef T_SIG
# define T_SIG 24
#endif
#ifndef T_SRV
# define T_SRV 33
#endif
#ifndef T_OPT
# define T_OPT 41
#endif
#ifndef T_TKEY
# define T_TKEY 249
#endif
#ifndef T_TSIG
# define T_TSIG 250
#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 +89,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 +103,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,34 +119,30 @@ 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)
HAVE_DBUS
Define this if you want to link against libdbus, and have dnsmasq
define some methods to allow (re)configuration of the upstream DNS
define this if you want to link against libdbus, and have dnsmasq
support some methods to allow (re)configuration of the upstream DNS
servers via DBus.
HAVE_IDN
define this if you want international domain name support.
NOTE: for backwards compatibility, IDN support is automatically
included when internationalisation support is built, using the
*-i18n makefile targets, even if HAVE_IDN is not explicitly set.
HAVE_CONNTRACK
define this to include code which propogates conntrack marks from
incoming DNS queries to the corresponding upstream queries. This adds
a build-dependency on libnetfilter_conntrack, but the resulting binary will
still run happily on a kernel without conntrack support.
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 +150,39 @@ 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 */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */
#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 +190,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 +202,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 +219,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 +279,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

90
src/conntrack.c Normal file
View File

@@ -0,0 +1,90 @@
/* dnsmasq is Copyright (c) 2000-2011 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/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_CONNTRACK
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
static int gotit = 0; /* yuck */
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
{
struct nf_conntrack *ct;
struct nfct_handle *h;
gotit = 0;
if ((ct = nfct_new()))
{
nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
#ifdef HAVE_IPV6
if (peer_addr->sa.sa_family == AF_INET6)
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
}
else
#endif
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
}
if ((h = nfct_open(CONNTRACK, 0)))
{
nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);
if (nfct_query(h, NFCT_Q_GET, ct) == -1)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
warned = 1;
}
}
nfct_close(h);
}
nfct_destroy(ct);
}
return gotit;
}
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
{
unsigned int *ret = (unsigned int *)data;
*ret = nfct_get_attr_u32(ct, ATTR_MARK);
(void)type; /* eliminate warning */
gotit = 1;
return NFCT_CB_CONTINUE;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2011 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

File diff suppressed because it is too large Load Diff

91
src/dhcp_protocol.h Normal file
View File

@@ -0,0 +1,91 @@
/* dnsmasq is Copyright (c) 2000-2011 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/>.
*/
#define BOOTREQUEST 1
#define BOOTREPLY 2
#define DHCP_COOKIE 0x63825363
/* The Linux in-kernel DHCP client silently ignores any packet
smaller than this. Sigh........... */
#define MIN_PACKETSZ 300
#define OPTION_PAD 0
#define OPTION_NETMASK 1
#define OPTION_ROUTER 3
#define OPTION_DNSSERVER 6
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_VENDOR_CLASS_OPT 43
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_REQUESTED_OPTIONS 55
#define OPTION_MESSAGE 56
#define OPTION_MAXMESSAGE 57
#define OPTION_T1 58
#define OPTION_T2 59
#define OPTION_VENDOR_ID 60
#define OPTION_CLIENT_ID 61
#define OPTION_SNAME 66
#define OPTION_FILENAME 67
#define OPTION_USER_CLASS 77
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_ARCH 93
#define OPTION_PXE_UUID 97
#define OPTION_SUBNET_SELECT 118
#define OPTION_DOMAIN_SEARCH 119
#define OPTION_SIP_SERVER 120
#define OPTION_VENDOR_IDENT 124
#define OPTION_VENDOR_IDENT_OPT 125
#define OPTION_END 255
#define SUBOPT_CIRCUIT_ID 1
#define SUBOPT_REMOTE_ID 2
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */
#define SUBOPT_PXE_DISCOVERY 6
#define SUBOPT_PXE_SERVERS 8
#define SUBOPT_PXE_MENU 9
#define SUBOPT_PXE_MENU_PROMPT 10
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
#define DHCP_CHADDR_MAX 16
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
};

111
src/dns_protocol.h Normal file
View File

@@ -0,0 +1,111 @@
/* dnsmasq is Copyright (c) 2000-2011 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/>.
*/
#define IN6ADDRSZ 16
#define INADDRSZ 4
#define PACKETSZ 512 /* maximum packet size */
#define MAXDNAME 1025 /* maximum presentation domain name */
#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
#define MAXLABEL 63 /* maximum length of domain label */
#define NOERROR 0 /* no error */
#define FORMERR 1 /* format error */
#define SERVFAIL 2 /* server failure */
#define NXDOMAIN 3 /* non existent domain */
#define NOTIMP 4 /* not implemented */
#define REFUSED 5 /* query refused */
#define QUERY 0 /* opcode */
#define C_IN 1 /* the arpa internet */
#define C_CHAOS 3 /* for chaos net (MIT) */
#define C_ANY 255 /* wildcard match */
#define T_A 1
#define T_NS 2
#define T_CNAME 5
#define T_SOA 6
#define T_PTR 12
#define T_MX 15
#define T_TXT 16
#define T_SIG 24
#define T_AAAA 28
#define T_SRV 33
#define T_NAPTR 35
#define T_OPT 41
#define T_TKEY 249
#define T_TSIG 250
#define T_MAILB 253
#define T_ANY 255
struct dns_header {
u16 id;
u8 hb3,hb4;
u16 qdcount,ancount,nscount,arcount;
};
#define HB3_QR 0x80
#define HB3_OPCODE 0x78
#define HB3_AA 0x04
#define HB3_TC 0x02
#define HB3_RD 0x01
#define HB4_RA 0x80
#define HB4_AD 0x20
#define HB4_CD 0x10
#define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
#define GETSHORT(s, cp) { \
unsigned char *t_cp = (unsigned char *)(cp); \
(s) = ((u16)t_cp[0] << 8) \
| ((u16)t_cp[1]) \
; \
(cp) += 2; \
}
#define GETLONG(l, cp) { \
unsigned char *t_cp = (unsigned char *)(cp); \
(l) = ((u32)t_cp[0] << 24) \
| ((u32)t_cp[1] << 16) \
| ((u32)t_cp[2] << 8) \
| ((u32)t_cp[3]) \
; \
(cp) += 4; \
}
#define PUTSHORT(s, cp) { \
u16 t_s = (u16)(s); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_s >> 8; \
*t_cp = t_s; \
(cp) += 2; \
}
#define PUTLONG(l, cp) { \
u32 t_l = (u32)(l); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_l >> 24; \
*t_cp++ = t_l >> 16; \
*t_cp++ = t_l >> 8; \
*t_cp = t_l; \
(cp) += 4; \
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2008 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2011 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,27 +10,49 @@
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-2011 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__)
# ifndef __ANDROID__
# define _GNU_SOURCE
# endif
# 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
#endif
#include <arpa/nameser.h>
/* and this. */
#include <getopt.h>
#include "config.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#include "dns_protocol.h"
#include "dhcp_protocol.h"
#define gettext_noop(S) (S)
#ifndef LOCALEDIR
# define _(S) (S)
@@ -42,10 +64,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 +74,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 +85,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) || defined (__ANDROID__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -84,10 +110,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 +140,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 +165,51 @@ 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)
/* Trust the compiler dead-code eliminator.... */
#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
#define OPT_BOGUSPRIV 0
#define OPT_FILTER 1
#define OPT_LOG 2
#define OPT_SELFMX 3
#define OPT_NO_HOSTS 4
#define OPT_NO_POLL 5
#define OPT_DEBUG 6
#define OPT_ORDER 7
#define OPT_NO_RESOLV 8
#define OPT_EXPAND 9
#define OPT_LOCALMX 10
#define OPT_NO_NEG 11
#define OPT_NODOTS_LOCAL 12
#define OPT_NOWILD 13
#define OPT_ETHERS 14
#define OPT_RESOLV_DOMAIN 15
#define OPT_NO_FORK 16
#define OPT_AUTHORITATIVE 17
#define OPT_LOCALISE 18
#define OPT_DBUS 19
#define OPT_DHCP_FQDN 20
#define OPT_NO_PING 21
#define OPT_LEASE_RO 22
#define OPT_ALL_SERVERS 23
#define OPT_RELOAD 24
#define OPT_LOCAL_REBIND 25
#define OPT_TFTP_SECURE 26
#define OPT_TFTP_NOBLOCK 27
#define OPT_LOG_OPTS 28
#define OPT_TFTP_APREF 29
#define OPT_NO_OVERRIDE 30
#define OPT_NO_REBIND 31
#define OPT_ADD_MAC 32
#define OPT_DNSSEC 33
#define OPT_CONSEC_ADDR 34
#define OPT_CONNTRACK 35
#define OPT_LAST 36
/* 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 +227,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,8 +238,15 @@ 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;
char *name;
unsigned char *txt;
unsigned short class, len;
struct txt_record *next;
};
@@ -197,6 +256,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 */
@@ -227,22 +291,27 @@ struct crec {
} name;
};
#define F_IMMORTAL 1
#define F_CONFIG 2
#define F_REVERSE 4
#define F_FORWARD 8
#define F_DHCP 16
#define F_NEG 32
#define F_HOSTS 64
#define F_IPV4 128
#define F_IPV6 256
#define F_BIGNAME 512
#define F_UPSTREAM 1024
#define F_SERVER 2048
#define F_NXDOMAIN 4096
#define F_QUERY 8192
#define F_CNAME 16384
#define F_NOERR 32768
#define F_IMMORTAL (1u<<0)
#define F_NAMEP (1u<<1)
#define F_REVERSE (1u<<2)
#define F_FORWARD (1u<<3)
#define F_DHCP (1u<<4)
#define F_NEG (1u<<5)
#define F_HOSTS (1u<<6)
#define F_IPV4 (1u<<7)
#define F_IPV6 (1u<<8)
#define F_BIGNAME (1u<<9)
#define F_NXDOMAIN (1u<<10)
#define F_CNAME (1u<<11)
#define F_NOERR (1u<<12)
#define F_CONFIG (1u<<13)
/* below here are only valid as args to log_query: cache
entries are limited to 16 bits */
#define F_UPSTREAM (1u<<16)
#define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
/* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold an IPv6 address.
@@ -250,19 +319,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 +335,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 +345,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 +363,8 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok;
int tftp_ok, mtu, done, dad;
char *name;
struct irec *next;
};
@@ -325,20 +390,30 @@ struct resolvc {
char *name;
};
/* adn-hosts parms from command-line */
/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
#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 */
};
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
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, flags;
unsigned int crc;
time_t time;
struct frec *next;
@@ -350,8 +425,6 @@ struct frec {
#define ACTION_OLD 3
#define ACTION_ADD 4
#define DHCP_CHADDR_MAX 16
struct dhcp_lease {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
@@ -367,9 +440,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 +457,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 +496,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,23 +509,37 @@ 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
#define DHOPT_TAGOK 4096
struct dhcp_boot {
char *file, *sname;
char *file, *sname, *tftp_sname;
struct in_addr next_server;
struct dhcp_netid *netid;
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 +557,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 +574,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,25 +582,12 @@ struct dhcp_context {
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
};
#define CONTEXT_PROXY 8
struct ping_result {
struct in_addr addr;
time_t time;
unsigned int hash;
struct ping_result *next;
};
@@ -509,28 +603,51 @@ struct tftp_transfer {
int sockfd;
time_t timeout;
int backoff;
unsigned int block, blocksize;
struct sockaddr_in peer;
char opt_blocksize, opt_transize;
unsigned int block, blocksize, expansion;
off_t offset;
union mysockaddr peer;
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)
in option.c */
unsigned int options;
unsigned int options, options2;
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 +657,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;
char *dhcp_hosts_file, *dhcp_opts_file;
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;
struct hostsfile *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 +731,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 int 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,36 +743,42 @@ 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);
struct in_addr a_record_from_hosts(char *name, time_t now);
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,
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags,
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags,
unsigned long local_ttl);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
size_t answer_request(HEADER *header, char *limit, size_t qlen,
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind, int checking_disabled);
size_t answer_request(struct dns_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,
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, size_t plen,
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(HEADER *header, size_t plen, char *buff);
size_t resize_packet(HEADER *header, size_t plen,
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
/* 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 +799,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,34 +808,38 @@ 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);
void set_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
/* 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);
union mysockaddr *local_addr, struct in_addr netmask);
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);
void create_wildcard_listeners(void);
void create_bound_listeners(int die);
int is_dad_listeners(void);
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 +850,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,40 +863,46 @@ 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,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
struct in_addr lease_find_max_addr(struct dhcp_context *context);
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, struct in_addr fallback);
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
@@ -768,18 +918,21 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
#endif
/* bpf.c or netlink.c */
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
int iface_enumerate(int family, void *parm, int (callback)());
/* dbus.c */
#ifdef HAVE_DBUS
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);
@@ -791,3 +944,9 @@ int helper_buf_empty(void);
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
#endif
/* conntrack.c */
#ifdef HAVE_CONNTRACK
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
int istcp, unsigned int *markp);
#endif

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-2011 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 (!option_bool(OPT_DEBUG) && uid != 0)
{
gid_t dummy;
if (setgroups(0, &dummy) == -1 ||
setgid(gid) == -1 ||
setuid(uid) == -1)
{
if (option_bool(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-2011 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,27 +29,34 @@ 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)
if (option_bool(OPT_LEASE_RO))
{
/* run "<lease_change_script> init" once to get the
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 (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
}
dns_dirty = 0;
@@ -323,6 +323,21 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
return NULL;
}
/* Find largest assigned address in context */
struct in_addr lease_find_max_addr(struct dhcp_context *context)
{
struct dhcp_lease *lease;
struct in_addr addr = context->start;
if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
for (lease = leases; lease; lease = lease->next)
if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
addr = lease->addr;
return addr;
}
struct dhcp_lease *lease_allocate(struct in_addr addr)
{
@@ -412,11 +427,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 +470,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 (option_bool(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 +546,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 (option_bool(OPT_DBUS) && !daemon->dbus)
return 0;
#endif
if (old_leases)
{
lease = old_leases;
@@ -513,7 +560,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 +569,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 +591,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);
@@ -552,25 +601,29 @@ int do_script_run(time_t now)
for (lease = leases; lease; lease = lease->next)
if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
(lease->aux_changed && option_bool(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

195
src/log.c
View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2011 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,16 @@
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 __ANDROID__
# include <android/log.h>
#endif
/* Implement logging to /dev/log asynchronously. If syslogd is
making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
syslogd, then the two daemons can deadlock. We get around this
@@ -30,7 +34,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,14 +55,16 @@ 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 = option_bool(OPT_DEBUG);
if (daemon->log_fac != -1)
log_fac = daemon->log_fac;
#ifdef LOG_LOCAL0
else if (daemon->options & OPT_DEBUG)
else if (option_bool(OPT_DEBUG))
log_fac = LOG_LOCAL0;
#endif
@@ -65,13 +72,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 +101,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
#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.... */
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
{
#if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__)
/* 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)
@@ -138,6 +154,19 @@ static void log_write(void)
while (entries)
{
/* The data in the payoad is written with a terminating zero character
and the length reflects this. For a stream connection we need to
send the zero as a record terminator, but this isn't done for a
datagram connection, so treat the length as one less than reality
to elide the zero. If we're logging to a file, turn the zero into
a newline, and leave the length alone. */
int len_adjust = 0;
if (log_to_file)
entries->payload[entries->offset + entries->length - 1] = '\n';
else if (connection_type == SOCK_DGRAM)
len_adjust = 1;
/* Avoid duplicates over a fork() */
if (entries->pid != getpid())
{
@@ -147,11 +176,11 @@ static void log_write(void)
connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
{
entries->length -= rc;
entries->offset += rc;
if (entries->length == 0)
if (entries->length == len_adjust)
{
free_entry();
if (entries_lost != 0)
@@ -240,6 +269,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 +281,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);
@@ -260,8 +306,28 @@ void my_syslog(int priority, const char *format, ...)
if (log_fd == -1)
{
/* fall-back to syslog if we die during startup or fail during running. */
#ifdef __ANDROID__
/* do android-specific logging.
log_fd is always -1 on Android except when logging to a file. */
int alog_lvl;
if (priority <= LOG_ERR)
alog_lvl = ANDROID_LOG_ERROR;
else if (priority == LOG_WARNING)
alog_lvl = ANDROID_LOG_WARN;
else if (priority <= LOG_INFO)
alog_lvl = ANDROID_LOG_INFO;
else
alog_lvl = ANDROID_LOG_DEBUG;
va_start(ap, format);
__android_log_vprint(alog_lvl, "dnsmasq", format, ap);
va_end(ap);
#else
/* fall-back to syslog if we die during startup or
fail during running (always on Solaris). */
static int isopen = 0;
if (!isopen)
{
openlog("dnsmasq", LOG_PID, log_fac);
@@ -270,6 +336,8 @@ void my_syslog(int priority, const char *format, ...)
va_start(ap, format);
vsyslog(priority, format, ap);
va_end(ap);
#endif
return;
}
@@ -297,8 +365,13 @@ void my_syslog(int priority, const char *format, ...)
p = entry->payload;
if (!log_to_file)
p += sprintf(p, "<%d>", priority | log_fac);
/* Omit timestamp for default daemontools situation */
if (!log_stderr || !option_bool(OPT_NO_FORK))
p += sprintf(p, "%.15s ", ctime(&time_now) + 4);
p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, (int)pid);
p += sprintf(p, "dnsmasq%s[%d]: ", func, (int)pid);
len = p - entry->payload;
va_start(ap, format);
len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
@@ -306,10 +379,6 @@ void my_syslog(int priority, const char *format, ...)
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0;
entry->pid = pid;
/* replace terminator with \n */
if (log_to_file)
entry->payload[entry->length - 1] = '\n';
}
/* almost always, logging won't block, so try and write this now,
@@ -368,14 +437,20 @@ void check_log_writer(fd_set *set)
void flush_log(void)
{
/* block until queue empty */
if (log_fd != -1)
/* write until queue empty, but don't loop forever if there's
no connection to the syslog in existance */
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 || !connection_good)
{
close(log_fd);
break;
}
waiter.tv_sec = 0;
waiter.tv_nsec = 1000000; /* 1 ms */
nanosleep(&waiter, NULL);
}
}
@@ -386,11 +461,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-2011 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,12 @@
# include <linux/if_addr.h>
#endif
#ifndef NDA_RTA
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#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 +43,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,59 +65,75 @@ 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;
}
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
/* family = AF_UNSPEC finds ARP table entries. */
int iface_enumerate(int family, void *parm, int (*callback)())
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
ssize_t len;
static unsigned int seq = 0;
int family = AF_INET;
struct {
struct nlmsghdr nlh;
@@ -125,7 +147,7 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
again:
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_type = family == AF_UNSPEC ? RTM_GETNEIGH : RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq;
@@ -141,70 +163,97 @@ 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
if (family == AF_INET && ipv6_callback)
{
family = AF_INET6;
goto again;
}
#endif
return 1;
}
else if (h->nlmsg_type == RTM_NEWADDR)
return 1;
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
struct rtattr *rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
if (ifa->ifa_family == AF_INET)
if (ifa->ifa_family == family)
{
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
if (ifa->ifa_family == AF_INET)
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
struct in_addr netmask, addr, broadcast;
rta = RTA_NEXT(rta, len1);
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr)
if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0;
}
if (addr.s_addr && ipv4_callback)
if (!((*ipv4_callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0;
}
#ifdef HAVE_IPV6
else if (ifa->ifa_family == AF_INET6)
{
struct in6_addr *addrp = NULL;
while (RTA_OK(rta, len1))
else if (ifa->ifa_family == AF_INET6)
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
struct in6_addr *addrp = NULL;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
rta = RTA_NEXT(rta, len1);
if (addrp)
if (!((*callback)(addrp, ifa->ifa_index,
ifa->ifa_index, ifa->ifa_flags & IFA_F_TENTATIVE, parm)))
return 0;
}
#endif
}
}
else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)
{
struct ndmsg *neigh = NLMSG_DATA(h);
struct rtattr *rta = NDA_RTA(neigh);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh));
size_t maclen = 0;
char *inaddr = NULL, *mac = NULL;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == NDA_DST)
inaddr = (char *)(rta+1);
else if (rta->rta_type == NDA_LLADDR)
{
maclen = rta->rta_len - sizeof(struct rtattr);
mac = (char *)(rta+1);
}
if (addrp && ipv6_callback)
if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0;
rta = RTA_NEXT(rta, len1);
}
#endif
}
if (inaddr && mac)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
return 0;
}
}
}
@@ -212,6 +261,12 @@ 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 +276,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 +293,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-2011 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,127 @@
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;
}
#elif defined(HAVE_SOLARIS_NETWORK)
#include <zone.h>
#include <alloca.h>
#ifndef LIFC_UNDER_IPMP
# define LIFC_UNDER_IPMP 0
#endif
int indextoname(int fd, int index, char *name)
{
int64_t lifc_flags;
struct lifnum lifn;
int numifs, bufsize, i;
struct lifconf lifc;
struct lifreq *lifrp;
if (index == 0)
return 0;
if (getzoneid() == GLOBAL_ZONEID)
{
if (!if_indextoname(index, name))
return 0;
return 1;
}
lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP;
lifn.lifn_family = AF_UNSPEC;
lifn.lifn_flags = lifc_flags;
if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0)
return 0;
numifs = lifn.lifn_count;
bufsize = numifs * sizeof(struct lifreq);
lifc.lifc_family = AF_UNSPEC;
lifc.lifc_flags = lifc_flags;
lifc.lifc_len = bufsize;
lifc.lifc_buf = alloca(bufsize);
if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0)
return 0;
lifrp = lifc.lifc_req;
for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++)
{
struct lifreq lifr;
strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0)
return 0;
if (lifr.lifr_index == index) {
strncpy(name, lifr.lifr_name, IF_NAMESIZE);
return 1;
}
}
return 0;
}
#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,37 +149,66 @@ 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;
}
static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask)
union mysockaddr *addr, struct in_addr netmask, int dad)
{
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
{
iface->dad = dad;
return 1;
}
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 +219,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 +250,46 @@ 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 */
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;
iface->dad = dad;
iface->done = 0;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
@@ -180,7 +301,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
#ifdef HAVE_IPV6
static int iface_allowed_v6(struct in6_addr *local,
int scope, int if_index, void *vparam)
int scope, int if_index, int dad, void *vparam)
{
union mysockaddr addr;
struct in_addr netmask; /* dummy */
@@ -196,7 +317,7 @@ static int iface_allowed_v6(struct in6_addr *local,
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = scope;
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, dad);
}
#endif
@@ -214,17 +335,17 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);
}
int enumerate_interfaces(void)
{
#ifdef HAVE_IPV6
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
#else
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, NULL);
if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))
return 0;
#endif
return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4);
}
/* set NONBLOCK bit on fd: See Stevens 16.6 */
@@ -239,207 +360,260 @@ int fix_fd(int fd)
return 1;
}
#if defined(HAVE_IPV6)
static int create_ipv6_listener(struct listener **link, int port)
static int make_sock(union mysockaddr *addr, int type, int dienow)
{
union mysockaddr addr;
int tcpfd, fd;
struct listener *l;
int opt = 1;
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
#endif
/* No error of the kernel doesn't support IPv6 */
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
return (errno == EPROTONOSUPPORT ||
errno == EAFNOSUPPORT ||
errno == EINVAL);
int family = addr->sa.sa_family;
int fd, rc, opt = 1;
if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
return 0;
if ((fd = socket(family, type, 0)) == -1)
{
int port;
/* No error if the kernel just doesn't support this IP flavour */
if (errno == EPROTONOSUPPORT ||
errno == EAFNOSUPPORT ||
errno == EINVAL)
return -1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
!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;
l = safe_malloc(sizeof(struct listener));
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = -1;
l->family = AF_INET6;
l->next = NULL;
*link = l;
err:
if (dienow)
{
port = prettyprint_addr(addr, daemon->namebuff);
if (!option_bool(OPT_NOWILD))
sprintf(daemon->namebuff, "port %d", port);
die(_("failed to create listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
goto err;
return 1;
#ifdef HAVE_IPV6
if (family == AF_INET6 && setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
goto err;
#endif
if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
goto err;
if (type == SOCK_STREAM)
{
if (listen(fd, 5) == -1)
goto err;
}
else if (!option_bool(OPT_NOWILD))
{
if (family == AF_INET)
{
#if defined(HAVE_LINUX_NETWORK)
if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
goto err;
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1)
goto err;
#endif
}
#ifdef HAVE_IPV6
else
{
/* 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
goto err;
}
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
goto err;
# endif
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1)
goto err;
# endif
}
#endif
}
return fd;
}
#endif
struct listener *create_wildcard_listeners(void)
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
{
union mysockaddr addr;
int opt = 1;
struct listener *l, *l6 = NULL;
int tcpfd = -1, fd = -1, tftpfd = -1;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
struct listener *l = NULL;
int fd = -1, tcpfd = -1, tftpfd = -1;
if (daemon->port != 0)
{
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
(tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return NULL;
if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
listen(tcpfd, 5) == -1 ||
!fix_fd(tcpfd) ||
#ifdef HAVE_IPV6
!create_ipv6_listener(&l6, daemon->port) ||
#endif
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(fd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
fd = make_sock(addr, SOCK_DGRAM, dienow);
tcpfd = make_sock(addr, SOCK_STREAM, dienow);
}
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (do_tftp)
{
addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return NULL;
if (!fix_fd(tftpfd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(tftpfd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tftpfd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(tftpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
if (addr->sa.sa_family == AF_INET)
{
/* port must be restored to DNS port for TCP code */
short save = addr->in.sin_port;
addr->in.sin_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in.sin_port = save;
}
# ifdef HAVE_IPV6
else
{
short save = addr->in6.sin6_port;
addr->in6.sin6_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in6.sin6_port = save;
}
# endif
}
#endif
l = safe_malloc(sizeof(struct listener));
l->family = AF_INET;
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = tftpfd;
l->next = l6;
if (fd != -1 || tcpfd != -1 || tftpfd != -1)
{
l = safe_malloc(sizeof(struct listener));
l->next = NULL;
l->family = addr->sa.sa_family;
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = tftpfd;
}
return l;
}
struct listener *create_bound_listeners(void)
void create_wildcard_listeners(void)
{
struct listener *listeners = NULL;
struct irec *iface;
int opt = 1;
union mysockaddr addr;
struct listener *l;
int tftp_enabled = daemon->tftp_unlimited || daemon->tftp_interfaces;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(addr.in);
#endif
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port);
l = create_listeners(&addr, tftp_enabled, 1);
#ifdef HAVE_IPV6
memset(&addr, 0, sizeof(addr));
# ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
# endif
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(daemon->port);
if (l)
l->next = create_listeners(&addr, tftp_enabled, 1);
else
l = create_listeners(&addr, tftp_enabled, 1);
#endif
daemon->listeners = l;
}
void create_bound_listeners(int dienow)
{
struct listener *new;
struct irec *iface;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (!iface->done && !iface->dad &&
(new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
{
new->iface = iface;
new->next = daemon->listeners;
daemon->listeners = new;
iface->done = 1;
}
}
int is_dad_listeners(void)
{
struct irec *iface;
if (option_bool(OPT_NOWILD))
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dad && !iface->done)
return 1;
return 0;
}
/* 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)
{
struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = listeners;
new->tftpfd = -1;
new->tcpfd = -1;
new->fd = -1;
union mysockaddr addr;
unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port;
int tries = ports_avail < 30 ? 3 * ports_avail : 100;
if (daemon->port != 0)
{
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tcpfd) ||
!fix_fd(new->fd))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
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
if (iface->addr.sa.sa_family == AF_INET6)
{
if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
die(_("failed to set IPV6 options on listening socket: %s"), NULL, EC_BADNET);
}
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
if (bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
{
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6 && (errno == ENODEV || errno == EADDRNOTAVAIL))
{
close(new->tcpfd);
close(new->fd);
free(new);
new = NULL;
}
else
}
#endif
{
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
}
else if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
}
if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
return fd;
if (errno != EADDRINUSE && errno != EACCES)
break;
}
#ifdef HAVE_TFTP
if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
{
short save = iface->addr.in.sin_port;
iface->addr.in.sin_port = htons(TFTP_PORT);
if ((new->tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tftpfd) ||
bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET);
iface->addr.in.sin_port = save;
}
#endif
if (new)
listeners = new;
close(fd);
}
return listeners;
return -1;
}
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
{
@@ -460,8 +634,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 +647,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 +729,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) &&
(daemon->options & OPT_NOWILD))
errno != 0 &&
option_bool(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 +752,15 @@ void check_servers(void)
struct server *new, *tmp, *ret = NULL;
int port = 0;
/* interface may be new since startup */
if (!option_bool(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 +783,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 +799,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;
@@ -688,20 +893,38 @@ int reload_servers(char *fname)
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
else
{
int scope_index = 0;
char *scope_id = strchr(token, '%');
if (scope_id)
{
*(scope_id++) = 0;
scope_index = if_nametoindex(scope_id);
}
if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
#endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
addr.in6.sin6_scope_id = scope_index;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
source_addr.in6.sin6_scope_id = 0;
}
else
continue;
}
#endif /* IPV6 */
#else /* IPV6 */
else
continue;
#endif
if (old_servers)
{
serv = old_servers;
@@ -744,16 +967,21 @@ 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);
for (l = daemon->listeners;
l && (l->family != AF_INET || l->fd == -1);
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

File diff suppressed because it is too large Load Diff

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-2011 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);
@@ -43,26 +43,36 @@ void tftp_request(struct listener *listen, time_t now)
ssize_t len;
char *packet = daemon->packet;
char *filename, *mode, *p, *end, *opt;
struct sockaddr_in addr, peer;
union mysockaddr 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 pretty_addr[ADDRSTRLEN];
char *name;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#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;
@@ -84,63 +94,157 @@ void tftp_request(struct listener *listen, time_t now)
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
if (daemon->options & OPT_NOWILD)
addr = listen->iface->addr.in;
if (option_bool(OPT_NOWILD))
{
addr = listen->iface->addr;
mtu = listen->iface->mtu;
name = listen->iface->name;
}
else
{
addr.sin_addr.s_addr = 0;
struct cmsghdr *cmptr;
int check;
struct interface_list *ir;
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
addr.sa.sa_family = listen->family;
#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)
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.in.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
#elif defined(HAVE_SOLARIS_NETWORK)
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
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_addr *a;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.in.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *(p.i);
}
if (!(ifr.ifr_ifindex = if_index) ||
ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1)
return;
#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;
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
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.in.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = p.s->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.in6.sin6_addr = p.p->ipi6_addr;
if_index = p.p->ipi6_ifindex;
}
}
#endif
if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
if (!indextoname(listen->tftpfd, if_index, namebuff))
return;
name = namebuff;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
check = iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, &if_index);
else
#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;
check = iface_check(AF_INET, (struct all_addr *)&addr.in.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
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
}
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
/* 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;
if (listen->family == AF_INET)
{
addr.in.sin_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin_len = sizeof(addr);
addr.in.sin_len = sizeof(addr.in);
#endif
}
#ifdef HAVE_IPV6
else
{
addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
#endif
}
#endif
if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return;
if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
{
free(transfer);
return;
@@ -151,15 +255,19 @@ 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;
prettyprint_addr(&peer, pretty_addr);
/* 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 (bind(transfer->sockfd, &addr.sa, sa_len(&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))
{
@@ -167,10 +275,15 @@ void tftp_request(struct listener *listen, time_t now)
{
if (++port <= daemon->end_tftp_port)
{
addr.sin_port = htons(port);
if (listen->family == AF_INET)
addr.in.sin_port = htons(port);
#ifdef HAVE_IPV6
else
addr.in6.sin6_port = htons(port);
#endif
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 +297,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)
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), pretty_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 || !option_bool(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 && option_bool(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, pretty_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 +367,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 +386,12 @@ 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));
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 +401,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 +428,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 && option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
goto perm;
/* If we're doing many tranfers from the same file, only
@@ -356,6 +478,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
char pretty_addr[ADDRSTRLEN];
struct ack {
unsigned short op, block;
@@ -370,6 +493,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
prettyprint_addr(&transfer->peer, pretty_addr);
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
@@ -378,27 +503,30 @@ 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)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
/* Sanitise error message */
if (!err)
err = "";
else
{
char *q, *r;
for (q = r = err; *r; r++)
if (isprint((int)*r))
unsigned char *q, *r;
for (q = r = (unsigned char *)err; *r; r++)
if (isprint(*r))
*(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));
pretty_addr);
/* Got err, ensure we take abort */
transfer->timeout = now;
@@ -427,9 +555,12 @@ 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"),
transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
len = 0;
{
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
transfer->file->filename, pretty_addr);
len = 0;
endcon = 1;
}
}
if (len != 0)
@@ -438,6 +569,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
if (endcon || len == 0)
{
if (!endcon)
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), transfer->file->filename, pretty_addr);
/* unlink */
*up = tmp;
free_transfer(transfer);
@@ -446,7 +579,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
}
up = &transfer->next;
}
}
}
static void free_transfer(struct tftp_transfer *transfer)
@@ -486,8 +619,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 +664,50 @@ 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))
{
transfer->expansion++;
if (size != transfer->blocksize)
size++; /* room in this block */
else if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next 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-2011 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,182 @@
#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
#if defined(LOCALEDIR) || defined(HAVE_IDN)
#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 */
static u32 seed[32];
static u32 in[12];
static u32 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)
{
u32 t[12]; u32 x; u32 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((unsigned char)c) && iscntrl((unsigned char)c))
/* iscntrl only gives expected results for ascii */
return 0;
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
else if (!isascii((unsigned char)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;
#if defined(LOCALEDIR) || defined(HAVE_IDN)
int rc;
#endif
if (nomem)
*nomem = 0;
if (!check_name(in))
return NULL;
#if defined(LOCALEDIR) || defined(HAVE_IDN)
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 +229,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);
@@ -258,7 +333,15 @@ int prettyprint_addr(union mysockaddr *addr, char *buf)
}
else if (addr->sa.sa_family == AF_INET6)
{
char name[IF_NAMESIZE];
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
if (addr->in6.sin6_scope_id != 0 &&
if_indextoname(addr->in6.sin6_scope_id, name) &&
strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
{
strcat(buf, "%");
strcat(buf, name);
}
port = ntohs(addr->in6.sin6_port);
}
#else
@@ -288,7 +371,8 @@ void prettyprint_time(char *buf, unsigned int t)
}
/* in may equal out, when maxlen may be -1 (No max len). */
/* in may equal out, when maxlen may be -1 (No max len).
Return -1 for extraneous no-hex chars found. */
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type)
{
@@ -300,7 +384,10 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
while (maxlen == -1 || i < maxlen)
{
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++)
if (*r != '*' && !isxdigit((unsigned char)*r))
return -1;
if (*r == 0)
maxlen = i;
@@ -332,14 +419,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 +521,4 @@ int read_write(int fd, unsigned char *packet, int size, int rw)
}
return 1;
}