Compare commits

...

17 Commits

Author SHA1 Message Date
Simon Kelley
984d2fded6 CHANGELOG update 2012-01-06 14:34:32 +00:00
Simon Kelley
a4f04ed45a Generate version string from git automatically 2012-01-06 11:47:02 +00:00
Simon Kelley
07736e8dcb VERSION file 2012-01-05 22:00:08 +00:00
Simon Kelley
00fc082d68 bump version in config.h 2012-01-05 21:42:12 +00:00
Simon Kelley
c72daea868 Accumulated 2.60 changes going into git 2012-01-05 21:33:27 +00:00
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
96 changed files with 22423 additions and 11395 deletions

3
Android.mk Normal file
View File

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

3498
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

23
FAQ
View File

@@ -303,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
@@ -354,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
@@ -381,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
@@ -413,10 +413,11 @@ A: Change your kernel configuration: either deselect CONFIG_SECURITY
_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?
A: Dnsmasq is in Suse itself, and the latest releases are also
available at ftp://ftp.suse.com/pub/people/ug/
Q: Where can I get .rpms Suitable for openSUSE/SLES?
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?
@@ -456,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-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,38 +10,48 @@
# 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`
LUA_CFLAGS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
LUA_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi`
VERSION= -DVERSION='\"`../bld/get-version`\"'
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 dhcp6.o rfc3315.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="$(VERSION) $(DBUS_CFLAGS) $(IDN_CFLAGS) $(CT_CFLAGS) $(LUA_CFLAGS)" \
BUILD_LIBS="$(DBUS_LIBS) $(IDN_LIBS) $(CT_LIBS) $(LUA_LIBS) $(SUNOS_LIBS)" \
-f ../Makefile dnsmasq
clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
@@ -55,26 +65,40 @@ install-common :
$(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
all-i18n :
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; \
@cd $(SRC) && $(MAKE) \
I18N=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
BUILD_CFLAGS="$(VERSION) $(DBUS_CFLAGS) $(CT_CFLAGS) $(LUA_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DBUS_LIBS) $(CT_LIBS) $(LUA_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 --no-wrap -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

1
VERSION Normal file
View File

@@ -0,0 +1 @@
$Format:%d$

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

28
bld/get-version Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/sh
# Determine the version string to build into a binary.
# When building in the git repository, we can use the output
# of "git describe" which gives an unequivocal answer.
#
# Failing that, we use the contents of the VERSION file
# which has a set of references substituted into it by git.
# If we can find one which matches $v[0-9].* then we assume it's
# a version-number tag, else we just use the whole string.
# we're called with pwd == src
cd ..
if [ -d .git ]; then
git describe
else
vers=`cat VERSION | sed 's/[(), ]/\n/ g' | grep -m 1 $v[0-9]`
if [ $? -eq 0 ]; then
echo ${vers#v}
else
cat VERSION
fi
fi
exit 0

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 "^\#[[:space:]]*define[[:space:]]*$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>

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

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. */
{

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

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

1010
debian/changelog vendored Normal file

File diff suppressed because it is too large Load Diff

6
debian/conffiles vendored Normal file
View File

@@ -0,0 +1,6 @@
/etc/init.d/dnsmasq
/etc/default/dnsmasq
/etc/dnsmasq.conf
/etc/resolvconf/update.d/dnsmasq
/etc/dbus-1/system.d/dnsmasq.conf
/etc/insserv.conf.d/dnsmasq

41
debian/control vendored Normal file
View File

@@ -0,0 +1,41 @@
Source: dnsmasq
Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Standards-Version: 3.9.2
Package: dnsmasq
Architecture: all
Depends: netbase, adduser, dnsmasq-base(>= ${source:Version})
Suggests: resolvconf
Conflicts: resolvconf (<<1.15)
Description: Small caching DNS proxy and DHCP/TFTP server
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
not in the global DNS. The DHCP server integrates with the DNS
server and allows machines with DHCP-allocated addresses
to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic
DHCP leases and BOOTP/TFTP for network booting of diskless machines.
Package: dnsmasq-base
Architecture: any
Depends: ${shlibs:Depends}
Conflicts: dnsmasq (<<2.41)
Description: Small caching DNS proxy and DHCP/TFTP server
This package contains the dnsmasq executable and documentation, but
not the infrastructure required to run it as a system daemon. For
that, install the dnsmasq package.
Package: dnsmasq-utils
Architecture: linux-any
Depends: ${shlibs:Depends}
Conflicts: dnsmasq (<<2.40)
Description: Utilities for manipulating DHCP leases
Small utilities to query a DHCP server's lease database and
remove leases from it. These programs are distributed with dnsmasq
and may not work correctly with other DHCP servers.

21
debian/copyright vendored Normal file
View File

@@ -0,0 +1,21 @@
dnsmasq is Copyright (c) 2000-2010 Simon Kelley
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
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.
On Debian GNU/Linux systems, the text of the GNU general public license is
available in the file /usr/share/common-licenses/GPL-2 or
/usr/share/common-licenses/GPL-3
The Debian package of dnsmasq was created by Simon Kelley with assistance
from Lars Bahner.

18
debian/dbus.conf vendored Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy user="dnsmasq">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy context="default">
<deny own="uk.org.thekelleys.dnsmasq"/>
<deny send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
</busconfig>

33
debian/default vendored Normal file
View File

@@ -0,0 +1,33 @@
# This file has five functions:
# 1) to completely disable starting dnsmasq,
# 2) to set DOMAIN_SUFFIX by running `dnsdomainname`
# 3) to select an alternative config file
# by setting DNSMASQ_OPTS to --conf-file=<file>
# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for
# more configuration variables.
# 5) to stop the resolvconf package from controlling dnsmasq's
# idea of which upstream nameservers to use.
# For upgraders from very old versions, all the shell variables set
# here in previous versions are still honored by the init script
# so if you just keep your old version of this file nothing will break.
#DOMAIN_SUFFIX=`dnsdomainname`
#DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt"
# Whether or not to run the dnsmasq daemon; set to 0 to disable.
ENABLED=1
# By default search this drop directory for configuration options.
# Libvirt leaves a file here to make the system dnsmasq play nice.
# Comment out this line if you don't want this. The dpkg-* are file
# endings which cause dnsmasq to skip that file. This avoids pulling
# in backups made by dpkg.
CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new
# If the resolvconf package is installed, dnsmasq will use its output
# rather than the contents of /etc/resolv.conf to find upstream
# nameservers. Uncommenting this line inhibits this behaviour.
# Not that including a "resolv-file=<filename>" line in
# /etc/dnsmasq.conf is not enough to override resolvconf if it is
# installed: the line below must be uncommented.
#IGNORE_RESOLVCONF=yes

266
debian/init vendored Normal file
View File

@@ -0,0 +1,266 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: dnsmasq
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: DHCP and DNS server
### END INIT INFO
set +e # Don't exit on error status
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dnsmasq
NAME=dnsmasq
DESC="DNS forwarder and DHCP server"
# Most configuration options in /etc/default/dnsmasq are deprecated
# but still honoured.
ENABLED=1
if [ -r /etc/default/$NAME ]; then
. /etc/default/$NAME
fi
# Get the system locale, so that messages are in the correct language, and the
# charset for IDN is correct
if [ -r /etc/default/locale ]; then
. /etc/default/locale
export LANG
fi
test -x $DAEMON || exit 0
# Provide skeleton LSB log functions for backports which don't have LSB functions.
if [ -f /lib/lsb/init-functions ]; then
. /lib/lsb/init-functions
else
log_warning_msg () {
echo "${@}."
}
log_success_msg () {
echo "${@}."
}
log_daemon_msg () {
echo -n "${1}: $2"
}
log_end_msg () {
if [ $1 -eq 0 ]; then
echo "."
elif [ $1 -eq 255 ]; then
/bin/echo -e " (warning)."
else
/bin/echo -e " failed!"
fi
}
fi
# RESOLV_CONF:
# If the resolvconf package is installed then use the resolv conf file
# that it provides as the default. Otherwise use /etc/resolv.conf as
# the default.
#
# If IGNORE_RESOLVCONF is set in /etc/default/dnsmasq or an explicit
# filename is set there then this inhibits the use of the resolvconf-provided
# information.
#
# Note that if the resolvconf package is installed it is not possible to
# override it just by configuration in /etc/dnsmasq.conf, it is necessary
# to set IGNORE_RESOLVCONF=yes in /etc/default/dnsmasq.
if [ ! "$RESOLV_CONF" ] &&
[ "$IGNORE_RESOLVCONF" != "yes" ] &&
[ -x /sbin/resolvconf ]
then
RESOLV_CONF=/var/run/dnsmasq/resolv.conf
fi
for INTERFACE in $DNSMASQ_INTERFACE; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -i $INTERFACE"
done
for INTERFACE in $DNSMASQ_EXCEPT; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -I $INTERFACE"
done
if [ ! "$DNSMASQ_USER" ]; then
DNSMASQ_USER="dnsmasq"
fi
start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
fi
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /var/run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
${DNSMASQ_INTERFACES:+ $DNSMASQ_INTERFACES} \
${DHCP_LEASE:+ -l $DHCP_LEASE} \
${DOMAIN_SUFFIX:+ -s $DOMAIN_SUFFIX} \
${RESOLV_CONF:+ -r $RESOLV_CONF} \
${CACHESIZE:+ -c $CACHESIZE} \
${CONFIG_DIR:+ -7 $CONFIG_DIR} \
${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} \
|| return 2
}
start_resolvconf()
{
# If interface "lo" is explicitly disabled in /etc/default/dnsmasq
# Then dnsmasq won't be providing local DNS, so don't add it to
# the resolvconf server set.
for interface in $DNSMASQ_EXCEPT
do
[ $interface = lo ] && return
done
if [ -x /sbin/resolvconf ] ; then
echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.$NAME
fi
return 0
}
stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /var/run/dnsmasq/$NAME.pid --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
return "$RETVAL"
}
stop_resolvconf()
{
if [ -x /sbin/resolvconf ] ; then
/sbin/resolvconf -d lo.$NAME
fi
return 0
}
status()
{
# Return
# 0 if daemon is running
# 1 if daemon is dead and pid file exists
# 3 if daemon is not running
# 4 if daemon status is unknown
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
case "$?" in
0) [ -e "/var/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
1) return 0 ;;
*) return 4 ;;
esac
}
case "$1" in
start)
test "$ENABLED" != "0" || exit 0
log_daemon_msg "Starting $DESC" "$NAME"
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
1)
log_success_msg "(already running)"
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
stop)
stop_resolvconf
if [ "$ENABLED" != "0" ]; then
log_daemon_msg "Stopping $DESC" "$NAME"
fi
stop
RETVAL="$?"
if [ "$ENABLED" = "0" ]; then
case "$RETVAL" in
0) log_daemon_msg "Stopping $DESC" "$NAME"; log_end_msg 0 ;;
esac
exit 0
fi
case "$RETVAL" in
0) log_end_msg 0 ; exit 0 ;;
1) log_warning_msg "(not running)" ; exit 0 ;;
*) log_end_msg 1; exit 1 ;;
esac
;;
restart|force-reload)
test "$ENABLED" != "0" || exit 1
$DAEMON --test ${CONFIG_DIR:+ -7 $CONFIG_DIR} ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} >/dev/null 2>&1
if [ $? -ne 0 ]; then
NAME="configuration syntax check"
RETVAL="2"
else
stop_resolvconf
stop
RETVAL="$?"
fi
log_daemon_msg "Restarting $DESC" "$NAME"
case "$RETVAL" in
0|1)
sleep 2
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
status)
log_daemon_msg "Checking $DESC" "$NAME"
status
case "$?" in
0) log_success_msg "(running)" ; exit 0 ;;
1) log_success_msg "(dead, pid file exists)" ; exit 1 ;;
3) log_success_msg "(not running)" ; exit 3 ;;
*) log_success_msg "(unknown)" ; exit 4 ;;
esac
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|status}" >&2
exit 3
;;
esac
exit 0

1
debian/insserv vendored Normal file
View File

@@ -0,0 +1 @@
$named dnsmasq

49
debian/postinst vendored Normal file
View File

@@ -0,0 +1,49 @@
#!/bin/sh
set -e
# create a user to run as (code stolen from dovecot-common)
if [ "$1" = "configure" ]; then
if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then
adduser --system --home /var/lib/misc --gecos "dnsmasq" \
--no-create-home --disabled-password \
--quiet dnsmasq || true
fi
# Make the directory where we keep the pid file - this
# has to be owned by "dnsmasq" do that the file can be unlinked.
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq
chown dnsmasq:nogroup /var/run/dnsmasq
fi
# handle new location of pidfile during an upgrade
if [ -e /var/run/dnsmasq.pid ]; then
mv /var/run/dnsmasq.pid /var/run/dnsmasq
fi
fi
if [ -x /etc/init.d/dnsmasq ]; then
update-rc.d dnsmasq defaults 15 85 >/dev/null
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then
if [ -e /var/run/dnsmasq/dnsmasq.pid ]; then
ACTION=restart
else
ACTION=start
fi
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq $ACTION || true
else
/etc/init.d/dnsmasq $ACTION || true
fi
fi
fi
# dpkg can botch the change of /usr/share/doc/dnsmasq from
# directory to symlink. Fix up here.
if [ ! -h /usr/share/doc/dnsmasq ] && { rmdir /usr/share/doc/dnsmasq; }; then
cd /usr/share/doc/
ln -s /usr/share/doc/dnsmasq-base dnsmasq
fi

12
debian/postrm vendored Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
set -e
if [ purge = "$1" ]; then
update-rc.d dnsmasq remove >/dev/null
if [ -x "$(command -v deluser)" ]; then
deluser --quiet --system dnsmasq > /dev/null || true
else
echo >&2 "not removing dnsmasq system account because deluser command was not found"
fi
rm -rf /var/run/dnsmasq
fi

14
debian/prerm vendored Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ "$1" = "remove" ]; then
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq stop || true
else
/etc/init.d/dnsmasq stop || true
fi
fi
exit 0

70
debian/readme vendored Normal file
View File

@@ -0,0 +1,70 @@
Notes on configuring dnsmasq as packaged for Debian.
(1) To configure dnsmasq edit /etc/dnsmasq.conf. The file is well
commented; see also the dnsmasq.8 man page for explanation of
the options. The file /etc/default/dnsmasq also exists but it
shouldn't need to be touched in most cases. To set up DHCP
options you might need to refer to a copy of RFC 2132. This is
available on Debian systems in the package doc-rfc-std as the file
/usr/share/doc/RFC/draft-standard/rfc2132.txt.gz .
(2) Installing the dnsmasq package also creates the directory
/etc/dnsmasq.d which is searched by dnsmasq for configuration file
fragments. This behaviour can be disabled by editing
/etc/default/dnsmasq.
(3) If the Debian resolvconf package is installed then, regardless
of what interface configuration daemons are employed, the list of
nameservers to which dnsmasq should forward queries can be found
in /var/run/dnsmasq/resolv.conf; also, 127.0.0.1 is listed as the
first nameserver address in /etc/resolv.conf. This works using the
default configurations of resolvconf and dnsmasq.
(4) In the absence of resolvconf, if you are using dhcpcd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/dhcpc/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(5) In the absence of resolvconf, if you are using pppd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/ppp/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(6) In the absence of resolvconf, dns-nameservers lines in
/etc/network/interfaces are ignored. If you do do not use
resolvconf, list 127.0.0.1 as the first nameserver address
in /etc/resolv.conf and configure your nameservers using
"server=<IP-address>" lines in /etc/dnsmasq.conf.
(7) If you run multiple DNS servers on a single machine, each
listening on a different interface, then it is necessary to use
the bind-interfaces option by uncommenting "bind-interfaces" in
/etc/dnsmasq.conf. This option stops dnsmasq from binding the
wildcard address and allows servers listening on port 53 on
interfaces not in use by dnsmasq to work. The Debian
libvirt package will add a configuration file in /etc/dnsmasq.d
which does this so that the "system" dnsmasq and "private" dnsmasq
instances started by libvirt do not clash.
(8) The following options are supported in DEB_BUILD_OPTIONS
noopt : compile without optimisation.
nostrip : don't remove symbols from binary.
nodocs : omit documentation.
notftp : omit TFTP support.
nodhcp : omit DHCP support.
noscript : omit lease-change script support.
noipv6 : omit IPv6 support.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be
combined with noi18n to be effective.
(9) Dnsmasq comes as two packages - dnsmasq-base and
dnsmasq. dnsmasq-base provides the dnsmasq executable and
documentation (including this file). Dnsmasq, which depends on
dnsmasq-base, provides the init script and configuration
infrastructure. This file assumes that both are installed. It is
possible to install only dnsmasq-base and use dnsmasq as a
non-"system" daemon. Libvirt, for instance, does this.

7
debian/readme.dnsmasq.d vendored Normal file
View File

@@ -0,0 +1,7 @@
# All files in this directory will be read by dnsmasq as
# configuration files, except if their names end in
# ".dpkg-dist",".dpkg-old" or ".dpkg-new"
#
# This can be changed by editing /etc/default/dnsmasq

70
debian/resolvconf vendored Normal file
View File

@@ -0,0 +1,70 @@
#!/bin/bash
#
# Script to update the resolver list for dnsmasq
#
# N.B. Resolvconf may run us even if dnsmasq is not running.
# If dnsmasq is installed then we go ahead and update
# the resolver list in case dnsmasq is started later.
#
# Assumption: On entry, PWD contains the resolv.conf-type files
#
# Requires bash because it uses a non-POSIX printf extension.
#
# Licensed under the GNU GPL. See /usr/share/common-licenses/GPL.
#
set -e
RUN_DIR="/var/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
[ -x /usr/sbin/dnsmasq ] || exit 0
[ -x /lib/resolvconf/list-records ] || exit 1
PATH=/bin:/sbin
report_err() { echo "$0: Error: $*" >&2 ; }
# Stores arguments (minus duplicates) in RSLT, separated by spaces
# Doesn't work properly if an argument itself contain whitespace
uniquify()
{
RSLT=""
while [ "$1" ] ; do
for E in $RSLT ; do
[ "$1" = "$E" ] && { shift ; continue 2 ; }
done
RSLT="${RSLT:+$RSLT }$1"
shift
done
}
if [ ! -d "$RUN_DIR" ] && ! mkdir --parents --mode=0755 "$RUN_DIR" ; then
report_err "Failed trying to create directory $RUN_DIR"
exit 1
fi
RSLVCNFFILES="$(/lib/resolvconf/list-records | sed -e '/^lo.dnsmasq$/d')"
NMSRVRS=""
if [ "$RSLVCNFFILES" ] ; then
uniquify $(sed -n -e 's/^[[:space:]]*nameserver[[:space:]]\+//p' $RSLVCNFFILES)
NMSRVRS="$RSLT"
fi
# Dnsmasq uses the mtime of $RSLVRLIST_FILE, with a resolution of one second,
# to detect changes in the file. This means that if a resolvconf update occurs
# within one second of the previous one then dnsmasq may fail to notice the
# more recent change. To work around this problem we sleep here to ensure
# that the new mtime is different.
if [ -f "$RSLVRLIST_FILE" ] && [ "$(ls -go --time-style='+%s' "$RSLVRLIST_FILE" | { read p h s t n ; echo "$t" ; })" = "$(date +%s)" ] ; then
sleep 1
fi
clean_up() { rm -f "$TMP_FILE" ; }
trap clean_up EXIT
: >| "$TMP_FILE"
for N in $NMSRVRS ; do echo "nameserver $N" >> "$TMP_FILE" ; done
mv -f "$TMP_FILE" "$RSLVRLIST_FILE"

13
debian/resolvconf-package vendored Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Resolvconf packaging event hook script for the dnsmasq package
restart_dnsmasq() {
if which invoke-rc.d >/dev/null 2>&1 ; then
invoke-rc.d dnsmasq restart
elif [ -x /etc/init.d/dnsmasq ] ; then
/etc/init.d/dnsmasq restart
fi
}
case "$1" in
install) restart_dnsmasq ;;
esac

186
debian/rules vendored Executable file
View File

@@ -0,0 +1,186 @@
#!/usr/bin/make -f
# debian/rules file - for dnsmasq.
# Copyright 2001-2011 by Simon Kelley
# Based on the sample in the debian hello package which carries the following:
# Copyright 1994,1995 by Ian Jackson.
# I hereby give you perpetual unlimited permission to copy,
# modify and relicense this file, provided that you do not remove
# my name from the file itself. (I assert my moral right of
# paternity under the Copyright, Designs and Patents Act 1988.)
# This file may have to be extensively modified
package=dnsmasq-base
# policy manual, section 10.1
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
CFLAGS = -g -O0 -Wall -W
else
CFLAGS = -g -O2 -Wall -W
endif
COPTS =
TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
ifeq (,$(findstring nodbus,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DBUS
endif
ifeq (,$(findstring noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_CONNTRACK
endif
endif
ifneq (,$(findstring noipv6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPV6
endif
ifneq (,$(findstring notftp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_TFTP
endif
ifneq (,$(findstring nodhcp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP
endif
ifneq (,$(findstring noscript,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_SCRIPT
endif
ifneq (,$(findstring nortc,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_BROKEN_RTC
endif
ifneq (,$(findstring noi18n,$(DEB_BUILD_OPTIONS)))
TARGET = install
ifeq (,$(findstring noidn, $(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_IDN
endif
endif
clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
make clean
make -C contrib/wrt clean
binary-indep: checkroot
$(checkdir)
rm -rf debian/daemon
install -m 755 \
-d debian/daemon/DEBIAN \
-d debian/daemon/usr/share/doc \
-d debian/daemon/etc/init.d \
-d debian/daemon/etc/dnsmasq.d \
-d debian/daemon/etc/resolvconf/update.d \
-d debian/daemon/usr/lib/resolvconf/dpkg-event.d \
-d debian/daemon/etc/default \
-d debian/daemon/etc/dbus-1/system.d \
-d debian/daemon/lib/systemd/system \
-d debian/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/daemon/DEBIAN
install -m 755 debian/postinst debian/postrm debian/prerm debian/daemon/DEBIAN
install -m 755 debian/init debian/daemon/etc/init.d/dnsmasq
install -m 755 debian/resolvconf debian/daemon/etc/resolvconf/update.d/dnsmasq
install -m 755 debian/resolvconf-package debian/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq
install -m 644 debian/default debian/daemon/etc/default/dnsmasq
install -m 644 dnsmasq.conf.example debian/daemon/etc/dnsmasq.conf
install -m 644 debian/readme.dnsmasq.d debian/daemon/etc/dnsmasq.d/README
install -m 644 debian/dbus.conf debian/daemon/etc/dbus-1/system.d/dnsmasq.conf
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol -pdnsmasq -Pdebian/daemon
chown -R root.root debian/daemon
chmod -R g-ws debian/daemon
dpkg --build debian/daemon ..
binary-arch: checkroot
$(checkdir)
rm -rf debian/base
install -m 755 \
-d debian/base/DEBIAN \
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
gzip -9 debian/base/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive
gzip -9 debian/base/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface
endif
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -9 debian/base/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian
install -m 644 debian/copyright debian/base/usr/share/doc/$(package)/copyright
gzip -9 debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9 $$f/man8/dnsmasq.8 ; \
fi \
done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps debian/base/usr/sbin/dnsmasq
dpkg-gencontrol -pdnsmasq-base -Pdebian/base
chown -R root.root debian/base
chmod -R g-ws debian/base
dpkg --build debian/base ..
ifeq ($(DEB_BUILD_ARCH_OS),linux)
rm -rf debian/utils
install -m 755 -d debian/utils/DEBIAN \
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" COPTS="$(COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1
install -m 755 contrib/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/man/man1/dhcp_lease_time.1
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
chown -R root.root debian/utils
chmod -R g-ws debian/utils
dpkg --build debian/utils ..
endif
define checkdir
test -f Makefile -a -f debian/rules
endef
# Below here is fairly generic really
binary: binary-arch binary-indep
build:
checkroot:
test root = "`whoami`"
.PHONY: binary binary-arch binary-indep clean checkroot

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
1.0

31
debian/systemd.service vendored Normal file
View File

@@ -0,0 +1,31 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test
# Enable DBus by default because we use DBus activation.
#
# Drop privileges and become the 'dnsmasq' user. It is recommended by dnsmasq
# upstream to run dnsmasq as an isolated user that does not run any other
# processes, owns no files and has no shell. The default 'nobody' user has a
# shell and might be used for other processes.
#
# Debian-specific: add /etc/dnsmasq.d to config search path (with the exception
# of .dpkg-*). Packages such as libvirt leave config files there.
#
# --pid-file without argument disables writing a PIDfile, we don't need one.
ExecStart=/usr/sbin/dnsmasq -k \
--enable-dbus \
--user=dnsmasq \
-7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new \
--pid-file
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

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,
@@ -141,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
@@ -159,13 +172,13 @@
# 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
# 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
# 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.
@@ -188,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
@@ -232,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.
@@ -250,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
@@ -276,15 +289,15 @@
# 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 some or all of them if you use
# 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)
@@ -298,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
@@ -318,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
@@ -329,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
@@ -371,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.
@@ -409,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.
@@ -439,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

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

View File

@@ -17,9 +17,8 @@ resueltos. Tambi
vía DHCP.
.PP
El servidor DHCP dnsmasq incluye soporte para asignación de direcciones
estáticas, redes múltiples, DHCP-relay y especificadores de subredes
RFC3011. Automáticamente envía un predeterminado sensible de opciones
DHCP, y puede ser configurado para enviar cualquier opciones DHCP deseadas,
estáticas y redes múltiples. Automáticamente envía un predeterminado sensible de
opciones DHCP, y puede ser configurado para enviar cualquier opciones DHCP deseadas,
incluyendo opciones encapsuladas por vendedores. Incluye un servidor seguro
TFTP solo-lectura para permitir el inicio vía red/PXE de hosts DHCP. Tambíen
incluye soporte para BOOTP.
@@ -33,17 +32,25 @@ archivo PID. En BSD, a menos que la librer
la forma larga de las opciones no funciona en la línea de comandos,
pero todavía es reconocida en el archivo de configuración.
.TP
.B --test
Leer archivo(s) de configuración y revisar su sintaxis. Salir con código
0 si todo está bien, o un código no-cero en cualquier otro caso. No
iniciar dnsmasq.
.TP
.B \-h, --no-hosts
No leer los nombres de hosts en /etc/hosts.
.TP
.B \-H, --addn-hosts=<archivo>
Archivo de hosts adicional. Leer el archivo especificado adicionalmente
a /etc/hosts. Si se brinda -h, leer solo el archivo especificado. Esta
opción puede ser repetida para más de un archivo de hosts adicional.
opción puede ser repetida para más de un archivo de hosts adicional. Si
un directorio es brindado, entonces leer todos los archivos contenidos en
ese directorio.
.TP
.B \-E, --expand-hosts
Agregar el dominio a nombres sencillos (sin punto) en /etc/hosts de la
misma manera que con nombres derivados de DHCP.
misma manera que con nombres derivados de DHCP. Nótese que esto no
aplica a nombres de dominio en cnames, expedientes PTR, TXT, etc.
.TP
.B \-T, --local-ttl=<tiempo>
Al responder con información desde /etc/hosts o desde el archivo
@@ -61,8 +68,14 @@ informaci
dnsmasq usa para hacer caché. Si las respuestas de servidores upstream
omiten esta información, dnsmasq no mete la respuesta en el caché.
Esta opción brinda un valor predeterminado para el time-to-live que
dnsmasq usa para meter respuestas en el caché aún en la ausencia de
un expediente SOA.
dnsmasq usa para meter respuestas negativas en el caché aún en la
ausencia de un expediente SOA.
.TP
.B --max-ttl=<tiempo>
Fijar un valor TTL (tiempo de vida) máximo que será entregado a
clientes. El TTL máximo especificado será otorgado a clientes en vez
del TTL verdadero si es menor. El valor TTL real es mantenido en el caché
para prevenir la inundación de los servidores DNS upstream.
.TP
.B \-k, --keep-in-foreground
No ir hacia el fondo al iniciar, pero aparte de eso ejecutar como
@@ -84,7 +97,8 @@ Fijar la facilidad a la cual dnsmasq deber
esto es DAEMON por predeterminado, y LOCAL0 cuando el modo debug está
en operación. Si la facilidad brindada contiene por lo menos un carácter
"/", se trata como un nombre de archivo, y dnsmasq bitacoreará a dicho
archivo, en vez de syslog. (Errores durante la lectura de la configuración
archivo, en vez de syslog. Si la facilidad es '-' entonces dnsmasq
bitacorea a stderr. (Errores durante la lectura de la configuración
irán a syslog todavía, pero todo output desde un inicio exitoso, y todo
output mientras en ejecución, irá a este archivo exclusivamente.)
Al bitacorear a un archivo, dnsmasq cerrará y reabrirá el archivo al
@@ -127,8 +141,8 @@ solo DHCP y/o TFTP.
.TP
.B \-P, --edns-packet-max=<tamaño>
Especificar el paquete UDP EDNS.0 más grande que es soportado por
el reenviador DNS. Por predeterminado es 1280, lo cual es el
máximo recomendado en RFC2671 para ethernet.
el reenviador DNS. Por predeterminado es 4096, lo cual es el
tamaño recomendado en RFC5625.
.TP
.B \-Q, --query-port=<puerto>
Enviar búsquedas outbound desde, y escuchar por respuestas en,
@@ -225,15 +239,19 @@ privados (192.168.x.x, etc.) los cuales no se encuentren en
/etc/hosts o en el archivo de arriendos DHCP es respondida con
"dominio no existente" en vez de ser reenviada upstream.
.TP
.B \-V, --alias=<IP viejo>,<IP nuevo>[,<máscara>]
.B \-V, --alias=[<IP viejo>]|[<IP inicio>-<IP final>],<IP nuevo>[,<máscara>]
Modificar direcciones IPv4 retornadas desde servidores DNS upstream;
<IP viejo> es remplazado con <IP nuevo>. Si la máscara opcional
es brindada, entonces cualquier dirección que coincida con el
<IP viejo> enmascarado será re-escrita. Así que, por ejemplo,
.B --alias=1.2.3.0,6.7.8.0,255.255.255.0 trazará 1.2.3.56 a 6.7.8.56
y 1.2.3.67 a 6.7.8.67. Esto es lo que
ruteadores Cisco PIX llaman "DNS doctoring".
.TP
ruteadores Cisco PIX llaman "DNS doctoring". Si la dirección vieja es
brindada como un rango, entonces solo direcciones en ese rango, y no
la subred entera, son re-escritas. De tal manera que
.B --alias=192.168.0.10-192.168.0.40,10.0.0.0,255.255.255.0
relaciona 192.168.0.10->192.168.0.40 a 10.0.0.10->10.0.0.40
.TP
.B \-B, --bogus-nxdomain=<dirección IP>
Transformar respuestas que contienen la dirección IP brindada a
respuestas tipo "Dominio no existe". La intención de esto es actuar
@@ -293,6 +311,17 @@ Denegar (y bitacorear) direcciones de servidores upstream que est
dentro de rangos IP privados. Esto bloquea un ataque donde un navegador
detrás de un firewall es usado para analizar máquinas en la red local.
.TP
.B --rebind-localhost-ok
Eximir a 127.0.0.0/8 de verificaciones de rebinding. Este rango de
direcciones es retornado por servidores de tiempo real tipo hoyo
negro, así que bloquearlo puede deshabilitar estos servicios.
.TP
.B --rebind-domain-ok=[<domain>]|[[/<domain>/[<domain>/]
No detectar y bloquear dns-rebind en búsquedas a estos dominios. El
argumento puede ser o un dominio sencillo, o múltiples dominios
rodeados por '/', como el syntax de --server, por ejemplo
.B --rebind-domain-ok=/dominio1/dominio2/dominio3/
.TP
.B \-n, --no-poll
No revisar periodicamente a /etc/resolv.conf en busca de cambios.
.TP
@@ -328,6 +357,20 @@ ser especificado como parte de la direcci
#. Más de una opción -S es permitida, con partes de dominio o
dirección IP repetidas como sea necesario.
Dominios más específicos toman precedencia sobre los menos específicos,
así que:
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/2.3.4.5
enviará búsquedas por *.google.com hacia 1.2.3.4, excepto
*www.google.com, el cual irá a 2.3.4.5.
La dirección especial de servidor '#' significa "usar los servidores
estándares", así que
.B --server=/google.com/1.2.3.4
.B --server=/www.google.com/#
enviará búsquedas por *.google.com hacia 1.2.3.4, excepto
*www.google.com, el cual será reenviado de manera usual.
También se permite una opción -S la cual brinda un dominio pero
ninguna dirección IP; esto le dice a dnsmasq que un dominio es local
y puede responder a búsquedas desde /etc/hosts o DHCP pero nunca
@@ -449,41 +492,51 @@ de casos. La
es al usar resolvedores de bitácoras de servidores web, los cuales pueden
generar un número inmenso de búsquedas simultáneas.
.TP
.B \-F, --dhcp-range=[[net:]network-id,]<dirección-inicio>,<dirección-final>[[,<máscara>],<broadcast>][,<tiempo de arriendo predeterminado>]
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<dirección-inicio>,<dirección-final>[,<netmask>[,<broadcast>]][,<tiempo de arriendo>]
Habilitar el servidor DHCP. Direcciones serán distribuidas desde el
rango <dirección-inicio> hasta <dirección-final> y desde direcciones definidas
estáticamente en opciones
.B dhcp-host
Si el tiempo de arriendo es especificado, entonces arriendos serán
otorgados por esa cantidad de tiempo. El tiempo de arriendo es en
segundos, o minutos (por ejemplo, 45m), o horas (por ejemplo, 1h), o el
literal "infinite". Esta opción puede ser repetida, con diferentes
segundos, o minutos (por ejemplo, 45m), u horas (por ejemplo, 1h), o
"infinite". Si no es brindada, el tiempo de arriendo predeterminado
es de una hora. El tiempo de arriendo mínimo es de dos minutos.
Esta opción puede ser repetida, con diferentes
direcciones, para habilitar servicio DHCP en más de una red. Para
redes conectadas diréctamente (en otras palabras, redes en las
cuales la máquina corriendo dnsmasq tiene una interface) la
máscara de subred es opcional. Pero, es requerida para redes que
reciben servicio DHCP vía un agente de relay. La dirección de
broadcast siempre es opcional. En algunos sistemas rotos, dnsmasq
solo puede escuchar en una interface cuando se usa DHCP, y el
nombre de esa interface debe ser brindado usando la opción
.B interface
Esta limitación actualmente afecta a OpenBSD antes de versión 4.0.
Siempre se permite tener más de un rango dhcp (dhcp-range) en una
subred. El parámetro opcional network-id es una etiqueta alfanumérica
la cual marca esta red de tal forma que opciones dhcp puedan ser
especificadas en base a cada red.
Cuando es prefijada con 'net:' entonces el significado cambia
broadcast siempre es opcional. Siempre se permite tener más de
un rango dhcp (dhcp-range) en una subred.
El parámetro opcional
.B set:<tag>
fija una etiqueta alfanumérica la cual marca esta red de
tal forma que opciones dhcp puedan ser especificadas en base a cada red.
Cuando es prefijada con 'tag:' en vez, entonces el significado cambia
de "fijar etiqueta" a "coincidir con etiqueta". Solo una etiqueta puede
ser fijada, pero más de una puede ser revisada por coincidencias. La
dirección final puede ser remplazada por la palabra clave
.B static
la cual le dice a dnsmasq que debe habilitar DHCP para la red
especificada, pero no alocar dinámicamente direcciones IP.
especificada, pero no alocar dinámicamente direcciones IP:
Solo hosts que tienen direcciones estáticas brindadas vía
.B dhcp-host
o desde /etc/ethers serán servidas.
o desde /etc/ethers serán servidas. La dirección final puede ser
remplazada por la palabra clave
.B proxy
caso en el cual dnsmasq proveerá proxy-DHCP en la subred especificada. (Ver
.B pxe-prompt
y
.B pxe-service
para detalles.)
La sección interface:<interface name> no es normalmente usada. Ver la
sección NOTAS para detalles sobre esto.
.TP
.B \-G, --dhcp-host=[<dirección de hardware>][,id:<client_id>|*][,net:<netid>][,<dirección IP>][,<nombre de host>][,<tiempo de arriendo>][,ignore]
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<tiempo_de_arriendo>][,ignorar]
Especificar parámetros por host para el servidor DHCP. Esto permite
que una máquina con una dirección de hardware particular sea siempre
alocada el mismo nombre de host, dirección IP, y tiempo de arriendo.
@@ -498,10 +551,15 @@ le dice a dnsmasq que debe darle a la m
ethernet 00:20:e0:3b:13:af el nombre wap, y un arriendo DHCP infinito.
.B --dhcp-host=lap,192.168.0.199
le dice a dnsmasq que siempre debe alocarle a la maquina lap
la dirección IP 192.168.0.199. Direcciones alocadas de esta manera
no tienen que estar dentro del rango dado con la opción --dhcp-range,
pero deben estar en la red siendo servida por el servidor DHCP. Se
permite usar identificadores de clientes en vez de direcciones de
la dirección IP 192.168.0.199.
Direcciones alocadas de esta manera no tienen que estar dentro
del rango dado con la opción --dhcp-range, pero deben estar en la subred
de un rango DHCP (dhcp-range) válido. Para subredes que no necesitan
una collección de direcciones dinamicamente alocadas, usar la palabra
clave "static" in la declaración dhcp-range.
Es permitido usar identificadores de cliente en vez de direcciones de
hardware para identificar hosts prefijando 'id:'. O sea que:
.B --dhcp-host=id:01:02:03:04,.....
se refiere al host con identificador de cliente 01:02:03:04.
@@ -515,7 +573,14 @@ presenta un ID de cliente algunas veces pero otras no.
Si un nombre aparece en /etc/hosts, la dirección asociada puede
ser alocada a un arriendo DHCP, pero solo si existe una opción
.B --dhcp-host
la cual especifica el nombre también. La palabra clave "ignore"
la cual especifica el nombre también. Solo un hostname puede ser
brindado en una opción
.B dhcp-host
pero aliases son posibles por medio del uso de CNAMEs. (Ver
.B --cname
).
La palabra clave "ignore"
le dice a dnsmasq que no debe ofrecer jamás un arriendo DHCP a
una máquina. La máquina puede ser especificada por dirección de
hardware, ID de cliente, o nombre de host, por ejemplo:
@@ -523,14 +588,16 @@ hardware, ID de cliente, o nombre de host, por ejemplo:
Esto es útil cuando hay otro servidor DHCP en la red que debe ser
usado por algúnas máquinas.
El net:<network-id> fija la etiqueta network-id cuando sea que
El set:<tag> fija la etiqueta cuando sea que
esta directiva dhcp-host está en uso. Esto puede ser usado para
enviar selectivamente opciones DHCP a este host. Cuando un host
coincide con cualquier directiva dhcp-host (o una implicada por
/etc/ethers) entonces la etiqueta network-id especial "known" es
enviar selectivamente opciones DHCP a este host. Más de una etiqueta
puede ser fijada en una directiva dhcp-host (pero no en otros lugares
donde "set:<tag>" es permitido). Cuando un host coincide con
cualquier directiva dhcp-host (o una implicada por
/etc/ethers) entonces la etiqueta especial "known" es
fijada. Esto permite que dnsmasq sea configurado para ignorar
pedidos desde máquinas desconocidas usando
.B --dhcp-ignore=#known
.B --dhcp-ignore=tag:!known
Direcciones ethernet (pero no client-ids) pueden tener bytes
comodínes, así que por ejemplo
.B --dhcp-host=00:20:e0:3b:13:*,ignore
@@ -546,14 +613,16 @@ solo coincidir
el tipo ARP para Token-Ring es 6.
Como caso especial, es posible incluir más de una dirección de
hardware. Esto permite que una dirección IP sea asociada con
hardware. Ejemplo:
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
Esto permite que una dirección IP sea asociada con
direcciones de hardware múltiples, y le brinda a dnsmasq permiso
para abandonar un arriendo DHCP a una de las direcciones de hardware
cuando otra pide un arriendo. Nótese que esto es algo peligroso,
sólo funcionará dependiblemente si una de las direcciones de hardware
está activa en cualquier momento y dnsmasq no tiene forma de enforzar
esto. Pero es útil, por ejemplo, para alocar una dirección IP estable
a una laptop que tiene interfaces alámbricas e inalámbricas.
a una laptop que tiene interface alámbrica e inalámbrica.
.TP
.B --dhcp-hostsfile=<archivo>
Leer información host DHCP desde el archivo especificado. El archivo contiene información de un host por línea. El formato de una línea es igual que texto hacia la derecha de '=' en --dhcp-host. La ventaja de almacenar información host DHCP en este archivo es que puede ser cambiada sin tener que reiniciar dnsmasq. El archivo será re-leído cuando dnsmasq recibe un SIGHUP.
@@ -562,6 +631,11 @@ Leer informaci
Leer información sobre opciones DHCP desde el archivo especificado. La
ventaja de usar esta opción es la misma que con --dhcp-hostsfile: el
archivo dhcp-optsfile será re-leído cuando dnsmasq recibe un SIGHUP.
Nótese que es posible colocar la información mediante
.B --dhcp-boot
como opciones DHCP, usando los nombres de opción bootfile-name,
server-ip-address, y tftp-server. Esto permite que sean incluidas en
un archivo dhcp-optsfile.
.TP
.B \-Z, --read-ethers
Leer /etc/ethers en busca de información sobre hosts para el servidor
@@ -569,9 +643,10 @@ DHCP. El formato de /etc/ethers es una direcci
por ya sea un nombre de host o una dirección IP. Al ser leidas por
dnsmasq, estas líneas tienen exáctamente el mismo efecto que opciones
.B --dhcp-host
que contienen la misma información. /etc/ethers es re-leída cuando dnsmasq recibe un SIGHUP.
que contienen la misma información. /etc/ethers es re-leída cuando
dnsmasq recibe un SIGHUP.
.TP
.B \-O, --dhcp-option=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
Especificar opciones diferentes o extra a clientes DHCP. Por
predeterminado, dnsmasq envía algunas opciones estándar a clientes
DHCP. La máscara de subred y dirección broadcast son fijadas igual
@@ -596,9 +671,9 @@ o
La dirección especial 0.0.0.0 es entendida que significa "la
dirección de la máquina que corre dnsmasq". Tipos de data permitidos
son direcciones IP de cuatro segmentos, un número decimal, dígitos hex
separados por colones, y un string de texto. Si las network-ids
separados por colones, y un string de texto. Si las etiquetas
opcionales son brindadas, entonces esta opción es solo enviada cuando
todas las network-ids coinciden.
todas las etiquetas coinciden.
Procesamiento especial es llevado a cabo en un argumento de texto para
la opción 119, en conforme con RFC3397. Direcciones IP textuales o de
@@ -634,11 +709,23 @@ vendor-class (n
seleccionar opciones encapsuladas en preferencia sobre cualquiera enviada
por el cliente. Es posible omitir el vendorclass completamente;
.B --dhcp-option=vendor:,1,0.0.0.0
caso en el cuál la opción encapsulada siempre es enviada. La dirección
0.0.0.0 no es tratada de forma especial en opciones de clase de vendedor
encapsuladas.
caso en el cuál la opción encapsulada siempre es enviada.
Opciones pueden ser encapsuladas dentro de otras opciones, por ejemplo:
.B --dhcp-option=encap:175, 190, "iscsi-client0"
enviará opción 175, dentro de la cual está opción 190. Si múltiples
opciones son brindadas que están encapsuladas con el mismo número de
opción entonces serán correctamente combinadas en una opción encapsulada.
encap: y vendor: no pueden ser fijadas ambas dentro de la misma opción dhcp-option.
La variante final en opciones encapsuladas es "Vendor-Identifying Vendor Options"
como especificado en RFC3925. Estos son denotados así:
.B --dhcp-option=rfc3925-encap:2, 10, "text"
El número en la sección rfc3925-encap: es el número enterprise usado
para identificar esta opción.
La dirección 0.0.0.0 no es tratada de forma especial en opciones encapsuladas.
.TP
.B --dhcp-option-force=[<network-id>,[<network-id>,]][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]]
.B --dhcp-option-force=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],]<opt>,[<value>[,<value>]]
Esto funciona exáctamente de la misma forma que
.B --dhcp-option
excepto que la opción siempre será enviada, aún si el cliente no la pide en
@@ -653,20 +740,21 @@ hacia opciones DHCP. Esto crea espacio extra en el paquete DHCP para opciones,
pero puede raramente confundir clientes viejos o defectuosos. Esta opción forza
comportamiento "simple y sencillo" para prevenir problemas en tales casos.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Trazar desde un string vendor-class a un network id. La mayoría de los
.B \-U, --dhcp-vendorclass=set:<tag>,<vendor-class>
Trazar desde un string vendor-class a una etiqueta. La mayoría de los
clientes DHCP proveen una "vendor class" la cual representa, en cierto
sentido, el tipo de host. Esta opción traza clases de vendedor a network
ids, de tal forma que opciones DHCP pueden ser selectivamente entregadas
a diferentes clases de hosts. Por ejemplo
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
.B dhcp-vendorclass=set:printers,Hewlett-Packard JetDirect
peritiría que opciones sean fijadas solo para impresoras HP así:
.B --dhcp-option=printers,3,192.168.4.4
.B --dhcp-option=tag:printers,3,192.168.4.4
El string vendor-class es coordinado con el vendor-class proveido por
el cliente, para permitir coincidencias borrosas.
el cliente, para permitir coincidencias borrosas. El prefijo set: es
opcional, pero permitido por razones de consistencia.
.TP
.B \-j, --dhcp-userclass=<network-id>,<user-class>
Trazar desde un string user-class a un network id (con coordinación
.B \-j, --dhcp-userclass=set:<tag>,<user-class>
Trazar desde un string user-class a una etiqueta (con coordinación
substring, como con vendor-class). La mayoría de los clientes DHCP
proveen un "user class" el cual es configurable. Esta opción traza
clases user a network ids, de tal manera que opciones DHCP puedan
@@ -674,65 +762,166 @@ ser selectivamente enviadas a diferentes tipos de hosts. Es posible,
por ejemplo, usar esto para especificar una impresora diferente para
hosts en la clase "cuentas" que para los de la clase "ingenieria".
.TP
.B \-4, --dhcp-mac=<network-id>,<dirección MAC>
Trazar desde una dirección MAC a una network id. La dirección MAC
.B \-4, --dhcp-mac=set:<tag>,<MAC address>
Trazar desde una dirección MAC a una etiqueta. La dirección MAC
puede incluir comodínes. Por ejemplo:
.B --dhcp-mac=3com,01:34:23:*:*:*
.B --dhcp-mac=set:3com,01:34:23:*:*:*
fijaría el tag "3com" a cualquier host el cual su MAC coincida con
el patrón.
.TP
.B --dhcp-circuitid=<network-id>,<circuit-id>, --dhcp-remoteid=<network-id>,<remote-id>
Trazar de opciones agente de relay RFC3046 a opciones network-id. Estos
Trazar de opciones agente de relay RFC3046 a etiquetas. Estos
datos pueden ser proveídos por agentes de relay DHCP. El circuit-id o
remote-id es normlamente brindado como hex separado por doblepuntos, pero
también se permite un string simple. Si se obtiene una coincidencia exacta
entre el circuit o agent ID y uno proveído por un agente de relay,
network-id es fijado.
la etiqueta es fijada.
.TP
.B --dhcp-subscrid=<network-id>,<subscriber-id>
Trazar de opciones relay subscriber-id RFC3993 a opciones network-id.
.B --dhcp-subscrid=set:<tag>,<subscriber-id>
Trazar de opciones relay subscriber-id RFC3993 a etiquetas.
.TP
.B --dhcp-match=<network-id>,<número de opción>
Fijar la opción network-id si el cliente envía un opción DHCP del nombre
brindado. Esto puede ser utilizado para identificar clientes particulares
que envían información usando números privados de opciones.
.B --dhcp-proxy[=<ip addr>]......
Un agente de relay normal es usado solamente para reenviar las partes
iniciales de una interacción DHCP con el servidor DHCP. Una vez que
un cliente es configurado, se comunica diectamente con el servidor. Esto
es indeseable si el agente de relay está agregando información extra a
los paquetes DHCP, tal como usado por
.B dhcp-circuitid
y
.B dhcp-remoteid.
Una implementación relay completa puede usar la opción serverid-override
RFC 5107 para obligar al servidor DHCP a usar el relay como un proxy
completo, con todos los paquetes pasando a travez de el. Esta opción
provee una manera alternativa de hacer la misma cosa, para relays que
no tienen soporte RFC 5107. Brindada por si sola, manipula el server-id
para todas las interacciones via relays. Si una lista de IPs es brindada,
solo interacciones via relays en esas direcciones son afectadas.
.TP
.B \-J, --dhcp-ignore=<network-id>[,<network-id>]
Cuando todos los network ids brindados coincidan con el juego de
network ids derivados de las clases net, host, y vendor, ignorar
el host y no brindarle un arriendo DHCP.
.B --dhcp-match=set:<tag>,<option number>|option:<option name>|vi-encap:<enterprise>[,<value>]
Sin un valor, fijar la etiqueta si el cliente envía una opción
DHCP del número o valor brindado. Cuando un valor es brindado, fijar la
etiqueta solo si la opción es enviada y coincide con el valor. El valor puede
ser de la forma "01:ff:*:02", caso en el cual el valor debe coincidir (aparte
de los comodines) pero la opción enviada puede tener data que no coincide despues
del final del valor. El valor también puede ser de la misma forma que
.B dhcp-option
caso en el cual la opción enviada es tratada como un array, y un elemento debe
coincidir, así que
--dhcp-match=set:efi-ia32,option:client-arch,6
fijará la etiqueta a "efi-ia32" si el número 6 aparece en la lista de
architecturas enviada por los clientes en opción 93. (Ver RFC 4578 para
detalles.) Si el valor es un string, coincidencia substring es usada.
La forma especial con vi-encap:<enterpise number> busca coincidencia con
clases de vendedor identificadoras para el enterprise especificado. Por
favor ver RFC 3925 para mas detalles sobre estas bestias raras e interesantes.
.TP
.B --dhcp-ignore-names[=<network-id>[,<network-id>]]
Cuando todos los network-ids brindados coinciden con el juego de
network-ids derivado de la red, host, classes de vendedor y usuario,
ignorar cualquier nombre de host proveido por el host. Nótese que,
a diferencia de dhcp-ignore, es permisible no brindar ningún tag netid,
.B --tag-if=set:<tag>[,set:<tag>[,tag:<tag>[,tag:<tag>]]]
Llevar a cabo operaciones boolean en etiquetas. Cualquier etiqueta
que aparece como set:<tag> es fijada si todas las etiquetas que aparecen
como tag:<tag> estan fijadas, (o desfijadas cuando tag:!<tag> es
usado). Si ningún tag:<tag> aparece, etiquetas set:<tag> son fijadas
incondicionalmente. Cualquier cantidad de formas set: y tag:
pueden aparecer, en cualquier orden. Líneas tag-if son ejecutadas
en orden, así que si la etiqueta en tag:<tag> es una etiqueta fijada
por otra
.B tag-if,
la línea que fija la etiqueta debe preceder a la que comprueba.
.TP
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
Cuando todoas las etiquetas brindadas aparecen en el juego de etiquetas
ignorar el host y no brindarle un arriendo DHCP.
.TP
.B --dhcp-ignore-names[=tag:<tag>[,tag:<tag>]]
Cuando todos las etiquetas brindadas aparecen en el juego de etiquetas, ignorar cualquier nombre de host proveido por el host. Nótese que,
a diferencia de dhcp-ignore, es permisible no brindar ninguna etiqueta,
y en tal caso nombres de host proveidos por clientes DHCP siempre son
ignorados, y hosts DHCP son agregados al DNS usando solo la configuración
dhcp-host en dnsmasq y el contenido de /etc/hosts y /etc/ethers.
.TP
.B --dhcp-broadcast=<network-id>[,<network-id>]
Cuando todos los network-ids brindados coinciden con el juego de network-ids
derivados de la red, host, clases de vendedor y usuarios, siempre usar
broadcast para comunicarse con el host cuando está sin configurar. La
mayoría de clientes DHCP que necesitan respuestas broadcast fijan una
opción en sus pedidos para que esto pase automaticamente, algunos
clientes BOOTP viejos no lo hacen.
.B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
Generar un nombre para clientes DHCP que de otra forma no tienen uno,
usando la dirección MAC expresada en hex, separada por guiones. Nótese
que si un host provee un nombre, será usado preferiblemente sobre este,
a menos que
.B --dhcp-ignore-names
esté fijado.
.TP
.B \-M, --dhcp-boot=[net:<network-id>,]<filename>,[<servername>[,<server address>]]
.B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]]
Cuando todas las etiquetas aparecen en el juego de etiquetas, siempre
usar broadcast para comunicar con el host cuando no está configurado.
Es permisible omitir las etiquetas, caso en el cual esto es
incondicional. La mayoría de clientes DHCP que necesitan
respuestas broadcast fijan una opción en sus pedidos para que esto pase automaticamente, algunos clientes BOOTP viejos no lo hacen.
.TP
.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>]]
Fijar opciones BOOTP que han de ser devueltas por el servidor DHCP. Nombre
y dirección de servidor son opcionales: si no son brindadas, el nombre es
dejado en blanco, y la dirección es fijada a la de la máquina que corre
dnsmasq. Si dnsmasq está brindando servicio TFTP (ver
.B --enable-tftp
) entonces solo el nombre de archivo es requirido aquí para habilitar
el inicio atravéz de una red. Si las opcionales network-ids son brindadas,
el inicio atravéz de una red. Si las opcionales etiquetas son brindadas,
ellas deberán coincidir para que esta configuración sea enviada. Nótese
que network-ids están prefijadas con "net:" para distinguirlas.
.TP
.TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>]
La mayoría de usos para boot-ROMS PXE simplemente permiten al sistema PXE
obtener una dirección IP y entonces bajar el archivo especificado por
.B dhcp-boot
y ejecutarlo. Sin embargo, el sistema PXE es capaz de llevar
a cabo funciones más complejas cuando están soportadas por un
servidor DHCP adecuado.
Esto especifica una opción boot que puede aparecer en un menú de boot
PXE. <CSA> es tipo de sistema de cliente, solo servicios del tipo correcto
aparecerán en un menú. Los tipos conocidos son x86PC, PC98, IA64_EFI,
Alpha, Arc_x86, Intel_Lean_Client, IA32_EFI, BC_EFI, Xscale_EFI y X86-64_EFI;
un número entero puede ser utilizado para otros tipos. El parámetro después
del texto de menú puede ser un nombre de archivo, caso en el cuál dnsmasq
actúa como un servidor boot y le ordena al cliente PXE bajar el archivo
vía TFTP, ya sea de sí mismo (
.B enable-tftp
debe estar fijado para que esto funcione) o desde otro servidor TFTP si la
dirección IP final es brindada.
Nótese que el sufijo "layer" (normalmente ".0") es brindado por PXE, y
no debe ser agregado al nombre base. Si un número entero es brindado en vez
de un nombre base, entonces el cliente PXE buscará un servicio boot adecuado
para ese tipo de red. Esta búsqueda puede ser hecha mediante broadcast,
o directamente a un servidor si la dirección IP es brindada. Si ningún tipo
de servicio boot o nombre de archivo es brindado (o un tipo de servicio boot
de 0 es especificado), entonces la opción de menú abortará el proceso net boot
y continuará desde el medio local.
.TP
.B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>]
Fijar esto hace que un aviso sea expuesto despues del boot PXE. Si el timeout
es brindado, entonces despues que el timeout se haya vencido sin input del
teclado, la primera opción del menú sera automaticamente ejecutada. Si el
timeout es cero entonces la primera opción del menú sera automaticamente
ejecutada. Si
.B pxe-prompt
es omitido, el sistema esperará para el input del usuario si hay múltiples
artículos en el menú, pero hará boot imediatamente si hay solo uno. Ver
.B pxe-service
para detalles sobre artículos de menu.
Dnsmasq tiene soporte para "proxy-DHCP" PXE, en este caso otro servidor
DHCP en la red es responsable por asignar direcciones IP, y dnsmasq
simplemente provee la dirección brindada en
.B pxe-prompt
y
.B pxe-service
para permitir boot a travez de la red. Este modo es habilitado usando
la palabra clave
.B proxy
en
.B dhcp-range.
.TP
.B \-X, --dhcp-lease-max=<número>
Limita a dnsmasq a el número especificado de arriendos DHCP. El
predeterminado es 150. El limite es para prevenir ataques DoS desde
predeterminado es 1000. El limite es para prevenir ataques DoS desde
hosts que crean cientos de arriendos y usan mucha de la memoria del
proceso dnsmasq.
.TP
@@ -772,23 +961,16 @@ cuidado.
.TP
.B --log-dhcp
Bitacoréo extra para DHCP: Bitacorear todas las opciones enviadas a
clientes DHCP y las etiquetas netid usadas para determinarlos.
clientes DHCP y las etiquetas usadas para determinarlos.
.TP
.B \-l, --dhcp-leasefile=<path>
Usar el archivo especificado para almacenar información de arriendos
DHCP. Si esta opción es brindada, pero ninguna opcion dhcp-range es
brindada, entonces se activa comportamiento tipo dnsmasq versión 1.
El archivo brindado se asume es un archivo de arriendos dhcpd ISC y
es analizado en busca de arriendos los cuales son agregados al sistema
DNS si tienen un nombre de host. Esta funcionalidad pudo haber sido
excluida de dnsmasq a la hora de compilación, y en tal caso ocurrirá
un error. Nótese que la integración de archivos de
arriendo ISC es una caracterísctica depreciada. No debería ser usada
en instalaciones nuevas, y será eliminada en una versión futura.
DHCP.
.TP
.B \-6 --dhcp-script=<path>
Cuando un arriendo DHCP nuevo es creado, o uno viejo es
destruido, el ejecutable especificado por esta opción es ejecutado.
<path> debe ser un pathname absoluto, ninguna búsqueda PATH ocurre.
Los argumentos para el binario son "add", "old", o "del", la dirección
MAC del host, la dirección IP, y el hostname, si es
conocido. "add" significa que un arriendo ha sido creado, "del" que
@@ -800,36 +982,64 @@ que no es ethernet, tendr
"06-01:23:45:67:89:ab" para token ring. El proceso es ejecutado como root
(asumiendo que dnsmasq fue originalmente ejecutado como root) aún si dnsmasq
está configurado para cambiar su UID a un usuario sin privilegios.
El ambiente es heredado del usuario que ha invocado a dnsmasq, y si el
host brindó un client-id, es almacenado en la variable de ambiente
DNSMASQ_CLIENT_ID. Si el dominio completamente calificado del host
es conocido, la parte de dominio es almacenada en DNSMASQ_DOMAIN. Si
el cliente brinda información de clase de vendedoro usuario,
estos son brindados en las variables DNSMASQ_VENDOR_CLASS y
El ambiente es heredado del usuario que ha invocado a dnsmasq, con algunas
o todas de las siguientes variables agregadas.
DNSMASQ_CLIENT_ID si el host brindo un client-id.
DNSMASQ_DOMAIN si el nombre de dominio completamente calificado del host
es conocido, esto es fijado a la parte del dominio.
Si el cliente brinda vendor-class, hostname o user-class, estos son
brindados en las variables
DNSMASQ_VENDOR_CLASS, DNSMASQ_SUPPLIED_HOSTNAME, y
DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn, pero solo para acciones "add"
y "old" cuando un host resume un arriendo existente, dado a que estos
y "old" cuando un host reanuda un arriendo existente, dado a que estos
datos no son almacenados en la base de datos de arriendos de dnsmasq.
Si dnsmasq fue compilado con HAVE_BROKEN_RTC, entonces la duración del
arriendo (en segundos) es almacenada en DNSMASQ_LEASE_LENGTH, de otra
manera el tiempo de vencimiento es almacenado en DNSMASQ_LEASE_EXPIRES.
El número de segundos faltante para el vencimiento del arriendo siempre
es almacenado en DNSMASQ_TIME_REMAINING.
Si un arriendo solía tener un nombre de host, el cual es removido, un
evento "old" es generado con el nuevo estado del arriendo, (por ejemplo, sin
nombre), y el nombre anterior es brindado en la variable de ambiente
DNSMASQ_OLD_HOSTNAME. DNSMASQ_INTERFACE almacena el nombre de la interface
DNSMASQ_OLD_HOSTNAME.
DNSMASQ_INTERFACE almacena el nombre de la interface
en la cual llegó el pedido; esto no es fijado para acciones "viejas"
cuando dnsmasq re-inicia.
DNSMASQ_RELAY_ADDRESS es fijado si el cliente
usó un relay DHCP para contactar a dnsmasq y la dirección IP del relay
es conocida.
DNSMASQ_TAGS contiene todas las etiquetas network-id fijadas
durante la transacción DHCP, separadas por espacios.
Todos los descriptores de archivo están cerrados
excepto stdin, stdout, y stderr los cuales están abiertos a /dev/null
(excepto en modo debug).
Este guión no es invocado concurrentemente: si cambios de arriendos
subsiguientes ocurren, el guión no es invocado otra vez hasta que
cualquier invocación existente haga exit. Al inicio de dnsmasq, el guión
Este guión no es invocado concurrentemente: máximo una instamcia del
guión está corriendo a la vez (dnsmasq espera a que una instancia de
guión haga exit antes de correr la siguiente). Cambios a la base de
datos de arriendos que requieren que el guión sea invocado son puestos
en cola esperando el exit de una instancia corriente. Si esta cola permite
que cambios multiples de estado le ocurran a un arriendo individual antes
de que el guión pueda ser ejecutado entonces estados anteriores son descartados
y el estado actual del arriendo es reflejado cuando el guión finalmente corre.
Al inicio de dnsmasq, el guión
será invocado para todos los arriendos existentes mientras van siendo
leídos desde el archivo de arriendos. Arriendos vencidos serán llamados
con "del" y otros con "old". <path> debe ser un path absoluto, ninguna
búsqueda PATH ocurre. Cuando dnsmasq recibe una señal HUP, el guión será
búsqueda PATH ocurre cuando arriendos dnsmasq serán llamados con "del"
y otros con "old". Cuando dnsmasq recibe una señal HUP, el guión será
invocado para arriendos existentes con un evento "old".
.TP
.B --dhcp-scriptuser
@@ -853,10 +1063,9 @@ cuando hay cambios hechos a el client-id y tiempos de arriendo y vencimiento.
.TP
.B --bridge-interface=<nombre de interface>,<alias>[,<alias>]
Tratar paquetes de pedidos DHCP que llegan a cualquiera de las interfaces <alias>
como si hubieran llegado a la interface <nombre de interface>. Esta opción solo
está disponible en plataformas BSD, y es necesaria cuando se usan
puentes "estilo viejo", ya que los paquetes llegan a interfaces tap que no
tienen una dirección IP.
como si hubieran llegado a la interface <nombre de interface>. Esta opción
es necesaria al usar bridging estilo viejo en plataformas BSD, dado a que
los paquetes llegan a interfaces tap que no tienen una dirección IP.
.TP
.B \-s, --domain=<dominio>[,<rango de IPs>]
Especifica los dominios DNS para el servidor DHCP. Dominios pueden ser
@@ -903,18 +1112,20 @@ sin una direcci
.B --dhcp-fqdn
está fijado.
.TP
.B --enable-tftp
.B --enable-tftp[=<interface>]
Habilitar la función de servidor TFTP. Esto está deliberadamente limitado
a lo necesario para hacerle a un cliente un inicio vía red. Solo lectura es
permitida; las extensiones tsize y blksize son soportadas (tsize solo es
soportada en modo octeto).
soportada en modo octeto). Ver sección de NOTAS para el uso de el argumento
de interface.
.TP
.B --tftp-root=<directorio>
.B --tftp-root=<directory>[,<interface>]
Buscar, relativo al directorio brindado, archivos para transferir mediante el
uso de TFTP. Cuando esta opción está fijada, paths TFTP que incluyen ".." son
rechazados, para prevenir que clientes salgan de la raíz especificada. Paths
absolutos (los que comienzan con "/") están permitidos, pero deben estar
dentro del tftp-root.
dentro del tftp-root. Si el argumento opcional de interface es brindado, el
directorio es solo usado para pedidos TFTP vía esa interface.
.TP
.B --tftp-unique-root
Agregar la dirección IP del cliente TFTP como un componente path del lado del
@@ -966,11 +1177,13 @@ Especificar un archivo de configuraci
también es permitida en archivos de configuración, para incluir múltiples
archivos de configuración.
.TP
.B \-7, --conf-dir=<directorio>
.B \-7, --conf-dir=<directorio>[,<file-extension>......]
Leer todos los archivos dentro del directorio brindado como archivos
de configuración. Archivos cuyos nombres terminen con ~ o comienzen
con . o comienzen y terminen con # son ignorados. Esta opción puede
ser brindada en la línea de comandos o en un archivo de configuración.
de configuración. Si extensiones son brindadas, cualquier archivo que
termine en esas extensiones son ignorados. Cualquier archivos cuyos nombres
terminen con ~ o comienzen con . o comienzen y terminen con # siempre son
ignorados. Esta opción puede ser brindada en la línea de comandos o en un
archivo de configuración.
.SH ARCHIVO DE CONFIGURACION
Al inicio, dnsmasq lee
.I /etc/dnsmasq.conf,
@@ -1100,36 +1313,46 @@ apunta a un nombre sombreado, entonces buscando el CNAME a trav
dnsmasq resultará en que la dirección no-sombreada será asociada con
el destino del CNAME. Para circumventar esto, agregar el CNAME a
/etc/hosts de tal manera que el CNAME es sombreado también.
.PP
El sistema network-id funciona de la siguiente manera: Para cada pedido
DHCP, dnsmasq colecciona un juego de etiquetas network-id válidas,
una del
El sistema de etiquetas funciona de la siguiente manera: Para cada pedido
DHCP, dnsmasq colecciona un juego de etiquetas válidas de líneas de
configuración activas que incluyen set:<tag>, incluyendo una del
.B dhcp-range
usado para alocar la dirección, una de cualquier
.B dhcp-host
que coincida (y "known" si un dhcp-host coincide), la etiqueta "bootp"
para pedidos BOOTP, una etiqueta cuyo nombre es el nombre de la
interface donde llegó el pedido, y posiblemente muchas de clases
de vendedor y usuario que coincidan que hayan sido enviadas por
el cliente DHCP. Cualquier opción
que coincida (y "known" si un dhcp-host coincide).
La etiqueta "bootp" es fijada para pedidos BOOTP, y una etiqueta cuyo
nombre es el nombre de la interface donde llegó el pedido tambien es
fijada.
Cualquier linea de configuración que incluya uno o mas
construcciones tag:<tag> solo será válida si todas las etiquetas
coinciden en el juego derivado arriba. Típicamente esto es dhcp-option.
.B dhcp-option
que tenga etiquetas network-id será usada en preferencia de una opción
que tenga etiquetas será usada en preferencia de una opción
.B dhcp-option,
sin etiqueta, con tal que _todas_ las etiquetas coincidan en alguna
parte del juego coleccionado describido arriba. El prefijo "#" en una
etiqueta significa "no" así que --dhcp=option=#purple,3,1.2.3.4 envía
la opción cuando la etiqueta network-id "purple" no está en el juego
de etiquetas válidas.
parte del juego coleccionado describido arriba. El prefijo '!' en una
etiqueta significa "no" así que --dhcp=option=tag:!purple,3,1.2.3.4 envía
la opción cuando la etiqueta "purple" no está en el juego
de etiquetas válidas. (Si se está usando esto en una línea de comandos
en vez de un archivo de configuración, asegurese de escapar !, el cual
es un metacaracter de shell.)
.PP
Nótese que para
.B dhcp-range
ambos tag:<tag> y set:<tag> son permitidos, para seleccionar el rango
en uso basado en (por ejemplo) dhcp-host, y para afectar las opciones
enviadas, basadas en el rango seleccionado.
Este sistema evolucionó de uno anterior mas limitado y para compatibildad
reversa "net:" puede ser usada en vez de "tag:" y "set:" puede ser
omitida. (Excepto en
.B dhcp-host,
donde "net:" puede ser usado en vez de "set:".) Por la misma razón, '#'
puede ser usado en vez de '!' para indicar NO.
.PP
Si el network-id en un
.B dhcp-range
es prefijado con "net:", entonces su significado cambia de "fijar
etiqueta" a "coincidir con etiqueta". O sea que si hay más de un
dhcp-range en en una subred, y uno tiene una etiqueta network-id la
cual está fijada (por ejemplo una opción de clase de vendedor) entonces
hosts que fijen la etiqueta network-id serán alocados direcciones en
el rango etiquetado.
.PP
El servidor DHCP de dnsmasq funcionará como servidor BOOTP tambien,
con tal que las direcciones MAC y IP de los clientes sean brindadas,
ya sea usando configuraciones
@@ -1140,11 +1363,54 @@ o en
.B dhcp-range
esté presente para activar el servidor DHCP en una red particular.
(Fijar --bootp-dynamic elimina la necesidad de trazados estáticos.) El
parámetro de nombre de archivos en un pedido BOOTP es revisado para
ver si coincide con algún network-id en configuraciónes
.B dhcp-option
al igual que la etiqueta "bootp", permitiendo así algún control sobre
las opciones devueltas a diferentes clases de hosts.
parámetro de nombre de archivos en un pedido BOOTP es usado como
una etiqueta, al igual que la etiqueta "bootp", permitiendo así algún
control sobre las opciones devueltas a diferentes clases de hosts.
.B dhcp-range
puede tener un nombre de interface brindado como
"interface:<interface-name>". La semántica de esto es así:
Para DHCP, si cualquier otro dhcp-range existe _sin_ un nombre de
interface, entonces el nombre de interface es ignorado y dnsmasq
se comporta como si las partes de interface no existieran, de otra forma
DHCP solo se provee a interfaces mencionadas en declaraciones
dhcp-range. Para DNS, si no hay opciones
.B --interface
o
.B --listen-address
el comportamiento no se modifica por la parte de interface. Si cualquiera
de estas opciones está presente, las interfaces mencionadas en dhcp-ranges
son agregadas all juego que obtienen servicio DNS.
Similarmente,
.B enable-tftp
puede tomar un nombre de interface, el cual habilita TFTP solo para una
interface en particular, ignorando opciones
.B --interface
o
.B --listen-address.
Adicionalmente,
.B --tftp-secure
y
.B --tftp-unique-root
y
.B --tftp-no-blocksize
son ignorados por pedidos desde dichas interfaces. (Una directiva
.B --tftp-root
brindando un path raíz y una interface debe ser brindada tambien.)
Estas reglas pueden parecer raras a primera vista, pero permiten que
una simple linea de la forma
"dhcp-range=interface:virt0,192.168.0.4,192.168.0.200" sea agregada a
configuración dnsmasq, lo cual brinda servicios DHCP y DNS a esa interface,
sin afectar los servicios en otras interfaces y irrespectivamente de
la existencia o no de lineas "interface=<interface>" en alguna otra parte
de la configuración dnsmasq.
"enable-tftp=virt0" y "tftp-root=<root>,virt0" hacen el mismo trabajo
para TFTP.
La idea es que una linea así pueda ser agregada automaticamente
por libvirt o sistemas equivalentes, sin estorbar alguna
configuración manual.
.SH CÓDIGOS EXIT
.PP
@@ -1177,10 +1443,8 @@ no escalaban tan bien.
.PP
Dnsmasq es capaz de soportar con DNS y DHCP a por lo menos mil (1,000)
clientes. Por supuesto que para lograr esto debe aumentarse el valor de
.B --dhcp-lease-max
, y tiempos de arriendo no deben ser muy cortos (menos de una hora).
El valor de
clientes. Los tiempos de arriendo no deben ser muy cortos (menos
de una hora). El valor de
.B --dns-forward-max
puede ser aumentado: comienze con el equivalente a el número de clientes y
auméntelo si parece lento el DNS. Nótese que el rendimiento DNS depende
@@ -1211,6 +1475,23 @@ o en un archivo hosts adicional. La lista puede ser muy larga. Dnsmasq ha sido
probado exitósamente con un millón de nombres. Ese tamaño de archivo necesita
un CPU de 1GHz y aproximadamente 60MB de RAM.
.SH INTERNACIONALIZACION
Dnsmasq puede ser compilado con soporte para internacionalización. Para hacer esto,
los targets make "all-i18n" y "install-i18n" deberán ser usados en vez de
los targets estándares "all" y "install". Cuando internacionalización es
compilada, dnsmasq producirá mensajes de bitácora en el lenguaje local y soportará
dominios internacionalizados (IDN). Nombres de dominio en /etc/hosts, /etc/ethers,
y /etc/dnsmasq.conf que contienen carácteres no-ASCII serán traducidos a
representación interna DNS punycode. Nótese que dnsmasq determina ambos el
lenguaje para mensajes y el juego de carácteres asumido para archivos de configuración
de la variable ambiental LANG. Esto debe estar fijado al valor predeterminado del sistema
por el guión responsable de iniciar dnsmasq. Al editar archivos de configuración,
tener cuidado de hacerlo usando solo el locale predeterminado del sistema y no
uno especifico del usuario, dado a que dnsmasq no tiene ninguna manera directa de
determinar el juego de caracteres en uso, y debe asumir que es el predeterminado
del sistema.
.SH ARCHIVOS
.IR /etc/dnsmasq.conf

File diff suppressed because it is too large Load Diff

1754
po/de.po

File diff suppressed because it is too large Load Diff

1062
po/es.po

File diff suppressed because it is too large Load Diff

1150
po/fi.po

File diff suppressed because it is too large Load Diff

1270
po/fr.po

File diff suppressed because it is too large Load Diff

1034
po/id.po

File diff suppressed because it is too large Load Diff

1150
po/it.po

File diff suppressed because it is too large Load Diff

1035
po/no.po

File diff suppressed because it is too large Load Diff

1656
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1036
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

2
src/NOTES Normal file
View File

@@ -0,0 +1,2 @@
Worry about IPv6 leases and DUID in script-storage.

157
src/bpf.c
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,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,63 @@ 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 + offsetof(struct ifreq, ifr_ifru);
#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;
}
/* We have no way to determine the prefix, so we assume it's 64 for now....... */
if (!((*callback)(addr, 64,
(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 +205,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 +250,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;
}

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,19 +10,21 @@
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;
static int uid = 0;
static char *addrbuff = NULL;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -72,9 +74,6 @@ void cache_init(void)
struct crec *crecp;
int i;
if (daemon->options & OPT_LOG)
addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize/10;
if (daemon->cachesize > 0)
@@ -223,7 +222,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;
@@ -363,9 +362,6 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* CONFIG bit no needed except for logging */
flags &= ~F_CONFIG;
/* if previous insertion failed give up now. */
if (insert_error)
return NULL;
@@ -500,7 +496,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)
{
@@ -690,10 +686,10 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
if (!nameexists)
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache->name.sname, a->target) &&
(lookup = whine_malloc(sizeof(struct crec) + strlen(a->alias)+1-SMALLDNAME)))
(lookup = whine_malloc(sizeof(struct crec))))
{
lookup->flags = F_FORWARD | F_IMMORTAL | F_HOSTS | F_CNAME;
strcpy(lookup->name.sname, a->alias);
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);
@@ -818,35 +814,38 @@ static int read_hostsfile(char *filename, int index, int cache_size)
while (atnl == 0)
{
struct crec *cache;
int fqdn;
int fqdn, nomem;
char *canon;
if ((atnl = gettok(f, token)) == EOF)
break;
fqdn = !!strchr(token, '.');
if (canonicalise(token))
if ((canon = canonicalise(token, &nomem)))
{
/* If set, add a version of the name with a default domain appended */
if ((daemon->options & OPT_EXPAND) && domain_suffix && !fqdn &&
if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
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(token)+1-SMALLDNAME)))
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
name_count++;
}
free(canon);
}
else
else if (!nomem)
my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
}
}
@@ -859,10 +858,11 @@ static int read_hostsfile(char *filename, int index, int cache_size)
return name_count;
}
void cache_reload(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;
@@ -889,22 +889,50 @@ void cache_reload(struct hostsfile *addn_hosts)
up = &cache->hash_next;
}
if ((daemon->options & 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 (!(daemon->options & OPT_NO_HOSTS))
if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size);
while (addn_hosts)
{
total_size = read_hostsfile(addn_hosts->fname, addn_hosts->index, total_size);
addn_hosts = addn_hosts->next;
}
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;
@@ -926,7 +954,7 @@ void cache_add_dhcp_entry(char *host_name,
struct in_addr *host_address, time_t ttd)
{
struct crec *crec = NULL, *aliasc;
unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
unsigned short flags = F_NAMEP | F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
int in_hosts = 0;
struct cname *a;
@@ -935,19 +963,22 @@ void cache_add_dhcp_entry(char *host_name,
/* 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))
{
@@ -996,7 +1027,7 @@ void cache_add_dhcp_entry(char *host_name,
if (aliasc)
{
aliasc->flags = F_FORWARD | F_DHCP | F_CNAME;
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
@@ -1009,6 +1040,7 @@ void cache_add_dhcp_entry(char *host_name,
}
}
}
#endif
void dump_cache(time_t now)
@@ -1021,34 +1053,34 @@ void dump_cache(time_t now)
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->queries_forwarded, daemon->local_answer);
if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN)))
return;
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
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;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries);
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->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)
@@ -1066,11 +1098,11 @@ void dump_cache(time_t now)
#ifdef HAVE_IPV6
else
{
a = addrbuff;
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
}
#else
else
@@ -1094,25 +1126,23 @@ 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);
}
}
}
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 querystr(char *str, unsigned short type)
@@ -1125,28 +1155,28 @@ void querystr(char *str, unsigned short type)
sprintf(str,"query[%s]", typestr[i].name);
}
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg)
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{
char *source, *dest = addrbuff;
char *source, *dest = daemon->addrbuff;
char *verb = "is";
if (!(daemon->options & OPT_LOG))
if (!option_bool(OPT_LOG))
return;
if (addr)
{
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
addr, daemon->addrbuff, ADDRSTRLEN);
#else
strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
}
if (flags & F_REVERSE)
{
dest = name;
name = addrbuff;
name = daemon->addrbuff;
}
if (flags & F_NEG)
@@ -1171,20 +1201,16 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, char *ar
}
}
else if (flags & F_CNAME)
{
/* nasty abuse of NXDOMAIN and CNAME flags */
if (flags & F_NXDOMAIN)
dest = arg;
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 = arg;
else if (flags & F_CONFIG)
source = "config";
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_SERVER)
@@ -1203,6 +1229,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, char *ar
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-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,21 +10,21 @@
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.46"
#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 */
@@ -32,74 +32,19 @@
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
#ifdef __uClinux__
# define RESOLVFILE "/etc/config/resolv.conf"
#else
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__)
# define LEASEFILE "/var/db/dnsmasq.leases"
#elif defined(__sun__) || defined (__sun)
# define LEASEFILE "/var/cache/dnsmasq.leases"
#else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
#endif
#if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
#else
# define CONFFILE "/etc/dnsmasq.conf"
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* DBUS interface specifics */
#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
/* Follows system specific switches. If you run on a
new system, you may want to edit these.
May replace this with Autoconf one day.
HAVE_LINUX_NETWORK
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.
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
@@ -118,55 +63,119 @@ HAVE_BROKEN_RTC
HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
HAVE_DHCP
define this to get dnsmasq's DHCPv4 server.
HAVE_ARC4RANDOM
define this if you have arc4random() to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_DHCP6
define this to get dnsmasq's DHCPv6 server. (implies HAVE_DHCP).
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change.
HAVE_LUASCRIPT
define this to get the ability to call Lua script on lease-change. (implies HAVE_SCRIPT)
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_BSD_BRIDGE
Define this to enable the --bridge-interface option, useful on some
BSD systems.
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
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
NO_IPV6
NO_TFTP
NO_DHCP
NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
with something like "make COPTS=-DNO_SCRIPT" will do the trick.
For *BSD systems you should define
HAVE_BSD_NETWORK
HAVE_SOCKADDR_SA_LEN
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
LEASEFILE
CONFFILE
RESOLVFILE
the default locations of these files are determined below, but may be overridden
in a build command line using COPTS.
*/
/* platform independent options- uncomment to enable */
/* The default set of options to build. Built with these options, dnsmasq
has no library dependencies other than libc */
#define HAVE_DHCP
/* #define HAVE_DHCP6 */
#define HAVE_TFTP
#define HAVE_SCRIPT
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */
/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
/* Default locations for important system files. */
#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
/* platform dependent options. */
#ifndef CONFFILE
# if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
# else
# define CONFFILE "/etc/dnsmasq.conf"
# endif
#endif
#ifndef RESOLVFILE
# if defined(__uClinux__)
# define RESOLVFILE "/etc/config/resolv.conf"
# else
# define RESOLVFILE "/etc/resolv.conf"
# endif
#endif
/* platform dependent options: these are determined automatically below
HAVE_LINUX_NETWORK
HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
HAVE_GETOPT_LONG
defined when GNU-sty;e getopt_long available.
HAVE_ARC4RANDOM
defined if arc4random() available to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_SOCKADDR_SA_LEN
defined if struct sockaddr has sa_len field (*BSD)
*/
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__)
@@ -203,32 +212,24 @@ NOTES:
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#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__) || \
defined (__FreeBSD_kernel__)
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
#endif
#if !defined (__FreeBSD_kernel__)
#if !defined(__FreeBSD_kernel__)
# define HAVE_ARC4RANDOM
#endif
#define HAVE_SOCKADDR_SA_LEN
#define HAVE_BSD_BRIDGE
#elif defined(__APPLE__)
#define HAVE_BSD_NETWORK
#undef HAVE_GETOPT_LONG
#define HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_SOCKADDR_SA_LEN
/* Define before sys/socket.h is included so we get socklen_t */
@@ -239,49 +240,23 @@ typedef unsigned long in_addr_t;
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_SOCKADDR_SA_LEN
#define HAVE_BSD_BRIDGE
#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
#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 */
/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
# define HAVE_IPV6
# define ADDRSTRLEN INET6_ADDRSTRLEN
# if defined(SOL_IPV6)
# define IPV6_LEVEL SOL_IPV6
# else
# define IPV6_LEVEL IPPROTO_IPV6
# endif
#elif defined(INET_ADDRSTRLEN)
# undef HAVE_IPV6
# define ADDRSTRLEN INET_ADDRSTRLEN
@@ -290,3 +265,103 @@ typedef unsigned long in_addr_t;
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif
/* rules to implement compile-time option dependencies and
the NO_XXX flags */
#ifdef NO_IPV6
#undef HAVE_IPV6
#endif
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
#ifdef NO_DHCP
#undef HAVE_DHCP
#undef HAVE_DHCP6
#endif
#if defined(NO_DHCP6) || !defined(HAVE_IPV6)
#undef HAVE_DHCP6
#endif
/* DHCP6 needs DHCP too */
#ifdef HAVE_DHCP6
#define HAVE_DHCP
#endif
#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
#undef HAVE_SCRIPT
#undef HAVE_LUASCRIPT
#endif
/* Must HAVE_SCRIPT to HAVE_LUASCRIPT */
#ifdef HAVE_LUASCRIPT
#define HAVE_SCRIPT
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
#ifdef DNSMASQ_COMPILE_OPTS
static char *compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_DBUS
"no-"
#endif
"DBus "
#ifndef LOCALEDIR
"no-"
#endif
"i18n "
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
"no-"
#endif
"IDN "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP)
# if !defined (HAVE_DHCP6)
"no-"
# endif
"DHCPv6 "
# if !defined(HAVE_SCRIPT)
"no-scripts "
# else
# if !defined(HAVE_LUASCRIPT)
"no-"
# endif
"Lua "
# endif
#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP "
#ifndef HAVE_CONNTRACK
"no-"
#endif
"conntrack";
#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-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,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;
@@ -229,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);
@@ -301,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);
@@ -328,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;
@@ -355,16 +390,26 @@ void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
}
}
void emit_dbus_signal(int action, char *mac, char *hostname, char *addr)
#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;
const char *action_str;
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)
@@ -374,6 +419,8 @@ void emit_dbus_signal(int action, char *mac, char *hostname, char *addr)
else
return;
addr = inet_ntoa(lease->addr);
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
return;
@@ -386,5 +433,6 @@ void emit_dbus_signal(int action, char *mac, char *hostname, char *addr)
dbus_message_unref(message);
}
#endif
#endif

File diff suppressed because it is too large Load Diff

220
src/dhcp6.c Normal file
View File

@@ -0,0 +1,220 @@
/* 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_DHCP6
struct iface_param {
struct dhcp_context *current;
int ind;
};
static int join_multicast(struct in6_addr *local, int prefix,
int scope, int if_index, void *vparam);
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, void *vparam);
void dhcp6_init(void)
{
int fd;
struct sockaddr_in6 saddr;
int class = IPTOS_CLASS_CS6;
if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
!fix_fd(fd) ||
!set_ipv6pktinfo(fd))
die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
memset(&saddr, 0, sizeof(saddr));
#ifdef HAVE_SOCKADDR_SA_LEN
saddr.sin6_len = sizeof(addr.in6);
#endif
saddr.sin6_family = AF_INET6;
saddr.sin6_addr = in6addr_any;
saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
/* join multicast groups on each interface we're interested in */
if (!iface_enumerate(AF_INET6, &fd, join_multicast))
die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
daemon->dhcp6fd = fd;
}
static int join_multicast(struct in6_addr *local, int prefix,
int scope, int if_index, void *vparam)
{
char ifrn_name[IFNAMSIZ];
struct ipv6_mreq mreq;
struct in6_addr maddr;
int fd = *((int *)vparam);
struct dhcp_context *context;
struct iname *tmp;
(void)prefix;
(void)scope; /* warnings */
if (!indextoname(fd, if_index, ifrn_name))
return 0;
/* Are we doing DHCP on this interface? */
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
return 1;
/* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
break;
if (!context)
return 1;
mreq.ipv6mr_interface = if_index;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &maddr);
if (!setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_SERVERS, &maddr);
if (!setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
return 1;
}
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
int if_index = 0;
union {
struct cmsghdr align; /* this ensures alignment */
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control_u;
union mysockaddr from;
struct all_addr dest;
ssize_t sz;
struct ifreq ifr;
struct iname *tmp;
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg) == -1) || sz <= 4)
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
dest.addr.addr6 = p.p->ipi6_addr;
}
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
ls -l
if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
break;
if (!context)
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
parm.current = NULL;
parm.ind = if_index;
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
lease_prune(NULL, now); /* lose any expired leases */
msg.msg_iov = &daemon->dhcp_packet;
sz = dhcp6_reply(parm.current, sz);
/* ifr.ifr_name, if_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd, iface_addr); */
lease_update_file(now);
lease_update_dns();
if (sz != 0)
send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, sz, &from, &dest, if_index);
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
for (context = daemon->dhcp6; context; context = context->next)
{
if ((context->flags & CONTEXT_IPV6) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
/* link it onto the current chain if we've not seen it before */
if (if_index == param->ind && context->current == context)
{
context->current = param->current;
param->current = context;
}
}
}
return 1;
}
#endif

55
src/dhcp6_protocol.h Normal file
View File

@@ -0,0 +1,55 @@
/* 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 DHCPV6_SERVER_PORT 547
#define DHCPV6_CLIENT_PORT 546
#define ALL_SERVERS "FF05::1:3"
#define ALL_RELAY_AGENTS_AND_SERVERS "FF02::1:2"
#define DHCP6SOLICIT 1
#define DHCP6ADVERTISE 2
#define DHCP6REQUEST 3
#define DHCP6CONFIRM 4
#define DHCP6RENEW 5
#define DHCP6REBIND 6
#define DHCP6REPLY 7
#define DHCP6RELEASE 8
#define DHCP6DECLINE 9
#define DHCP6RECONFIGURE 10
#define DHCP6IREQ 11
#define DHCP6RELAYFORW 12
#define DHCP6RELAYREPL 13
#define OPTION6_CLIENT_ID 1
#define OPTION6_SERVER_ID 2
#define OPTION6_IA_NA 3
#define OPTION6_IA_TA 4
#define OPTION6_IAADDR 5
#define OPTION6_ORO 6
#define OPTION6_PREFERENCE 7
#define OPTION6_ELAPSED_TIME 8
#define OPTION6_RELAY_MSG 9
#define OPTION6_AUTH 11
#define OPTION6_UNICAST 12
#define OPTION6_STATUS_CODE 13
#define OPTION6_RAPID_COMMIT 14
#define OPTION6_USER_CLASS 15
#define OPTION6_VENDOR_CLASS 16
#define OPTION6_VENDOR_OPTS 17
#define OPTION6_INTERFACE_ID 18
#define OPTION6_RECONFIGURE_MSG 19
#define OPTION6_RECONF_ACCEPT 20

96
src/dhcp_protocol.h Normal file
View File

@@ -0,0 +1,96 @@
/* 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 DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#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];
};

114
src/dns_protocol.h Normal file
View File

@@ -0,0 +1,114 @@
/* 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 NAMESERVER_PORT 53
#define TFTP_PORT 69
#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; \
}

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,45 +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/>.
*/
/* Declare static char *compiler_opts in config.h */
#define DNSMASQ_COMPILE_OPTS
#include "dnsmasq.h"
struct daemon *daemon;
static char *compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifdef HAVE_BSD_BRIDGE
"BSD-bridge "
#endif
#ifndef HAVE_DBUS
"no-"
#endif
"DBus "
#ifndef LOCALEDIR
"no-"
#endif
"I18N "
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP";
static volatile pid_t pid = 0;
static volatile int pipewrite;
@@ -56,8 +28,8 @@ 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);
static void fatal_event(struct event_desc *ev, char *msg);
static int read_event(int fd, struct event_desc *evp, char **msg);
int main (int argc, char **argv)
{
@@ -67,9 +39,11 @@ int main (int argc, char **argv)
struct iname *if_tmp;
int piperead, pipefd[2], err_pipe[2];
struct passwd *ent_pw = NULL;
#if defined(HAVE_SCRIPT)
uid_t script_uid = 0;
gid_t script_gid = 0;
struct group *gp= NULL;
#endif
struct group *gp = NULL;
long i, max_fd = sysconf(_SC_OPEN_MAX);
char *baduser = NULL;
int log_err;
@@ -108,11 +82,15 @@ int main (int argc, char **argv)
daemon->edns_pktsz : DNSMASQ_PACKETSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DHCP
if (!daemon->lease_file)
{
if (daemon->dhcp)
daemon->lease_file = LEASEFILE;
}
#endif
/* Close any file descriptors we inherited apart from std{in|out|err} */
for (i = 0; i < max_fd; i++)
@@ -124,51 +102,57 @@ int main (int argc, char **argv)
#elif !(defined(IP_RECVDSTADDR) && \
defined(IP_RECVIF) && \
defined(IP_SENDSRCADDR))
if (!(daemon->options & OPT_NOWILD))
if (!option_bool(OPT_NOWILD))
{
bind_fallback = 1;
daemon->options |= OPT_NOWILD;
set_option_bool(OPT_NOWILD);
}
#endif
#ifndef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif
#ifdef HAVE_CONNTRACK
if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
#else
if (option_bool(OPT_CONNTRACK))
die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
#endif
#ifdef HAVE_SOLARIS_NETWORK
if (daemon->max_logs != 0)
die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
#endif
#ifdef __ANDROID__
if (daemon->max_logs != 0)
die(_("asychronous logging is not available under Android"), NULL, EC_BADCONF);
#endif
rand_init();
now = dnsmasq_time();
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
#if !defined(HAVE_LINUX_NETWORK) && !defined(IP_RECVIF)
int c;
struct iname *tmp;
for (c = 0, tmp = daemon->if_names; tmp; tmp = tmp->next)
if (!tmp->isloop)
c++;
if (c != 1)
die(_("must set exactly one interface on broken systems without IP_RECVIF"), NULL, EC_BADCONF);
#endif
/* Note that order matters here, we must call lease_init before
creating any file descriptors which shouldn't be leaked
to the lease-script init process. */
lease_init(now);
dhcp_init();
}
#endif
if (!enumerate_interfaces())
die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
if (daemon->options & OPT_NOWILD)
if (option_bool(OPT_NOWILD))
{
daemon->listeners = create_bound_listeners();
create_bound_listeners(1);
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
@@ -181,14 +165,13 @@ int main (int argc, char **argv)
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
}
else if ((daemon->port != 0 || (daemon->options & OPT_TFTP)) &&
!(daemon->listeners = create_wildcard_listeners()))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
else
create_wildcard_listeners();
if (daemon->port != 0)
cache_init();
if (daemon->options & OPT_DBUS)
if (option_bool(OPT_DBUS))
#ifdef HAVE_DBUS
{
char *err;
@@ -204,8 +187,10 @@ int main (int argc, char **argv)
if (daemon->port != 0)
pre_allocate_sfds();
#if defined(HAVE_SCRIPT)
/* Note getpwnam returns static storage */
if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser)
if (daemon->dhcp && daemon->scriptuser &&
(daemon->lease_change_command || daemon->luascript))
{
if ((ent_pw = getpwnam(daemon->scriptuser)))
{
@@ -215,6 +200,7 @@ int main (int argc, char **argv)
else
baduser = daemon->scriptuser;
}
#endif
if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
baduser = daemon->username;
@@ -267,14 +253,12 @@ int main (int argc, char **argv)
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, NULL);
err_pipe[1] = -1;
if (!(daemon->options & OPT_DEBUG))
if (!option_bool(OPT_DEBUG))
{
int nullfd;
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
@@ -282,7 +266,7 @@ int main (int argc, char **argv)
die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC);
#ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK))
if (!option_bool(OPT_NO_FORK))
{
pid_t pid;
@@ -290,19 +274,21 @@ int main (int argc, char **argv)
When startup is complete we close this and the process terminates. */
safe_pipe(err_pipe, 0);
if ((pid = fork()) == -1 )
die(_("cannot fork into background: %s"), NULL, EC_MISC);
if ((pid = fork()) == -1)
/* fd == -1 since we've not forked, never returns. */
send_event(-1, EVENT_FORK_ERR, errno, NULL);
if (pid != 0)
{
struct event_desc ev;
char *msg;
/* close our copy of write-end */
close(err_pipe[1]);
/* check for errors after the fork */
if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1))
fatal_event(&ev);
if (read_event(err_pipe[0], &ev, &msg))
fatal_event(&ev, msg);
_exit(EC_GOOD);
}
@@ -312,9 +298,11 @@ int main (int argc, char **argv)
/* NO calls to die() from here on. */
setsid();
pid = fork();
if (pid != 0 && pid != -1)
if ((pid = fork()) == -1)
send_event(err_pipe[1], EVENT_FORK_ERR, errno, NULL);
if (pid != 0)
_exit(0);
}
#endif
@@ -332,29 +320,32 @@ int main (int argc, char **argv)
}
else if (getuid() == 0)
{
send_event(err_pipe[1], EVENT_PIDFILE, errno);
send_event(err_pipe[1], EVENT_PIDFILE, errno, daemon->runfile);
_exit(0);
}
}
/* open stdout etc to /dev/null */
nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
log_err = log_start(ent_pw, err_pipe[1]);
log_err = log_start(ent_pw, err_pipe[1]);
if (!option_bool(OPT_DEBUG))
{
/* open stdout etc to /dev/null */
int nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
#ifndef NO_FORK
if (daemon->dhcp && daemon->lease_change_command)
#ifdef HAVE_SCRIPT
if (daemon->dhcp && (daemon->lease_change_command || daemon->luascript))
daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
#endif
if (!(daemon->options & OPT_DEBUG) && getuid() == 0)
if (!option_bool(OPT_DEBUG) && getuid() == 0)
{
int bad_capabilities = 0;
gid_t dummy;
@@ -364,23 +355,29 @@ int main (int argc, char **argv)
(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, daemon->groupname);
_exit(0);
}
if (ent_pw && ent_pw->pw_uid != 0)
{
#if defined(HAVE_LINUX_NETWORK)
#if defined(HAVE_LINUX_NETWORK)
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
CAP_NET_RAW (for icmp) if we're doing dhcp. If we have yet to bind
ports because of DAD, we need CAP_NET_BIND_SERVICE too. */
if (is_dad_listeners())
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETUID) | (1 << CAP_NET_BIND_SERVICE);
else
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
bad_capabilities = errno;
#elif defined(HAVE_SOLARIS_PRIVS)
#elif defined(HAVE_SOLARIS_NETWORK)
/* http://developers.sun.com/solaris/articles/program_privileges.html */
priv_set_t *priv_set;
@@ -400,33 +397,34 @@ int main (int argc, char **argv)
if (priv_set)
priv_freeset(priv_set);
#elif defined(HAVE_SOLARIS_NETWORK)
bad_capabilities = ENOTSUP;
#endif
if (bad_capabilities != 0)
{
send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, NULL);
_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, daemon->username);
_exit(0);
}
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
if (is_dad_listeners())
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE);
else
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
if (capset(hdr, data) == -1)
{
send_event(err_pipe[1], EVENT_CAP_ERR, errno);
send_event(err_pipe[1], EVENT_CAP_ERR, errno, NULL);
_exit(0);
}
#endif
@@ -435,8 +433,8 @@ int main (int argc, char **argv)
}
#ifdef HAVE_LINUX_NETWORK
if (daemon->options & OPT_DEBUG)
prctl(PR_SET_DUMPABLE, 1);
if (option_bool(OPT_DEBUG))
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif
if (daemon->port == 0)
@@ -449,7 +447,7 @@ int main (int argc, char **argv)
my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
#ifdef HAVE_DBUS
if (daemon->options & OPT_DBUS)
if (option_bool(OPT_DBUS))
{
if (daemon->dbus)
my_syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
@@ -465,12 +463,12 @@ int main (int argc, char **argv)
if (bind_fallback)
my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
if (!(daemon->options & OPT_NOWILD))
if (!option_bool(OPT_NOWILD))
for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
if (if_tmp->name && !if_tmp->used)
my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
if (daemon->port != 0 && (daemon->options & OPT_NO_RESOLV))
if (daemon->port != 0 && option_bool(OPT_NO_RESOLV))
{
if (daemon->resolv_files && !daemon->resolv_files->is_default)
my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
@@ -482,6 +480,7 @@ int main (int argc, char **argv)
if (daemon->max_logs != 0)
my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
@@ -490,26 +489,29 @@ int main (int argc, char **argv)
{
prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
my_syslog(LOG_INFO,
my_syslog(MS_DHCP | LOG_INFO,
(dhcp_tmp->flags & CONTEXT_STATIC) ?
_("DHCP, static leases only on %.0s%s, lease time %s") :
(dhcp_tmp->flags & CONTEXT_PROXY) ?
_("DHCP, proxy on subnet %.0s%s%.0s") :
_("DHCP, IP range %s -- %s, lease time %s"),
daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
}
}
#endif
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
max_fd = FD_SETSIZE;
#endif
my_syslog(LOG_INFO, "TFTP %s%s %s",
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
daemon->options & OPT_TFTP_SECURE ? _("secure mode") : "");
option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "");
/* This is a guess, it assumes that for small limits,
disjoint files might be served, but for large limits,
@@ -534,7 +536,7 @@ int main (int argc, char **argv)
if (daemon->tftp_max > max_fd)
{
daemon->tftp_max = max_fd;
my_syslog(LOG_WARNING,
my_syslog(MS_TFTP | LOG_WARNING,
_("restricting maximum simultaneous TFTP transfers to %d"),
daemon->tftp_max);
}
@@ -571,22 +573,36 @@ int main (int argc, char **argv)
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
((daemon->options & OPT_DBUS) && !daemon->dbus))
(option_bool(OPT_DBUS) && !daemon->dbus))
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
}
/* Wake every second whilst waiting for DAD to complete */
else if (is_dad_listeners())
{
t.tv_sec = 1;
t.tv_usec = 0;
tp = &t;
}
#ifdef HAVE_DBUS
set_dbus_listeners(&maxfd, &rset, &wset, &eset);
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
bump_maxfd(daemon->dhcpfd, &maxfd);
if (daemon->pxefd != -1)
{
FD_SET(daemon->pxefd, &rset);
bump_maxfd(daemon->pxefd, &maxfd);
}
}
#endif
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
@@ -596,7 +612,8 @@ int main (int argc, char **argv)
FD_SET(piperead, &rset);
bump_maxfd(piperead, &maxfd);
#ifndef NO_FORK
#ifdef HAVE_DHCP
# ifdef HAVE_SCRIPT
while (helper_buf_empty() && do_script_run(now));
if (!helper_buf_empty())
@@ -604,11 +621,12 @@ int main (int argc, char **argv)
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
#else
# else
/* need this for other side-effects */
while (do_script_run(now));
# endif
#endif
/* must do this just before select(), when we know no
more calls to my_syslog() can occur */
set_log_writer(&wset, &maxfd);
@@ -622,6 +640,20 @@ int main (int argc, char **argv)
now = dnsmasq_time();
check_log_writer(&wset);
/* Check the interfaces to see if any have exited DAD state
and if so, bind the address. */
if (is_dad_listeners())
{
enumerate_interfaces();
/* NB, is_dad_listeners() == 1 --> we're binding interfaces */
create_bound_listeners(0);
}
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast();
#endif
/* Check for changes to resolv files once per second max. */
/* Don't go silent for long periods if the clock goes backwards. */
@@ -629,23 +661,19 @@ int main (int argc, char **argv)
difftime(now, daemon->last_resolv) > 1.0 ||
difftime(now, daemon->last_resolv) < -1.0)
{
daemon->last_resolv = now;
/* poll_resolv doesn't need to reload first time through, since
that's queued anyway. */
if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL))
poll_resolv();
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
}
if (FD_ISSET(piperead, &rset))
async_event(piperead, now);
#ifdef HAVE_LINUX_NETWORK
if (FD_ISSET(daemon->netlinkfd, &rset))
netlink_multicast();
#endif
#ifdef HAVE_DBUS
/* if we didn't create a DBus connection, retry now. */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
if (option_bool(OPT_DBUS) && !daemon->dbus)
{
char *err;
if ((err = dbus_init()))
@@ -662,12 +690,19 @@ int main (int argc, char **argv)
check_tftp_listeners(&rset, now);
#endif
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(now);
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
if (FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(now, 0);
if (daemon->pxefd != -1 && FD_ISSET(daemon->pxefd, &rset))
dhcp_packet(now, 1);
}
#ifndef NO_FORK
# ifdef HAVE_SCRIPT
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write();
# endif
#endif
}
@@ -708,28 +743,57 @@ static void sig_handler(int sig)
else
return;
send_event(pipewrite, event, 0);
send_event(pipewrite, event, 0, NULL);
errno = errsave;
}
}
void send_event(int fd, int event, int data)
void send_event(int fd, int event, int data, char *msg)
{
struct event_desc ev;
struct iovec iov[2];
ev.event = event;
ev.data = data;
ev.msg_sz = msg ? strlen(msg) : 0;
iov[0].iov_base = &ev;
iov[0].iov_len = sizeof(ev);
iov[1].iov_base = msg;
iov[1].iov_len = ev.msg_sz;
/* error pipe, debug mode. */
if (fd == -1)
fatal_event(&ev);
fatal_event(&ev, msg);
else
/* pipe is non-blocking and struct event_desc is smaller than
PIPE_BUF, so this either fails or writes everything */
while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
while (writev(fd, iov, msg ? 2 : 1) == -1 && errno == EINTR);
}
static void fatal_event(struct event_desc *ev)
/* NOTE: the memory used to return msg is leaked: use msgs in events only
to describe fatal errors. */
static int read_event(int fd, struct event_desc *evp, char **msg)
{
char *buf;
if (!read_write(fd, (unsigned char *)evp, sizeof(struct event_desc), 1))
return 0;
*msg = NULL;
if (evp->msg_sz != 0 &&
(buf = malloc(evp->msg_sz + 1)) &&
read_write(fd, (unsigned char *)buf, evp->msg_sz, 1))
{
buf[evp->msg_sz] = 0;
*msg = buf;
}
return 1;
}
static void fatal_event(struct event_desc *ev, char *msg)
{
errno = ev->data;
@@ -737,6 +801,9 @@ static void fatal_event(struct event_desc *ev)
{
case EVENT_DIE:
exit(0);
case EVENT_FORK_ERR:
die(_("cannot fork into background: %s"), NULL, EC_MISC);
case EVENT_PIPE_ERR:
die(_("failed to create helper: %s"), NULL, EC_MISC);
@@ -745,19 +812,19 @@ static void fatal_event(struct event_desc *ev)
die(_("setting capabilities failed: %s"), NULL, EC_MISC);
case EVENT_USER_ERR:
case EVENT_HUSER_ERR:
die(_("failed to change user-id to %s: %s"),
ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
EC_MISC);
die(_("failed to change user-id to %s: %s"), msg, EC_MISC);
case EVENT_GROUP_ERR:
die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
die(_("failed to change group-id to %s: %s"), msg, EC_MISC);
case EVENT_PIDFILE:
die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
die(_("failed to open pidfile %s: %s"), msg, EC_FILE);
case EVENT_LOG_ERR:
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
die(_("cannot open log %s: %s"), msg, EC_FILE);
case EVENT_LUA_ERR:
die(_("failed to load Lua script: %s"), msg, EC_MISC);
}
}
@@ -766,18 +833,24 @@ static void async_event(int pipe, time_t now)
pid_t p;
struct event_desc ev;
int i;
if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
char *msg;
/* NOTE: the memory used to return msg is leaked: use msgs in events only
to describe fatal errors. */
if (read_event(pipe, &ev, &msg))
switch (ev.event)
{
case EVENT_RELOAD:
clear_cache_and_reload(now);
if (daemon->port != 0 && daemon->resolv_files && (daemon->options & OPT_NO_POLL))
if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
{
reload_servers(daemon->resolv_files->name);
check_servers();
}
#ifdef HAVE_DHCP
rerun_scripts();
#endif
break;
case EVENT_DUMP:
@@ -786,11 +859,13 @@ static void async_event(int pipe, time_t now)
break;
case EVENT_ALARM:
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
lease_prune(NULL, now);
lease_update_file(now);
}
#endif
break;
case EVENT_CHILD:
@@ -808,11 +883,11 @@ static void async_event(int pipe, time_t now)
break;
case EVENT_KILLED:
my_syslog(LOG_WARNING, _("child process killed by signal %d"), ev.data);
my_syslog(LOG_WARNING, _("script process killed by signal %d"), ev.data);
break;
case EVENT_EXITED:
my_syslog(LOG_WARNING, _("child process exited with status %d"), ev.data);
my_syslog(LOG_WARNING, _("script process exited with status %d"), ev.data);
break;
case EVENT_EXEC_ERR:
@@ -821,9 +896,10 @@ static void async_event(int pipe, time_t now)
break;
/* necessary for fatal errors in helper */
case EVENT_HUSER_ERR:
case EVENT_USER_ERR:
case EVENT_DIE:
fatal_event(&ev);
case EVENT_LUA_ERR:
fatal_event(&ev, msg);
break;
case EVENT_REOPEN:
@@ -840,7 +916,7 @@ static void async_event(int pipe, time_t now)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
#ifndef NO_FORK
#if defined(HAVE_SCRIPT)
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
@@ -856,6 +932,9 @@ static void async_event(int pipe, time_t now)
if (daemon->lease_stream)
fclose(daemon->lease_stream);
if (daemon->runfile)
unlink(daemon->runfile);
my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
flush_log();
@@ -863,7 +942,7 @@ static void async_event(int pipe, time_t now)
}
}
static void poll_resolv()
void poll_resolv(int force, int do_reload, time_t now)
{
struct resolvc *res, *latest;
struct stat statbuf;
@@ -871,19 +950,37 @@ static void poll_resolv()
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
if (daemon->port == 0 || option_bool(OPT_NO_POLL))
return;
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (force)
{
res->mtime = 0;
continue;
}
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
if (res->mtime != 0)
{
/* existing file evaporated, force selection of the latest
file even if its mtime hasn't changed since we last looked */
poll_resolv(1, do_reload, now);
return;
}
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (force || (statbuf.st_mtime != res->mtime))
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
@@ -900,8 +997,8 @@ static void poll_resolv()
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
if (daemon->options & OPT_RELOAD)
cache_reload(daemon->addn_hosts);
if (option_bool(OPT_RELOAD) && do_reload)
clear_cache_and_reload(now);
}
else
{
@@ -918,11 +1015,12 @@ static void poll_resolv()
void clear_cache_and_reload(time_t now)
{
if (daemon->port != 0)
cache_reload(daemon->addn_hosts);
cache_reload();
#ifdef HAVE_DHCP
if (daemon->dhcp)
{
if (daemon->options & OPT_ETHERS)
if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf);
@@ -931,6 +1029,7 @@ void clear_cache_and_reload(time_t now)
lease_update_file(now);
lease_update_dns();
}
#endif
}
static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
@@ -1038,7 +1137,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if (confd == -1)
continue;
if (daemon->options & OPT_NOWILD)
if (option_bool(OPT_NOWILD))
iface = listener->iface;
else
{
@@ -1064,7 +1163,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
close(confd);
}
#ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0)
else if (!option_bool(OPT_DEBUG) && (p = fork()) != 0)
{
if (p != -1)
{
@@ -1084,15 +1183,14 @@ static void check_dns_listeners(fd_set *set, time_t now)
unsigned char *buff;
struct server *s;
int flags;
struct in_addr dst_addr_4;
dst_addr_4.s_addr = 0;
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!(daemon->options & OPT_DEBUG))
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!option_bool(OPT_DEBUG))
alarm(CHILD_LIFETIME);
#endif
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next)
s->tcpfd = -1;
@@ -1103,10 +1201,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
if (listener->family == AF_INET)
dst_addr_4 = iface->addr.in.sin_addr;
buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
buff = tcp_request(confd, now, &iface->addr, iface->netmask);
shutdown(confd, SHUT_RDWR);
close(confd);
@@ -1121,7 +1216,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
close(s->tcpfd);
}
#ifndef NO_FORK
if (!(daemon->options & OPT_DEBUG))
if (!option_bool(OPT_DEBUG))
{
flush_log();
_exit(0);
@@ -1132,7 +1227,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
}
}
#ifdef HAVE_DHCP
int make_icmp_sock(void)
{
int fd;
@@ -1255,5 +1350,6 @@ int icmp_ping(struct in_addr addr)
return gotreply;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2008 Simon Kelley
/* dnsmasq is Copyright (c) 2000-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,11 +10,11 @@
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) */
@@ -22,28 +22,45 @@
# define _FILE_OFFSET_BITS 64
#endif
/* Get linux C library versions. */
#ifdef __linux__
# define _GNU_SOURCE
/* 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 <netinet/in.h>
#include <sys/socket.h>
#ifdef __APPLE__
# include <nameser.h>
# include <arpa/nameser_compat.h>
#else
# include <arpa/nameser.h>
/* Define before netinet/in.h to select API. OSX Lion onwards. */
# define __APPLE_USE_RFC_3542
#endif
#include <netinet/in.h>
/* and this. */
/* Also needed before config.h. */
#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"
#ifdef HAVE_DHCP6
#include "dhcp6_protocol.h"
#endif
#define gettext_noop(S) (S)
#ifndef LOCALEDIR
# define _(S) (S)
@@ -55,10 +72,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>
@@ -66,6 +82,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>
@@ -79,7 +99,7 @@
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun)
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun) || defined (__ANDROID__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -106,7 +126,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
#define LINUX_CAPABILITY_VERSION_3 0x20080522
#include <sys/prctl.h>
#elif defined(HAVE_SOLARIS_PRIVS)
#elif defined(HAVE_SOLARIS_NETWORK)
#include <priv.h>
#endif
@@ -115,7 +135,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
/* Async event queue */
struct event_desc {
int event, data;
int event, data, msg_sz;
};
#define EVENT_RELOAD 1
@@ -135,6 +155,8 @@ struct event_desc {
#define EVENT_GROUP_ERR 15
#define EVENT_DIE 16
#define EVENT_LOG_ERR 17
#define EVENT_FORK_ERR 18
#define EVENT_LUA_ERR 19
/* Exit codes. */
#define EC_GOOD 0
@@ -152,38 +174,52 @@ struct event_desc {
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV (1u<<0)
#define OPT_FILTER (1u<<1)
#define OPT_LOG (1u<<2)
#define OPT_SELFMX (1u<<3)
#define OPT_NO_HOSTS (1u<<4)
#define OPT_NO_POLL (1u<<5)
#define OPT_DEBUG (1u<<6)
#define OPT_ORDER (1u<<7)
#define OPT_NO_RESOLV (1u<<8)
#define OPT_EXPAND (1u<<9)
#define OPT_LOCALMX (1u<<10)
#define OPT_NO_NEG (1u<<11)
#define OPT_NODOTS_LOCAL (1u<<12)
#define OPT_NOWILD (1u<<13)
#define OPT_ETHERS (1u<<14)
#define OPT_RESOLV_DOMAIN (1u<<15)
#define OPT_NO_FORK (1u<<16)
#define OPT_AUTHORITATIVE (1u<<17)
#define OPT_LOCALISE (1u<<18)
#define OPT_DBUS (1u<<19)
#define OPT_DHCP_FQDN (1u<<20)
#define OPT_NO_PING (1u<<21)
#define OPT_LEASE_RO (1u<<22)
#define OPT_ALL_SERVERS (1u<<23)
#define OPT_RELOAD (1u<<24)
#define OPT_TFTP (1u<<25)
#define OPT_TFTP_SECURE (1u<<26)
#define OPT_TFTP_NOBLOCK (1u<<27)
#define OPT_LOG_OPTS (1u<<28)
#define OPT_TFTP_APREF (1u<<29)
#define OPT_NO_OVERRIDE (1u<<30)
#define OPT_NO_REBIND (1u<<31)
/* 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_FQDN_UPDATE 36
#define OPT_LAST 37
/* 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 {
@@ -201,7 +237,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;
};
@@ -219,7 +255,8 @@ struct naptr {
};
struct txt_record {
char *name, *txt;
char *name;
unsigned char *txt;
unsigned short class, len;
struct txt_record *next;
};
@@ -264,22 +301,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.
@@ -287,19 +329,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
};
@@ -315,6 +345,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;
@@ -341,7 +373,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;
};
@@ -367,13 +400,19 @@ 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;
@@ -384,7 +423,7 @@ struct frec {
#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;
@@ -396,8 +435,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 */
@@ -413,10 +450,13 @@ struct dhcp_lease {
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr, override;
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;
#ifdef HAVE_DHCP6
char is_ipv6;
#endif
struct dhcp_lease *next;
};
@@ -430,6 +470,12 @@ 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];
@@ -442,7 +488,7 @@ struct dhcp_config {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
char *hostname, *domain;
struct dhcp_netid netid;
struct dhcp_netid_list *netid;
struct in_addr addr;
time_t decline_time;
unsigned int lease_time;
@@ -455,7 +501,6 @@ struct dhcp_config {
#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 */
@@ -464,7 +509,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;
};
@@ -472,23 +522,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 {
@@ -506,12 +570,10 @@ struct dhcp_mac {
struct dhcp_mac *next;
};
#ifdef HAVE_BSD_BRIDGE
struct dhcp_bridge {
char iface[IF_NAMESIZE];
struct dhcp_bridge *alias, *next;
};
#endif
struct cond_domain {
char *domain;
@@ -524,7 +586,12 @@ struct dhcp_context {
struct in_addr netmask, broadcast;
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
#ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */
int prefix;
#endif
int flags;
char *interface;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
@@ -532,25 +599,13 @@ 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
#define CONTEXT_IPV6 16
struct ping_result {
struct in_addr addr;
time_t time;
unsigned int hash;
struct ping_result *next;
};
@@ -568,18 +623,35 @@ struct tftp_transfer {
int backoff;
unsigned int block, blocksize, expansion;
off_t offset;
struct sockaddr_in peer;
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;
@@ -591,6 +663,7 @@ extern struct daemon {
char *mxtarget;
char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
int group_set, osport;
char *domain_suffix;
struct cond_domain *cond_domain;
@@ -604,16 +677,22 @@ extern struct daemon {
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl;
unsigned long local_ttl, neg_ttl, max_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_context *dhcp, *dhcp6;
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, *bootp_dynamic;
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;
@@ -621,6 +700,9 @@ extern struct daemon {
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 */
@@ -632,27 +714,33 @@ extern struct daemon {
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];
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;
#ifdef HAVE_BSD_BRIDGE
struct dhcp_bridge *bridges;
#ifdef HAVE_DHCP6
int duid_len;
unsigned char *duid;
struct iovec outpacket;
int dhcp6fd;
#endif
/* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus;
@@ -663,12 +751,15 @@ pid_t tcp_pids[MAX_PROCS];
/* TFTP stuff */
struct tftp_transfer *tftp_trans;
/* utility string buffer, hold max sized IP address as string */
char *addrbuff;
} *daemon;
/* cache.c */
void cache_init(void);
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg);
char *record_source(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,
@@ -679,35 +770,39 @@ 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(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);
@@ -717,6 +812,9 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(char *a, char *b);
time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
#endif
int retry_send(void);
void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
@@ -740,44 +838,56 @@ 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(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);
void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface);
/* 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 fix_fd(int fd);
struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
/* dhcp.c */
#ifdef HAVE_DHCP
void dhcp_init(void);
void dhcp_packet(time_t now);
char *get_domain(struct in_addr addr);
void dhcp_packet(time_t now, int pxe_fd);
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg);
struct dhcp_context *address_available(struct dhcp_context *context,
struct in_addr addr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr,
struct dhcp_netid *netids);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);int address_allocate(struct dhcp_context *context,
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,
@@ -791,12 +901,17 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
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);
struct dhcp_lease *lease_allocate4(struct in_addr addr);
#ifdef HAVE_DHCP6
struct dhcp_lease *lease_allocate6(struct in6_addr *addrp);
#endif
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, int auth);
@@ -805,22 +920,29 @@ 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);
void send_event(int fd, int event, int data);
#endif
void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now);
/* netlink.c */
#ifdef HAVE_LINUX_NETWORK
@@ -836,18 +958,20 @@ 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);
void emit_dbus_signal(int action, char *mac, char *hostname, char *addr);
# ifdef HAVE_DHCP
void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
# endif
#endif
/* helper.c */
#ifndef NO_FORK
#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,
@@ -860,3 +984,15 @@ 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
/* rfc3315.c */
#ifdef HAVE_DHCP6
void make_duid(time_t now);
size_t dhcp6_reply(struct dhcp_context *context, size_t sz);
#endif

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,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"
@@ -20,15 +20,15 @@ static struct frec *lookup_frec(unsigned short id, unsigned int crc);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
static unsigned short get_id(unsigned int crc);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
static void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
{
struct msghdr msg;
struct iovec iov[1];
@@ -65,15 +65,15 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
if (to->sa.sa_family == AF_INET)
{
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
struct in_pktinfo p;
p.ipi_ifindex = 0;
p.ipi_spec_dst = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
*a = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_SENDSRCADDR;
@@ -82,15 +82,16 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
else
#ifdef HAVE_IPV6
{
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
pkt->ipi6_addr = source->addr.addr6;
struct in6_pktinfo p;
p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
p.ipi6_addr = source->addr.addr6;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = IPV6_PKTINFO;
cmptr->cmsg_level = IPV6_LEVEL;
cmptr->cmsg_type = daemon->v6pktinfo;
cmptr->cmsg_level = IPPROTO_IPV6;
}
#else
iface = 0; /* eliminate warning */
(void)iface; /* eliminate warning */
#endif
}
@@ -110,8 +111,8 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
}
}
static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain)
static unsigned int search_servers(time_t now, struct all_addr **addrpp,
unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
{
/* If the query ends in the domain in one of our servers, set
@@ -121,13 +122,13 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned int namelen = strlen(qdomain);
unsigned int matchlen = 0;
struct server *serv;
unsigned short flags = 0;
unsigned int flags = 0;
for (serv = daemon->servers; serv; serv=serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_FOR_NODOTS;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
@@ -153,38 +154,64 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
char *matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen &&
hostname_isequal(matchstart, serv->domain) &&
domainlen >= matchlen &&
(domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
(domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_HAS_DOMAIN;
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
if (serv->flags & SERV_NO_REBIND)
*norebind = 1;
else
{
if (sflag & qtype)
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
/* implement priority rules for --address and --server for same domain.
--address wins if the address is for the correct AF
--server wins otherwise. */
if (domainlen != 0 && domainlen == matchlen)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
if ((serv->flags & SERV_LITERAL_ADDRESS))
{
if (!(sflag & qtype) && flags == 0)
continue;
}
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
{
if (flags & (F_IPV4 | F_IPV6))
continue;
}
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
if (domainlen >= matchlen)
{
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else
flags = 0;
}
}
}
}
}
if (flags == 0 && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception for NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == 0 && !(qtype & F_QUERY) &&
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward A or AAAA queries for simple names, except the empty name */
flags = F_NOERR;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR;
@@ -197,22 +224,29 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
}
else if ((*type) & SERV_USE_RESOLV)
{
*type = 0; /* use normal servers for this domain */
*domain = NULL;
}
return flags;
}
static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
HEADER *header, size_t plen, time_t now, struct frec *forward)
struct dns_header *header, size_t plen, time_t now, struct frec *forward)
{
char *domain = NULL;
int type = 0;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned short flags = 0;
unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
@@ -221,7 +255,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
forward->sentto->failed_queries++;
if (!(daemon->options & OPT_ORDER))
if (!option_bool(OPT_ORDER))
{
forward->forwardall = 1;
daemon->last_server = NULL;
@@ -234,7 +268,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (!flags && !(forward = get_new_frec(now, NULL)))
/* table full - server failure. */
@@ -242,30 +276,45 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (forward)
{
/* force unchanging id for signed packets */
int is_sign;
find_pseudoheader(header, plen, NULL, NULL, &is_sign);
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(is_sign, forward->orig_id, crc);
forward->new_id = get_id(crc);
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
if (norebind)
forward->flags |= FREC_NOREBIND;
if (header->hb4 & HB4_CD)
forward->flags |= FREC_CHECKING_DISABLED;
/* In strict_order mode, or when using domain specific servers
always try servers in the order specified in resolv.conf,
header->id = htons(forward->new_id);
/* In strict_order mode, always try servers in the order
specified in resolv.conf, if a domain is given
always try all the available servers,
otherwise, use the one last known to work. */
if (type != 0 || (daemon->options & OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server))
if (type == 0)
{
if (option_bool(OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server) ||
daemon->forwardcount++ > FORWARD_TEST ||
difftime(now, daemon->forwardtime) > FORWARD_TIME)
{
start = daemon->servers;
forward->forwardall = 1;
daemon->forwardcount = 0;
daemon->forwardtime = now;
}
}
else
{
start = daemon->servers;
forward->forwardall = 1;
if (!option_bool(OPT_ORDER))
forward->forwardall = 1;
}
}
}
@@ -278,7 +327,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
struct server *firstsentto = start;
int forwarded = 0;
if (udpaddr && option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
while (1)
{
/* only send to servers dealing with our domain.
@@ -314,6 +366,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
if (sendto(fd, (char *)header, plen, 0,
@@ -367,14 +429,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (udpfd != -1)
{
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
return 0;
}
static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n)
static size_t process_reply(struct dns_header *header, time_t now,
struct server *server, size_t n, int check_rebind, int checking_disabled)
{
unsigned char *pheader, *sizep;
int munged = 0, is_sign;
@@ -394,29 +456,33 @@ static size_t process_reply(HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
/* RFC 4035 sect 4.6 para 3 */
if (!is_sign && !option_bool(OPT_DNSSEC))
header->hb4 &= ~HB4_AD;
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!(daemon->options & OPT_LOG))
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
munged = 1;
header->rcode = NXDOMAIN;
header->aa = 0;
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
}
else
{
if (header->rcode == NXDOMAIN &&
if (RCODE(header) == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now))
{
@@ -424,13 +490,13 @@ static size_t process_reply(HEADER *header, time_t now,
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
munged = 1;
header->aa = 1;
header->rcode = NOERROR;
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
}
if (extract_addresses(header, n, daemon->namebuff, now))
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected"));
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
}
}
@@ -456,7 +522,7 @@ void reply_query(int fd, int family, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
HEADER *header;
struct dns_header *header;
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
@@ -480,17 +546,17 @@ void reply_query(int fd, int family, time_t now)
sockaddr_isequal(&server->addr, &serveraddr))
break;
header = (HEADER *)daemon->packet;
header = (struct dns_header *)daemon->packet;
if (!server ||
n < (int)sizeof(HEADER) || !header->qr ||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
return;
server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
!(daemon->options & OPT_ORDER) &&
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
!option_bool(OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
@@ -507,8 +573,7 @@ void reply_query(int fd, int family, time_t now)
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
header->hb3 &= ~(HB3_QR | HB3_TC);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
@@ -517,7 +582,7 @@ void reply_query(int fd, int family, time_t now)
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
server = NULL;
else
{
@@ -532,7 +597,7 @@ void reply_query(int fd, int family, time_t now)
break;
}
}
if (!(daemon->options & OPT_ALL_SERVERS))
if (!option_bool(OPT_ALL_SERVERS))
daemon->last_server = server;
}
@@ -541,13 +606,18 @@ void reply_query(int fd, int family, time_t now)
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
{
if ((nn = process_reply(header, now, server, (size_t)n)))
int check_rebind = !(forward->flags & FREC_NOREBIND);
if (!option_bool(OPT_NO_REBIND))
check_rebind = 0;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
header->hb4 |= HB4_RA; /* recursion if available */
send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
free_frec(forward); /* cancel */
@@ -557,7 +627,7 @@ void reply_query(int fd, int family, time_t now)
void receive_query(struct listener *listen, time_t now)
{
HEADER *header = (HEADER *)daemon->packet;
struct dns_header *header = (struct dns_header *)daemon->packet;
union mysockaddr source_addr;
unsigned short type;
struct all_addr dst_addr;
@@ -587,7 +657,7 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
if (listen->family == AF_INET && option_bool(OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
@@ -612,9 +682,9 @@ void receive_query(struct listener *listen, time_t now)
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return;
if (n < (int)sizeof(HEADER) ||
if (n < (int)sizeof(struct dns_header) ||
(msg.msg_flags & MSG_TRUNC) ||
header->qr)
(header->hb3 & HB3_QR))
return;
source_addr.sa.sa_family = listen->family;
@@ -622,8 +692,8 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6)
source_addr.in6.sin6_flowinfo = 0;
#endif
if (!(daemon->options & OPT_NOWILD))
if (!option_bool(OPT_NOWILD))
{
struct ifreq ifr;
@@ -633,23 +703,39 @@ void receive_query(struct listener *listen, time_t now)
#if defined(HAVE_LINUX_NETWORK)
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)
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (listen->family == AF_INET)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *((unsigned int *)CMSG_DATA(cmptr));
#else
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
{
union {
unsigned char *c;
unsigned int *i;
struct in_addr *a;
#ifndef HAVE_SOLARIS_NETWORK
struct sockaddr_dl *s;
#endif
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *(p.i);
#else
if_index = p.s->sdl_index;
#endif
}
}
#endif
@@ -657,33 +743,28 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
dst_addr.addr.addr6 = p.p->ipi6_addr;
if_index = p.p->ipi6_ifindex;
}
}
#endif
/* enforce available interface configuration */
if (if_index == 0)
return;
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
#else
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (!iface_check(listen->family, &dst_addr, &ifr, &if_index))
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name))
return;
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
option_bool(OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
@@ -710,7 +791,7 @@ void receive_query(struct listener *listen, time_t now)
dst_addr_4, netmask, now);
if (m >= 1)
{
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header,
send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header,
m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
}
@@ -726,17 +807,25 @@ void receive_query(struct listener *listen, time_t now)
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
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)
{
int size = 0;
size_t size = 0;
int norebind = 0;
int checking_disabled;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
/* Max TCP packet + slop */
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
HEADER *header;
struct dns_header *header;
struct server *last_server;
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
while (1)
{
if (!packet ||
@@ -745,51 +834,59 @@ unsigned char *tcp_request(int confd, time_t now,
!read_write(confd, packet, size, 1))
return packet;
if (size < (int)sizeof(HEADER))
if (size < (int)sizeof(struct dns_header))
continue;
header = (HEADER *)packet;
header = (struct dns_header *)packet;
/* save state of "cd" flag in query */
checking_disabled = header->hb4 & HB4_CD;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
char types[20];
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
{
char types[20];
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, types);
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif
}
}
if (local_addr->sa.sa_family == AF_INET)
dst_addr_4 = local_addr->in.sin_addr;
else
dst_addr_4.s_addr = 0;
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
local_addr, netmask, now);
dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0)
{
unsigned short flags = 0;
unsigned int flags = 0;
struct all_addr *addrp = NULL;
int type = 0;
char *domain = NULL;
if (option_bool(OPT_ADD_MAC))
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
else
last_server = daemon->last_server;
@@ -803,7 +900,7 @@ unsigned char *tcp_request(int confd, time_t now,
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
while (1)
{
{
if (!firstsendto)
firstsendto = last_server;
else
@@ -819,18 +916,38 @@ unsigned char *tcp_request(int confd, time_t now,
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
if ((last_server->tcpfd == -1) &&
(last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
(!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
if (last_server->tcpfd == -1)
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue;
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
if (last_server->tcpfd == -1)
continue;
c1 = size >> 8;
c2 = size;
@@ -868,7 +985,8 @@ unsigned char *tcp_request(int confd, time_t now,
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m);
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
break;
}
@@ -900,6 +1018,7 @@ static struct frec *allocate_frec(time_t now)
f->time = now;
f->sentto = NULL;
f->rfd4 = NULL;
f->flags = 0;
#ifdef HAVE_IPV6
f->rfd6 = NULL;
#endif
@@ -952,6 +1071,7 @@ static void free_frec(struct frec *f)
f->rfd4 = NULL;
f->sentto = NULL;
f->flags = 0;
#ifdef HAVE_IPV6
if (f->rfd6 && --(f->rfd6->refcount) == 0)
@@ -1073,22 +1193,12 @@ void server_gone(struct server *server)
daemon->srv_save = NULL;
}
/* return unique random ids.
For signed packets we can't change the ID without breaking the
signing, so we keep the same one. In this case force is set, and this
routine degenerates into killing any conflicting forward record. */
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
/* return unique random ids. */
static unsigned short get_id(unsigned int crc)
{
unsigned short ret = 0;
if (force)
{
struct frec *f = lookup_frec(force_id, crc);
if (f)
free_frec(f); /* free */
ret = force_id;
}
else do
do
ret = rand16();
while (lookup_frec(ret, crc));

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-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,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_SCRIPT
/* This file has code to fork a helper process which recieves data via a pipe
shared with the main process and which is responsible for calling a script when
DHCP leases change.
@@ -28,15 +30,25 @@
main process.
*/
#ifndef NO_FORK
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);
#ifdef HAVE_LUASCRIPT
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
lua_State *lua;
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
#endif
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;
@@ -60,7 +72,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
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, NULL);
_exit(0);
}
@@ -78,46 +90,107 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
if (!(daemon->options & OPT_DEBUG) && uid != 0)
if (!option_bool(OPT_DEBUG) && uid != 0)
{
gid_t dummy;
if (setgroups(0, &dummy) == -1 ||
setgid(gid) == -1 ||
setuid(uid) == -1)
{
if (daemon->options & OPT_NO_FORK)
if (option_bool(OPT_NO_FORK))
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_HUSER_ERR, errno);
send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0);
send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */
send_event(err_fd, EVENT_HUSER_ERR, errno);;
send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
}
_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--)
/* close all the sockets etc, we don't need them here.
Don't close err_fd, in case the lua-init fails.
Note that we have to do this before lua init
so we don't close any lua fds. */
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)
max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
max_fd != event_fd && max_fd != err_fd)
close(max_fd);
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
const char *lua_err = NULL;
lua = lua_open();
luaL_openlibs(lua);
/* get Lua to load our script file */
if (luaL_dofile(lua, daemon->luascript) != 0)
lua_err = lua_tostring(lua, -1);
else
{
lua_getglobal(lua, "lease");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_err = _("lease() function missing in Lua script");
}
if (lua_err)
{
if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */
send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
}
_exit(0);
}
lua_pop(lua, 1); /* remove nil from stack */
lua_getglobal(lua, "init");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
else
lua_pop(lua, 1); /* remove nil from stack */
}
#endif
/* All init done, close our copy of the error pipe, so that main process can return */
if (err_fd != -1)
close(err_fd);
/* loop here */
while(1)
{
struct script_data data;
char *p, *action_str, *hostname = NULL;
char *p, *action_str, *hostname = NULL, *domain = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff;
unsigned char *end, *extradata, *alloc_buff = NULL;
int err = 0;
free(alloc_buff);
/* we read zero bytes when pipe closed: this is our signal to exit */
if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
_exit(0);
{
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
lua_getglobal(lua, "shutdown");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
}
#endif
_exit(0);
}
if (data.action == ACTION_DEL)
action_str = "del";
else if (data.action == ACTION_ADD)
@@ -137,34 +210,142 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet */
if (!read_write(pipefd[0], buf, data.clid_len, 1))
/* expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u", data.length);
#else
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
#endif
/* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
continue;
if (!read_write(pipefd[0], buf,
data.hostname_len + data.ed_len + data.clid_len, 1))
continue;
/* CLID into packet */
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
/* and expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u ", data.length);
buf += data.clid_len;
if (data.hostname_len != 0)
{
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
domain = dot+1;
*dot = 0;
}
}
extradata = buf + data.hostname_len;
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
lua_getglobal(lua, "lease"); /* function to call */
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
if (data.clid_len != 0)
{
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "client_id");
}
if (strlen(data.interface) != 0)
{
lua_pushstring(lua, data.interface);
lua_setfield(lua, -2, "interface");
}
#ifdef HAVE_BROKEN_RTC
lua_pushnumber(lua, data.length);
lua_setfield(lua, -2, "lease_length");
#else
sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires);
lua_pushnumber(lua, data.expires);
lua_setfield(lua, -2, "lease_expires");
#endif
if (hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "hostname");
}
if (domain)
{
lua_pushstring(lua, domain);
lua_setfield(lua, -2, "domain");
}
end = extradata + data.ed_len;
buf = extradata;
buf = grab_extradata_lua(buf, end, "vendor_class");
buf = grab_extradata_lua(buf, end, "supplied_hostname");
buf = grab_extradata_lua(buf, end, "cpewan_oui");
buf = grab_extradata_lua(buf, end, "cpewan_serial");
buf = grab_extradata_lua(buf, end, "cpewan_class");
buf = grab_extradata_lua(buf, end, "tags");
if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1))
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "user_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
if (data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
lua_setfield(lua, -2, "relay_address");
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
lua_pushnumber(lua, data.remaining_time);
lua_setfield(lua, -2, "time_remaining");
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "old_hostname");
}
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "mac_address");
lua_pushstring(lua, inet_ntoa(data.addr));
lua_setfield(lua, -2, "ip_address");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
#endif
/* no script, just lua */
if (!daemon->lease_change_command)
continue;
/* possible fork errors are all temporary resource problems */
while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2);
if (pid == -1)
continue;
/* wait for child to complete */
if (pid != 0)
{
@@ -178,9 +359,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{
/* On error send event back to main process for logging */
if (WIFSIGNALED(status))
send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
break;
}
@@ -203,52 +384,33 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
if (data.vclass_len != 0)
if (domain)
my_setenv("DNSMASQ_DOMAIN", domain, &err);
end = extradata + data.ed_len;
buf = extradata;
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++)
{
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;
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.uclass_len != 0)
if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
if (data.action != ACTION_DEL && data.remaining_time != 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);
}
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))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
*dot = 0;
}
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
@@ -269,66 +431,81 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
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, NULL);
_exit(0);
}
}
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;
}
#ifdef HAVE_LUASCRIPT
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
{
unsigned char *next;
if (!buf || (buf == end))
return NULL;
for (next = buf; *next != 0; next++)
if (next == end)
return NULL;
if (next != buf)
{
lua_pushstring(lua, (char *)buf);
lua_setfield(lua, -2, field);
}
return next + 1;
}
#endif
/* 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;
int i;
unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
#ifdef HAVE_DBUS
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(daemon->namebuff, p, i);
emit_dbus_signal(action, daemon->namebuff, hostname ? hostname : "", inet_ntoa(lease->addr));
#endif
/* 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)
{
@@ -350,31 +527,24 @@ 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;
#else
buf->expires = lease->expires;
#endif
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
if (lease->expires != 0)
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
else
buf->remaining_time = 0;
p = (unsigned char *)(buf+1);
if (clid_len != 0)
@@ -382,24 +552,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: spaces are allowed in hostnames (for DNS-SD)
and are likley to be a security hole in most scripts. */
for (i = 0; i < (int)hostname_len; i++)
if ((daemon->options & OPT_LEASE_RO) && hostname[i] == ' ')
*(p++) = '*';
else
*(p++) = hostname[i];
bytes_in_buf = p - (unsigned char *)buf;
}
@@ -432,3 +594,4 @@ void helper_write(void)
#endif

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,44 +10,56 @@
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;
void lease_init(time_t now)
{
unsigned long ei;
struct in_addr addr;
struct all_addr addr;
struct dhcp_lease *lease;
int clid_len, hw_len, hw_type;
FILE *leasestream;
#ifdef HAVE_DHCP6
int v6pass = 0;
#endif
/* 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,30 +69,50 @@ 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);
}
#ifdef HAVE_DHCP6
again:
#endif
/* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */
if (leasestream)
while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
while (fscanf(leasestream, "%lu %255s %64s %255s %764s",
&ei, daemon->dhcp_buff2, daemon->namebuff,
daemon->dhcp_buff, daemon->packet) == 5)
{
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
addr.s_addr = inet_addr(daemon->namebuff);
/* decode hex in place */
#ifdef HAVE_DHCP6
if (!v6pass)
#endif
{
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
}
#ifdef HAVE_DHCP6
if (v6pass)
inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6);
else
#endif
inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4);
clid_len = 0;
if (strcmp(daemon->packet, "*") != 0)
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
if (!(lease = lease_allocate(addr)))
#ifdef HAVE_DHCP6
if (v6pass)
lease = lease_allocate6(&addr.addr.addr6);
else
#endif
lease = lease_allocate4(addr.addr.addr4);
if (!lease)
die (_("too many stored leases"), NULL, EC_MISC);
#ifdef HAVE_BROKEN_RTC
@@ -94,23 +126,39 @@ void lease_init(time_t now)
even when sizeof(time_t) == 8 */
lease->expires = (time_t)ei;
#endif
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
#ifdef HAVE_DHCP6
if (!v6pass)
#endif
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, 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_DHCP6
if (!v6pass)
{
if (fscanf(leasestream, "duid %255s", daemon->dhcp_buff) == 1)
{
daemon->duid_len = parse_hex(daemon->dhcp_buff, (unsigned char *)daemon->dhcp_buff, 130, NULL, NULL);
daemon->duid = safe_malloc(daemon->duid_len);
memcpy(daemon->duid, daemon->dhcp_buff, daemon->duid_len );
v6pass = 1;
goto again;
}
/* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
if (daemon->dhcp6)
make_duid(now);
}
#endif
#ifdef HAVE_SCRIPT
if (!daemon->lease_stream)
{
int rc = 0;
@@ -131,6 +179,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;
@@ -171,7 +220,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)
{
@@ -182,11 +230,18 @@ void lease_update_file(time_t now)
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (lease->is_ipv6)
continue;
#endif
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++)
@@ -195,17 +250,12 @@ void lease_update_file(time_t now)
if (i != lease->hwaddr_len - 1)
ourprintf(&err, ":");
}
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
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 ", daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
@@ -216,6 +266,43 @@ void lease_update_file(time_t now)
ourprintf(&err, "*\n");
}
#ifdef HAVE_DHCP6
if (daemon->duid)
{
ourprintf(&err, "duid ");
for (i = 0; i < daemon->duid_len - 1; i++)
ourprintf(&err, "%.2x:", daemon->duid[i]);
ourprintf(&err, "%.2x\n", daemon->duid[i]);
for (lease = leases; lease; lease = lease->next)
{
if (!lease->is_ipv6)
continue;
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN);
ourprintf(&err, "* %s ", daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(&err, "%.2x\n", lease->clid[i]);
}
else
ourprintf(&err, "*\n");
}
}
#endif
if (fflush(daemon->lease_stream) != 0 ||
fsync(fileno(daemon->lease_stream)) < 0)
err = errno;
@@ -235,7 +322,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));
}
@@ -257,7 +344,7 @@ void lease_update_dns(void)
if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
}
@@ -278,7 +365,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if (lease->hostname)
dns_dirty = 1;
*up = lease->next; /* unlink */
*up = lease->next; /* unlink */
/* Put on old_leases list 'till we
can run the script */
@@ -300,18 +387,30 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
if (clid)
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
{
#ifdef HAVE_DHCP6
if (lease->is_ipv6)
continue;
#endif
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
}
for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
{
#ifdef HAVE_DHCP6
if (lease->is_ipv6)
continue;
#endif
if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
}
return NULL;
}
@@ -320,14 +419,41 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
if (lease->addr.s_addr == addr.s_addr)
return lease;
{
#ifdef HAVE_DHCP6
if (lease->is_ipv6)
continue;
#endif
if (lease->addr.s_addr == addr.s_addr)
return lease;
}
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)
{
#ifdef HAVE_DHCP6
if (lease->is_ipv6)
continue;
#endif
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)
static struct dhcp_lease *lease_allocate(void)
{
struct dhcp_lease *lease;
if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
@@ -335,8 +461,6 @@ struct dhcp_lease *lease_allocate(struct in_addr addr)
memset(lease, 0, sizeof(struct dhcp_lease));
lease->new = 1;
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
lease->expires = 1;
#ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */
@@ -350,6 +474,26 @@ struct dhcp_lease *lease_allocate(struct in_addr addr)
return lease;
}
struct dhcp_lease *lease_allocate4(struct in_addr addr)
{
struct dhcp_lease *lease = lease_allocate();
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
return lease;
}
#ifdef HAVE_DHCP6
struct dhcp_lease *lease_allocate6(struct in6_addr *addrp)
{
struct dhcp_lease *lease = lease_allocate();
memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ;
lease->is_ipv6 = 1;
return lease;
}
#endif
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{
time_t exp = now + (time_t)len;
@@ -473,7 +617,7 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
/* Depending on mode, we check either unqualified name or FQDN. */
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
{
if (daemon->options & OPT_DHCP_FQDN)
if (option_bool(OPT_DHCP_FQDN))
{
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
continue;
@@ -537,7 +681,7 @@ int do_script_run(time_t now)
#ifdef HAVE_DBUS
/* If we're going to be sending DBus signals, but the connection is not yet up,
delay everything until it is. */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
if (option_bool(OPT_DBUS) && !daemon->dbus)
return 0;
#endif
@@ -548,7 +692,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);
@@ -558,15 +702,17 @@ int do_script_run(time_t now)
else
{
kill_name(lease);
#ifndef NO_FORK
#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->old_hostname);
free(lease->clid);
free(lease->vendorclass);
free(lease->userclass);
free(lease->extradata);
free(lease);
return 1;
@@ -577,7 +723,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);
@@ -587,26 +733,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
#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

186
src/log.c
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,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;
@@ -54,12 +59,12 @@ int log_start(struct passwd *ent_pw, int errfd)
{
int ret = 0;
log_stderr = !!(daemon->options & OPT_DEBUG);
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
@@ -67,13 +72,19 @@ int log_start(struct passwd *ent_pw, int errfd)
{
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))
{
send_event(errfd, EVENT_LOG_ERR, errno);
send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : "");
_exit(0);
}
@@ -90,7 +101,7 @@ int log_start(struct passwd *ent_pw, int errfd)
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 &&
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0 &&
fchown(log_fd, ent_pw->pw_uid, -1) != 0)
ret = errno;
@@ -99,37 +110,34 @@ int log_start(struct passwd *ent_pw, int errfd)
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)
@@ -146,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())
{
@@ -155,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)
@@ -175,7 +196,7 @@ static void log_write(void)
if (errno == EINTR)
continue;
if (errno == EAGAIN)
if (errno == EAGAIN || errno == EWOULDBLOCK)
return; /* syslogd busy, go again when select() or poll() says so */
if (errno == ENOBUFS)
@@ -223,7 +244,8 @@ static void log_write(void)
errno == ECONNREFUSED ||
errno == EISCONN ||
errno == EINTR ||
errno == EAGAIN)
errno == EAGAIN ||
errno == EWOULDBLOCK)
{
/* try again on next syslog() call */
connection_good = 0;
@@ -248,6 +270,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;
@@ -256,10 +282,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);
@@ -268,8 +307,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);
@@ -278,6 +337,8 @@ void my_syslog(int priority, const char *format, ...)
va_start(ap, format);
vsyslog(priority, format, ap);
va_end(ap);
#endif
return;
}
@@ -305,8 +366,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 */
@@ -314,10 +380,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,
@@ -376,14 +438,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);
}
}
@@ -394,11 +462,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-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,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,13 @@
# 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 +44,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 +66,76 @@ 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.
family = AF_LOCAL finds MAC addresses. */
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;
@@ -122,10 +146,16 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
again:
if (family == AF_UNSPEC)
req.nlh.nlmsg_type = RTM_GETNEIGH;
else if (family == AF_LOCAL)
req.nlh.nlmsg_type = RTM_GETLINK;
else
req.nlh.nlmsg_type = RTM_GETADDR;
again:
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = 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 +171,121 @@ 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 && family != AF_LOCAL)
{
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_prefixlen, 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;
}
#ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
{
struct ifinfomsg *link = NLMSG_DATA(h);
struct rtattr *rta = IFLA_RTA(link);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link));
char *mac = NULL;
size_t maclen = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFLA_ADDRESS)
{
maclen = rta->rta_len - sizeof(struct rtattr);
mac = (char *)(rta+1);
}
rta = RTA_NEXT(rta, len1);
}
if (mac && !((*callback)(link->ifi_type, link->ifi_flags, mac, maclen, parm)))
return 0;
}
#endif
}
}
@@ -212,6 +293,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 +308,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,10 +325,10 @@ 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)
if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
{
struct rtmsg *rtm = NLMSG_DATA(h);
int fd;
@@ -247,7 +338,7 @@ static void nl_routechange(struct nlmsghdr *h)
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save)
{
if (daemon->srv_save->sfd)

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,64 +10,131 @@
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)
{
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)
{
#ifdef HAVE_BSD_BRIDGE
/* 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(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))
if (daemon->if_names || 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)
if (addr && tmp->addr.sa.sa_family == family)
if (tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
@@ -82,37 +149,35 @@ 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;
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 +188,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 +219,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))
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))
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;
@@ -179,12 +269,13 @@ 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)
static int iface_allowed_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
union mysockaddr addr;
struct in_addr netmask; /* dummy */
(void)prefix; /* warning */
netmask.s_addr = 0;
memset(&addr, 0, sizeof(addr));
@@ -196,7 +287,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 +305,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,209 +330,209 @@ 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)
{
int family = addr->sa.sa_family;
int fd, rc, opt = 1;
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;
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;
#ifdef HAVE_IPV6
if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, 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, IPPROTO_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 if (!set_ipv6pktinfo(fd))
goto err;
#endif
}
return fd;
}
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd)
{
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);
/* 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
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) != -1)
return 1;
# ifdef IPV6_2292PKTINFO
else if (errno == ENOPROTOOPT && setsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
{
daemon->v6pktinfo = IPV6_2292PKTINFO;
return 1;
}
# endif
#else
if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &opt, sizeof(opt)) != -1)
return 1;
#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);
if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
return 0;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
!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;
return 1;
return 0;
}
#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);
for (iface = daemon->interfaces; iface; iface = iface->next)
{
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;
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);
#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);
}
#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);
}
#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;
}
if (l)
l->next = create_listeners(&addr, tftp_enabled, 1);
else
l = create_listeners(&addr, tftp_enabled, 1);
#endif
if (new)
listeners = new;
}
return listeners;
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)
@@ -519,8 +610,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
@@ -534,7 +625,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
/* when using random ports, servers which would otherwise use
the INADDR_ANY/port0 socket have sfd set to NULL */
if (!daemon->osport)
if (!daemon->osport && intname[0] == 0)
{
errno = 0;
@@ -614,13 +705,13 @@ void pre_allocate_sfds(void)
}
for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
!allocate_sfd(&srv->source_addr, srv->interface) &&
errno != 0 &&
(daemon->options & OPT_NOWILD))
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);
@@ -637,11 +728,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);
@@ -680,25 +775,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;
@@ -769,20 +869,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;
@@ -825,16 +943,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

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,13 +10,13 @@
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 int add_resource_record(HEADER *header, char *limit, int *truncp,
static int add_resource_record(struct dns_header *header, char *limit, int *truncp,
unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type,
unsigned short class, char *format, ...);
@@ -25,9 +25,9 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp,
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (int)((pp) += (len)), 1)
(!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
@@ -138,7 +138,8 @@ static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
for(j=0; j<l; j++, p++)
if (isExtract)
{
if (legal_char(*p))
unsigned char c = *p;
if (isascii(c) && !iscntrl(c) && c != '.')
*cp++ = *p;
else
return 0;
@@ -216,7 +217,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
as CNAME targets according to RFC 2317 */
char *cp;
for (cp = cp1; *cp; cp++)
if (!isdigit((int)*cp))
if (!isdigit((unsigned char)*cp))
return 0;
addr[3] = addr[2];
@@ -243,7 +244,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
if (*name == '\\' && *(name+1) == '[' &&
(*(name+2) == 'x' || *(name+2) == 'X'))
{
for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++)
{
char xdig[2];
xdig[0] = *cp1;
@@ -261,7 +262,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
{
for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
{
if (*(cp1+1) || !isxdigit((int)*cp1))
if (*(cp1+1) || !isxdigit((unsigned char)*cp1))
return 0;
for (j = sizeof(struct all_addr)-1; j>0; j--)
@@ -277,7 +278,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
return 0;
}
static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
{
while(1)
{
@@ -332,7 +333,7 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
return ansp;
}
static unsigned char *skip_questions(HEADER *header, size_t plen)
static unsigned char *skip_questions(struct dns_header *header, size_t plen)
{
int q;
unsigned char *ansp = (unsigned char *)(header+1);
@@ -347,7 +348,7 @@ static unsigned char *skip_questions(HEADER *header, size_t plen)
return ansp;
}
static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, size_t plen)
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
{
int i, rdlen;
@@ -370,7 +371,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. Return all-ones
if there is not question section. */
unsigned int questions_crc(HEADER *header, size_t plen, char *name)
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
{
int q;
unsigned int crc = 0xffffffff;
@@ -412,7 +413,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
}
size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t hlen)
size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
{
unsigned char *ansp = skip_questions(header, plen);
@@ -436,7 +437,7 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
return ansp - (unsigned char *)header;
}
unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
@@ -452,7 +453,7 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
{
*is_sign = 0;
if (header->opcode == QUERY)
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
@@ -509,58 +510,208 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
return ret;
}
struct macparm {
unsigned char *limit;
struct dns_header *header;
size_t plen;
union mysockaddr *l3;
};
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct macparm *parm = parmv;
int match = 0;
unsigned short rdlen;
struct dns_header *header = parm->header;
unsigned char *lenp, *datap, *p;
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
if (!match)
return 1; /* continue */
if (ntohs(header->arcount) == 0)
{
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, parm->plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount),
header, parm->plen)))
return 0;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */
PUTLONG(0, p); /* extended RCODE */
lenp = p;
PUTSHORT(0, p); /* RDLEN */
rdlen = 0;
if (((ssize_t)maclen) > (parm->limit - (p + 4)))
return 0; /* Too big */
header->arcount = htons(1);
datap = p;
}
else
{
int i, is_sign;
unsigned short code, len;
if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||
is_sign ||
(!(p = skip_name(p, header, parm->plen, 10))))
return 0;
p += 8; /* skip UDP length and RCODE */
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, parm->plen, rdlen))
return 0; /* bad packet */
datap = p;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_MAC)
return 0;
p += len;
}
if (((ssize_t)maclen) > (parm->limit - (p + 4)))
return 0; /* Too big */
}
PUTSHORT(EDNS0_OPTION_MAC, p);
PUTSHORT(maclen, p);
memcpy(p, mac, maclen);
p += maclen;
PUTSHORT(p - datap, lenp);
parm->plen = p - (unsigned char *)header;
return 0; /* done */
}
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
{
struct macparm parm;
/* Must have an existing pseudoheader as the only ar-record,
or have no ar-records. Must also not be signed */
if (ntohs(header->arcount) > 1)
return plen;
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
parm.l3 = l3;
iface_enumerate(AF_UNSPEC, &parm, filter_mac);
return parm.plen;
}
/* is addr in the non-globally-routed IP space? */
static int private_net(struct in_addr addr)
static int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
{
int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--)
{
if (!(p = skip_name(p, header, qlen, 10)))
if (name && option_bool(OPT_LOG))
{
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0;
}
else if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
p += 4; /* ttl */
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A))
if (qclass == C_IN && qtype == T_A)
{
struct doctor *doctor;
struct in_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
/* alignment */
/* alignment */
memcpy(&addr, p, INADDRSZ);
for (doctor = daemon->doctors; doctor; doctor = doctor->next)
if (is_same_net(doctor->in, addr, doctor->mask))
{
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
memcpy(p, &addr, INADDRSZ);
break;
}
{
if (doctor->end.s_addr == 0)
{
if (!is_same_net(doctor->in, addr, doctor->mask))
continue;
}
else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
continue;
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->hb3 &= ~HB3_AA;
memcpy(p, &addr, INADDRSZ);
break;
}
}
else if (qtype == T_TXT && name && option_bool(OPT_LOG))
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, rdlen))
return 0;
while ((p1 - p) < rdlen)
{
unsigned int i, len = *p1;
unsigned char *p2 = p1;
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
if (isprint(*(p2+1)))
{
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, len);
*p1 = len;
p1 += len+1;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
@@ -569,7 +720,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
return p;
}
static int find_soa(HEADER *header, size_t qlen)
static int find_soa(struct dns_header *header, size_t qlen, char *name)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -578,7 +729,7 @@ static int find_soa(HEADER *header, size_t qlen)
/* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--)
@@ -614,7 +765,7 @@ static int find_soa(HEADER *header, size_t qlen)
}
/* rewrite addresses in additioal section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen))
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
return 0;
if (!found_soa)
@@ -626,8 +777,9 @@ static int find_soa(HEADER *header, size_t qlen)
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
int is_sign, int check_rebind, int checking_disabled)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -636,11 +788,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
cache_start_insert();
/* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
if (daemon->doctors)
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || option_bool(OPT_LOG))
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, name);
}
/* go through the questions. */
@@ -650,7 +802,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
{
int found = 0, cname_count = 5;
struct crec *cpp = NULL;
int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
unsigned long cttl = ULONG_MAX, attl;
namep = p;
@@ -689,6 +841,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -718,12 +875,12 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
}
}
if (!found && !(daemon->options & OPT_NO_NEG))
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
@@ -764,6 +921,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -798,9 +960,9 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if ((daemon->options & OPT_NO_REBIND) &&
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4))
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
@@ -819,12 +981,12 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
}
}
if (!found && !(daemon->options & OPT_NO_NEG))
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
@@ -841,18 +1003,19 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
}
}
/* Don't put stuff from a truncated packet into the cache, but do everything else */
if (!header->tc)
/* Don't put stuff from a truncated packet into the cache,
also don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it. */
if (!(header->hb3 & HB3_TC) && !(header->hb4 & HB4_CD) && !checking_disabled)
cache_end_insert();
return 0;
}
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name.
Abuse F_BIGNAME to indicate an NS query - yuck. */
return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass;
@@ -860,7 +1023,7 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
if (typep)
*typep = 0;
if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
return 0; /* must be exactly one query. */
if (!extract_name(header, qlen, &p, name, 1, 4))
@@ -880,50 +1043,49 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
return F_IPV6;
if (qtype == T_ANY)
return F_IPV4 | F_IPV6;
if (qtype == T_NS || qtype == T_SOA)
return F_QUERY | F_BIGNAME;
}
return F_QUERY;
}
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags, unsigned long ttl)
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{
unsigned char *p = skip_questions(header, qlen);
header->qr = 1; /* response */
header->aa = 0; /* authoritive */
header->ra = 1; /* recursion if available */
header->tc = 0; /* not truncated */
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
/* set RA flag */
header->hb4 |= HB4_RA;
header->nscount = htons(0);
header->arcount = htons(0);
header->ancount = htons(0); /* no answers unless changed below */
if (flags == F_NEG)
header->rcode = SERVFAIL; /* couldn't get memory */
SET_RCODE(header, SERVFAIL); /* couldn't get memory */
else if (flags == F_NOERR)
header->rcode = NOERROR; /* empty domain */
SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN)
header->rcode = NXDOMAIN;
SET_RCODE(header, NXDOMAIN);
else if (p && flags == F_IPV4)
{ /* we know the address */
header->rcode = NOERROR;
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (p && flags == F_IPV6)
{
header->rcode = NOERROR;
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
}
#endif
else /* nowhere to forward to */
header->rcode = REFUSED;
SET_RCODE(header, REFUSED);
return p - (unsigned char *)header;
}
@@ -936,12 +1098,17 @@ int check_for_local_domain(char *name, time_t now)
struct txt_record *txt;
struct interface_name *intr;
struct ptr_record *ptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
struct naptr *naptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
for (naptr = daemon->naptr; naptr; naptr = naptr->next)
if (hostname_isequal(name, naptr->name))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name))
return 1;
@@ -963,7 +1130,7 @@ int check_for_local_domain(char *name, time_t now)
/* Is the packet a reply with the answer address equal to addr?
If so mung is into an NXDOMAIN reply and also put that information
in the cache. */
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 *baddr, time_t now)
{
unsigned char *p;
@@ -1010,7 +1177,7 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
return 0;
}
static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
@@ -1111,12 +1278,16 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
if (crecp->flags & (F_IMMORTAL | F_DHCP))
return daemon->local_ttl;
return crecp->ttd - now;
/* Return the Max TTL value if it is lower then the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
else
return daemon->max_ttl;
}
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(HEADER *header, char *limit, size_t qlen,
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)
{
char *name = daemon->namebuff;
@@ -1140,11 +1311,11 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, ext_rcode, flags;
unsigned short udpsz, flags;
unsigned char *psave = pheader;
GETSHORT(udpsz, pheader);
GETSHORT(ext_rcode, pheader);
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
@@ -1159,7 +1330,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
dryrun = 1;
}
if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -1197,7 +1368,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_TXT, t->class, "t", t->len, t->txt))
@@ -1248,7 +1419,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name) &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1282,7 +1453,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
record_source(daemon->addn_hosts, crecp->uid));
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL,
@@ -1292,8 +1463,8 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
else if (is_arpa == F_IPV4 &&
(daemon->options & OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4))
option_bool(OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4, 1))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
@@ -1318,18 +1489,41 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype != type && qtype != T_ANY)
continue;
/* Check for "A for A" queries */
if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
/* Check for "A for A" queries; be rather conservative
about what looks like dotted-quad. */
if (qtype == T_A)
{
ans = 1;
if (!dryrun)
char *cp;
unsigned int i, a;
int x;
for (cp = name, i = 0, a = 0; *cp; i++)
{
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
{
i = 5;
break;
}
a = (a << 8) + x;
if (*cp == '.')
cp++;
}
if (i == 4)
{
ans = 1;
if (!dryrun)
{
addr.addr.addr4.s_addr = htonl(a);
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL, type, C_IN, "4", &addr))
anscount++;
}
continue;
}
continue;
}
/* interface name stuff */
@@ -1367,7 +1561,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
/* See if a putative address is on the network from which we recieved
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
{
struct crec *save = crecp;
do {
@@ -1392,7 +1586,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
{
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(daemon->addn_hosts, crecp->uid));
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
@@ -1428,7 +1622,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
record_source(daemon->addn_hosts, crecp->uid));
record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), NULL, type, C_IN,
@@ -1450,7 +1644,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
{
@@ -1461,16 +1655,16 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
}
if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
{
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_MX, C_IN, "sd", 1,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
anscount++;
}
}
@@ -1479,7 +1673,8 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype == T_SRV || qtype == T_ANY)
{
int found = 0;
struct mx_srv_record *move = NULL, **up = &daemon->mxnames;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name))
{
@@ -1487,7 +1682,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
@@ -1497,9 +1692,27 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
rec->offset = offset;
}
}
/* unlink first SRV record found */
if (!move)
{
move = rec;
*up = rec->next;
}
else
up = &rec->next;
}
else
up = &rec->next;
/* put first SRV record back at the end. */
if (move)
{
*up = move;
move->next = NULL;
}
if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
{
ans = 1;
if (!dryrun)
@@ -1516,7 +1729,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
NULL, T_NAPTR, C_IN, "sszzzd",
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
@@ -1528,7 +1741,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype == T_MAILB)
ans = 1, nxdomain = 1;
if (qtype == T_SOA && (daemon->options & OPT_FILTER))
if (qtype == T_SOA && option_bool(OPT_FILTER))
{
ans = 1;
if (!dryrun)
@@ -1575,14 +1788,23 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
/* done all questions, set up header and return length of result */
header->qr = 1; /* response */
header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
header->ra = 1; /* recursion if available */
header->tc = trunc; /* truncation */
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
/* set RA flag */
header->hb4 |= HB4_RA;
/* authoritive - only hosts and DHCP derived names. */
if (auth)
header->hb3 |= HB3_AA;
/* truncation */
if (trunc)
header->hb3 |= HB3_TC;
if (anscount == 0 && nxdomain)
header->rcode = NXDOMAIN;
SET_RCODE(header, NXDOMAIN);
else
header->rcode = NOERROR; /* no error */
SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(addncount);

File diff suppressed because it is too large Load Diff

298
src/rfc3315.c Normal file
View File

@@ -0,0 +1,298 @@
/* 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_DHCP6
static size_t outpacket_counter;
static int make_duid1(unsigned short type, unsigned int flags, char *mac,
size_t maclen, void *parm);
void make_duid(time_t now)
{
iface_enumerate(AF_LOCAL, &now, make_duid1);
if (!daemon->duid)
die("Cannot create DHCPv6 server DUID", NULL, EC_MISC);
}
static int make_duid1(unsigned short type, unsigned int flags, char *mac,
size_t maclen, void *parm)
{
/* create DUID as specified in RFC3315. We use the MAC of the
first interface we find that isn't loopback or P-to-P */
unsigned char *p;
if (flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
return 1;
daemon->duid = p = safe_malloc(maclen + 8);
daemon->duid_len = maclen + 8;
PUTSHORT(1, p); /* DUID_LLT */
PUTSHORT(type, p); /* address type */
PUTLONG(*((time_t *)parm), p); /* time */
memcpy(p, mac, maclen);
return 0;
}
void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize)
{
u16 opt, opt_len;
void *start;
if (!opts)
return NULL;
while (1)
{
if (end - opts < 4)
return NULL;
start = opts;
GETSHORT(opt, opts);
GETSHORT(opt_len, opts);
if (opt_len > (end - opts))
return NULL;
if (opt == search && (opt_len >= minsize))
return start;
opts += opt_len;
}
}
void *opt6_next(void *opts, void *end)
{
u16 opt_len;
if (end - opts < 4)
return NULL;
opts += 2;
GETSHORT(opt_len, opts);
if (opt_len >= (end - opts))
return NULL;
return opts + opt_len;
}
#define opt6_len(opt) ((int)(((unsigned short *)(opt))[1]))
#define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4u+(unsigned int)(i)]))
static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
{
/* this worries about unaligned data and byte order */
unsigned int ret = 0;
int i;
unsigned char *p = opt6_ptr(opt, offset);
for (i = 0; i < size; i++)
ret = (ret << 8) | *p++;
return ret;
}
/*
daemon->outpacket_counter = 4; message type and ID
elapsed time:
int o = new_opt(OPTION_ELAPSED_TIME);
put_opt_short(o, 100)
finalise_opt(o);
IA_NA
int o = new_opt(OPTION_IA_NA);
put_opt_long(o, IAID);
put_opt_long(o, T1);
put_opt_long(0, T2);
int o1 = new_opt(OPTION_IAADDR);
put_opt(o1, &addr, sizeof(addr));
put_opt_long(o1, preferred_lifetime);
put_opt_long(o1, valid_lifetime);
finalise_opt(o1);
finalise_opt(o);
*/
void end_opt6(int container)
{
void *p = daemon->outpacket.iov_base + container + 2;
u16 len = outpacket_counter - container - 4 ;
PUTSHORT(len, p);
}
static void *expand(size_t headroom)
{
void *ret;
if (expand_buf(&daemon->outpacket, outpacket_counter + headroom))
{
ret = daemon->outpacket.iov_base + outpacket_counter;
outpacket_counter += headroom;
return ret;
}
return NULL;
}
int new_opt6(int opt)
{
int ret = outpacket_counter;
void *p;
if ((p = expand(4)))
{
PUTSHORT(opt, p);
PUTSHORT(0, p);
}
return ret;
}
void *put_opt6(void *data, size_t len)
{
void *p;
if (data && (p = expand(len)))
memcpy(p, data, len);
return p;
}
void put_opt6_long(unsigned int val)
{
void *p;
if (( p = expand(4)))
PUTLONG(p, val);
}
void put_opt6_short(unsigned int val)
{
void *p;
if ((p = expand(2)))
PUTSHORT(val, p);
}
void put_opt6_byte(unsigned int val)
{
void *p;
if ((p = expand(1)))
*((unsigned char *)p) = val;
}
size_t dhcp6_reply(struct dhcp_context *context, size_t sz)
{
void *packet_options = ((void *)daemon->dhcp_packet.iov_base) + 4;
void *end = ((void *)daemon->dhcp_packet.iov_base) + sz;
void *na_option, *na_end;
void *opt, *p;
int o;
outpacket_counter = 4; /* skip message type and transaction-id */
if (!(opt = opt6_find(packet_options, end, OPTION6_CLIENT_ID, 1)))
return;
o = new_opt6(OPTION6_CLIENT_ID);
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
end_opt6(o);
o = new_opt6(OPTION6_SERVER_ID);
put_opt6(daemon->duid, daemon->duid_len);
end_opt6(o);
if ((opt = opt6_find(packet_options, end, OPTION6_IA_NA, 12)))
{
while (opt = opt6_find(opt, end, OPTION6_IA_NA, 12))
{
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_option = opt6_find(opt6_ptr(opt, 12), ia_end, OPTION6_IAADDR, 24);
unsigned int iaid = opt6_uint(opt, 0, 4);
unsigned int t1 = opt6_uint(opt, 4, 4);
unsigned int t2 = opt6_uint(opt, 8, 4);
if (ia_option)
while ((ia_option = ia_option, ia_end, OPTION6_IAADDR, 24))
{
/* do address option */
ia_option = opt6_next(ia_option, ia_end);
}
else
{
/* no preferred address call address allocate */
}
opt = opt6_next(opt, end);
}
}
else if ((opt = opt6_find(packet_options, end, OPTION6_IA_TA, 4)))
while (opt = opt6_find(opt, end, OPTION6_IA_TA, 4))
{
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_option = opt6_find(opt6_ptr(opt, 4), ia_end, OPTION6_IAADDR, 24);
unsigned int iaid = opt6_uint(opt, 0, 4);
if (ia_option)
while ((ia_option = ia_option, ia_end, OPTION6_IAADDR, 24))
{
/* do address option */
ia_option = opt6_next(ia_option, ia_end);
}
else
{
/* no preferred address */
}
opt = opt6_next(opt, end);
}
else
return; /* no IA_NA and no IA_TA */
}
#endif

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,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,35 @@ 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 *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 +93,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 == IPPROTO_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 == IPPROTO_IPV6 && 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);
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);
/* 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;
@@ -155,13 +258,15 @@ void tftp_request(struct listener *listen, time_t now)
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
transfer->netascii = transfer->carrylf = 0;
prettyprint_addr(&peer, daemon->addrbuff);
/* 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, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
!fix_fd(transfer->sockfd))
{
@@ -169,10 +274,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;
@@ -187,7 +297,7 @@ void tftp_request(struct listener *listen, time_t now)
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
else
{
if (strcasecmp(mode, "netascii") == 0)
@@ -195,42 +305,50 @@ void tftp_request(struct listener *listen, time_t now)
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) && !transfer->netascii)
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, daemon->addrbuff, (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))
@@ -248,11 +366,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);
@@ -268,13 +385,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;
@@ -284,7 +400,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)
@@ -311,7 +427,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
@@ -375,6 +491,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
@@ -391,20 +509,22 @@ void check_tftp_listeners(fd_set *rset, time_t now)
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));
daemon->addrbuff);
/* Got err, ensure we take abort */
transfer->timeout = now;
@@ -433,9 +553,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, daemon->addrbuff);
len = 0;
endcon = 1;
}
}
if (len != 0)
@@ -444,6 +567,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, daemon->addrbuff);
/* unlink */
*up = tmp;
free_transfer(transfer);
@@ -452,7 +577,7 @@ void check_tftp_listeners(fd_set *rset, time_t now)
}
up = &transfer->next;
}
}
}
static void free_transfer(struct tftp_transfer *transfer)
@@ -492,8 +617,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;
}
@@ -564,15 +688,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
{
if (size == transfer->blocksize)
{
transfer->expansion++;
if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
}
else
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';

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
@@ -9,15 +9,13 @@
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 Berstein, which is public domain. */
Daniel J Bernstein, which is public domain. */
#include "dnsmasq.h"
@@ -26,6 +24,9 @@
#include <sys/times.h>
#endif
#if defined(LOCALEDIR) || defined(HAVE_IDN)
#include <idna.h>
#endif
#ifdef HAVE_ARC4RANDOM
void rand_init(void)
@@ -42,11 +43,9 @@ unsigned short rand16(void)
/* SURF random number generator */
typedef unsigned int uint32;
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
static u32 seed[32];
static u32 in[12];
static u32 out[8];
void rand_init()
{
@@ -65,7 +64,7 @@ void rand_init()
static void surf(void)
{
uint32 t[12]; uint32 x; uint32 sum = 0;
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];
@@ -97,48 +96,110 @@ unsigned short rand16(void)
#endif
int legal_char(char c)
static int check_name(char *in)
{
/* 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;
}
int canonicalise(char *s)
{
/* 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)
@@ -259,6 +320,25 @@ 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);
}
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{
int pfbytes = prefixlen >> 3;
int pfbits = prefixlen & 7;
if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
return 0;
if (pfbits == 0 ||
(a->s6_addr[pfbytes] >> (8 - pfbits) != b->s6_addr[pfbytes] >> (8 - pfbits)))
return 1;
return 0;
}
#endif
/* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf)
{
@@ -272,7 +352,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
@@ -302,7 +390,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)
{
@@ -314,7 +403,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;
@@ -346,14 +438,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 */
@@ -405,7 +502,7 @@ void bump_maxfd(int fd, int *max)
int retry_send(void)
{
struct timespec waiter;
if (errno == EAGAIN)
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;