Compare commits

...

24 Commits
v2.16 ... v2.40

Author SHA1 Message Date
Simon Kelley
5aabfc78bc import of dnsmasq-2.40.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
f2621c7ff0 import of dnsmasq-2.39.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
6b01084f8e import of dnsmasq-2.38.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
1b7ecd111d import of dnsmasq-2.37.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
832af0bafb import of dnsmasq-2.36.tar.gz 2012-01-05 17:31:13 +00:00
Simon Kelley
4011c4e05e import of dnsmasq-2.35.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
1697269ce7 import of dnsmasq-2.34.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
208b65c5cf import of dnsmasq-2.33.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
849a8357ba import of dnsmasq-2.32.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
7cebd20fe7 import of dnsmasq-2.31.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
26d0dbaf24 import of dnsmasq-2.30.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
309331f52c import of dnsmasq-2.29.tar.gz 2012-01-05 17:31:12 +00:00
Simon Kelley
5e9e0efb01 import of dnsmasq-2.28.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
cdeda28f82 import of dnsmasq-2.27.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
aedef83058 import of dnsmasq-2.26.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
e17fb629a2 import of dnsmasq-2.25.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
b8187c80a8 import of dnsmasq-2.24.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
3d8df260e1 import of dnsmasq-2.23.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
91dccd0958 import of dnsmasq-2.22.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
0a852541d3 import of dnsmasq-2.21.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
f6b7dc47c7 import of dnsmasq-2.20.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
bb01cb9604 import of dnsmasq-2.19.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
59353a6b56 import of dnsmasq-2.18.tar.gz 2012-01-05 17:31:11 +00:00
Simon Kelley
26128d2747 import of dnsmasq-2.17.tar.gz 2012-01-05 17:31:11 +00:00
77 changed files with 28863 additions and 5537 deletions

1092
CHANGELOG

File diff suppressed because it is too large Load Diff

189
FAQ
View File

@@ -21,8 +21,7 @@ Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
that?
A: Update: from version 2.10, it does. There are a few limitations:
data obtained via TCP is not cached, and dynamically-created
interfaces may break under certain circumstances. Source-address
data obtained via TCP is not cached, and source-address
or query-port specifications are ignored for TCP.
Q: When I send SIGUSR1 to dump the contents of the cache, some entries have
@@ -40,18 +39,17 @@ A: They are negative entries: that's what the N flag means. Dnsmasq asked
Q: Will dnsmasq compile/run on non-Linux systems?
A: Yes, there is explicit support for *BSD and Solaris.
A: Yes, there is explicit support for *BSD and MacOS X. There are
start-up scripts for MacOS X Tiger and Panther in /contrib. Earlier
dnsmasq releases ran under Solaris, but that capability has
rotted. Dnsmasq will link with uclibc to provide small
binaries suitable for use in embedded systems such as
routers. (There's special code to support machines with flash
filesystems and no battery-backed RTC.)
If you encounter make errors with *BSD, try installing gmake from
ports and building dnsmasq with "make MAKE=gmake"
For other systems, try altering the settings in config.h.
A: Update for V2. Doing DHCP is rather non-portable, so there may be
a few teething troubles. The initial 2.0 release is known to work
on Linux 2.2.x, Linux 2.4.x and Linux 2.6.x with uclibc and glibc
2.3. It also works on FreeBSD 4.8. The crucial problem is sending
raw packets, bypassing the IP stack. Dnsmasq contains code to do
using PF_PACKET sockets (which is for Linux) and the Berkeley packet
filter (which works with BSD). If you are trying to port to another
Un*x, bpf is the most likeley candidate. See config.h
Q: My companies' nameserver knows about some names which aren't in the
public DNS. Even though I put it first in /etc/resolv.conf, it
dosen't work: dnsmasq seems not to use the nameservers in the order
@@ -89,7 +87,7 @@ A: This has been seen when a system is bringing up a PPP interface at
Q: I'm running on BSD and dnsmasq won't accept long options on the
command line.
A: Dnsmasq when built on BSD systems doesn't use GNU getopt by
A: Dnsmasq when built on some BSD systems doesn't use GNU getopt by
default. You can either just use the single-letter options or
change config.h and the Makefile to use getopt-long. Note that
options in /etc/dnsmasq.conf must always be the long form,
@@ -106,16 +104,26 @@ A: Resolver code sometime does strange things when given names without
"ping" will get a lookup failure, appending a dot to the end of the
hostname will fix things. (ie "ping myhost" fails, but "ping
myhost." works. The solution is to make sure that all your hosts
have a domain set ("domain" in resolv.conf, the network applet in
windows, or set a domain in your DHCP server). Any domain will do,
but "localnet" is traditional. Now when you resolve "myhost" the
resolver will attempt to look up "myhost.localnet" so you need to
have dnsmasq reply to that name. The way to do that is to include
the domain in each name on /etc/hosts and/or to use the
--expand-hosts and --domain-suffix options.
have a domain set ("domain" in resolv.conf, or set a domain in
your DHCP server, see below fr Windows XP and Mac OS X).
Any domain will do, but "localnet" is traditional. Now when you
resolve "myhost" the resolver will attempt to look up
"myhost.localnet" so you need to have dnsmasq reply to that name.
The way to do that is to include the domain in each name on
/etc/hosts and/or to use the --expand-hosts and --domain options.
Q: How do I set the DNS domain in Windows XP or MacOS X (ref: previous
question)?
A: for XP, Control Panel > Network Connections > { Connection to gateway /
DNS } > Properties > { Highlight TCP/IP } > Properties > Advanced >
DNS Tab > DNS suffix for this connection:
A: for OS X, System Preferences > Network > {Connection to gateway / DNS } >
Search domains:
Q: Can I get dnsmasq to save the contents of its cache to disk when
I shut my machine down and re-load when it starts again.
I shut my machine down and re-load when it starts again?
A: No, that facility is not provided. Very few names in the DNS have
their time-to-live set for longer than a few hours so most of the
@@ -282,7 +290,9 @@ Q: Can I get email notification when a new version of dnsmasq is
A: Yes, new releases of dnsmasq are always announced through
freshmeat.net, and they allow you to subcribe to email alerts when
new versions of particular projects are released.
new versions of particular projects are released. New releases are
also announced in the dnsmasq-discuss mailing list, subscribe at
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss
Q: What does the dhcp-authoritative option do?
@@ -299,7 +309,140 @@ A: Because when a Gentoo box shuts down, it releases its lease with
dnsmasq ignores it until is times out and restarts the process.
To fix this, set the dhcp-authoritative flag in dnsmasq.
Q: My laptop has two network interfaces, a wired one and a wireless
one. I never use both interfaces at the same time, and I'd like the
same IP and configuration to be used irrespective of which
interface is in use. How can I do that?
A: By default, the identity of a machine is determined by using the
MAC address, which is associated with interface hardware. Once an
IP is bound to the MAC address of one interface, it cannot be
associated with another MAC address until after the DHCP lease
expires. The solution to this is to use a client-id as the machine
identity rather than the MAC address. If you arrange for the same
client-id to sent when either interface is in use, the DHCP server
will recognise the same machine, and use the same address. The
method for setting the client-id varies with DHCP client software,
dhcpcd uses the "-I" flag. Windows uses a registry setting,
see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
Q: Can dnsmasq do DHCP on IP-alias interfaces?
A: Yes, from version-2.21. The support is only available running under
Linux, on a kernel which provides the RT-netlink facility. All 2.4
and 2.6 kernels provide RT-netlink and it's an option in 2.2
kernels.
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
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
have one of the address-ranges as static-only, and have known
hosts allocated addresses on that subnet using dhcp-host options,
while anonymous hosts go on the other.
Q: Dnsmasq sometimes logs "nameserver xxx.xxx.xxx.xxx refused
to do a recursive query" and DNS stops working. What's going on?
A: Probably the nameserver is an authoritative nameserver for a
particular domain, but is not configured to answer general DNS
queries for an arbitrary domain. It is not suitable for use by
dnsmasq as an upstream server and should be removed from the
configuration. Note that if you have more than one upstream
nameserver configured dnsmasq will load-balance across them and
it may be some time before dnsmasq gets around to using a
particular nameserver. This means that a particular configuration
may work for sometime with a broken upstream nameserver
configuration.
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
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
seconds. Because the DHCP server is not re-entrant, it cannot serve
other DHCP requests during this time. To avoid dropping requests,
the address probe may be skipped when dnsmasq is under heavy load.
Q: I'm using dnsmasq on a machine with the Firestarter firewall, and
DHCP doesn't work. What's the problem?
A: This a variant on the iptables problem. Explicit details on how to
proceed can be found at
http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2005q3/000431.html
Q: Dnsmasq logs "running as root because setting capabilities failed"
when it starts up. Why did that happen and what can do to fix it?
A: Change your kernel configuration: either deselect CONFIG_SECURITY
_or_ select CONFIG_SECURITY_CAPABILITIES.
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: Can I run dnsmasq in a Linux vserver?
A: Yes, as a DNS server, dnsmasq will just work in a vserver.
To use dnsmasq's DHCP function you need to give the vserver
extra system capabilities. Please note that doing so will lesser
the overall security of your system. The capabilities
required are NET_ADMIN and NET_RAW. NET_ADMIN is essential, NET_RAW
is required to do an ICMP "ping" check on newly allocated
addresses. If you don't need this check, you can disable it with
--no-ping and omit the NET_RAW capability.
Adding the capabilities is done by adding them, one per line, to
either /etc/vservers/<vservername>/ccapabilities for a 2.4 kernel or
/etc/vservers/<vservername>/bcapabilities for a 2.6 kernel (please
refer to the vserver documentation for more information).
Q: What's the problem with syslog and dnsmasq?
A: In almost all cases: none. If you have the normal arrangement with
local daemons logging to a local syslog, which then writes to disk,
then there's never a problem. If you use network logging, then
there's a potential problem with deadlock: the syslog daemon will
do DNS lookups so that it can log the source of log messages,
these lookups will (depending on exact configuration) go through
dnsmasq, which also sends log messages. With bad timing, you can
arrive at a situation where syslog is waiting for dnsmasq, and
dnsmasq is waiting for syslog; they will both wait forever. This
problem is fixed from dnsmasq-2.39, which introduces asynchronous
logging: dnsmasq no longer waits for syslog and the deadlock is
broken. There is a remaining problem in 2.39, where "log-queries"
is in use. In this case most DNS queries generate two log lines, if
these go to a syslog which is doing a DNS lookup for each log line,
then those queries will in turn generate two more log lines, and a
chain reaction runaway will occur. To avoid this, use syslog-ng
and turn on syslog-ng's dns-cache function.

View File

@@ -1,22 +1,44 @@
PREFIX?=/usr/local
BINDIR = ${PREFIX}/sbin
MANDIR = ${PREFIX}/man
PREFIX ?= /usr/local
BINDIR ?= ${PREFIX}/sbin
MANDIR ?= ${PREFIX}/share/man
LOCALEDIR ?= ${PREFIX}/share/locale
SRC = src
PO = po
MAN = man
CFLAGS?= -O2
all :
@cd $(SRC); $(MAKE) dnsmasq
all : dnsmasq
dnsmasq :
$(MAKE) I18N=-DNO_GETTEXT -f ../bld/Makefile -C $(SRC) dnsmasq
clean :
rm -f *~ contrib/*/*~ */*~ $(SRC)/*.o $(SRC)/dnsmasq core build
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
rm -f $(SRC)/*.o $(SRC)/dnsmasq.a $(SRC)/dnsmasq core */core
install : all
install : all install-common
install-common :
install -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
install -m 644 dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
install -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
install -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
all-i18n :
$(MAKE) I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' -f ../bld/Makefile -C $(SRC) dnsmasq
cd $(PO); for f in *.po; do \
$(MAKE) -f ../bld/Makefile -C ../$(SRC) $${f%.po}.mo; \
done
install-i18n : all-i18n install-common
cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR)
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR)
merge :
$(MAKE) I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' -f ../bld/Makefile -C $(SRC) dnsmasq.pot
cd $(PO); for f in *.po; do \
msgmerge -U $$f ../$(SRC)/dnsmasq.pot; \
done

View File

@@ -1,68 +0,0 @@
Upgrading to dnsmasq V2
-----------------------
Version 1.x of dnsmasq includes a facility for reading the dhcp.leases
file written by ISC dhcpd. This allows the names of machines which
have addresses allocated by DHCP to be included in the DNS.
Version 2.x of dnsmasq replaces the ISC dhcpd integration with a DHCP
server integrated into dnsmasq. Versions 2.0-2.5 removed the ISC
integration completely, but in version 2.6 it was re-enabled for
backwards compatibility purposes. The change to an integrated DHCP
server has the following advantages:
* Small. ISC dhcpd is a large and comprehensive DHCP solution. The
dnsmasq DHCP server adds about 15k to DNS-only dnsmasq and provides
all the facilities likely to be needed in the sort of networks
which are targeted by dnsmasq.
* Easy to configure. All configuration is in one file and there are
sensible defaults for common settings. Many applications will need
just one extra line in /etc/dnsmasq.conf which tells it the range of
addresses to allocate to DHCP.
* Support for static leases. When static leases are used with ISC DHCP
they don't appear in the dhcp.leases file (since that file is used
for storage of dynamic leases which aren't pre-configured.) Hence
static leases cannot be used with dnsmasq unless each machine with a
static lease is also inserted into /etc/hosts. This is not required
with the dnsmasq DHCP server.
DHCP configuration
------------------
To convert an installation which is currently using ISC dhcpd, remove
the ISC DHCP daemon. Unless you want dnsmasq to use the same file
to store its leases it is necessary to remove the configuration line in
/etc/dnsmasq.conf which specifies the dhcp.leases file.
To enable DHCP, simply add a line like this to /etc/dnsmasq.conf
dhcp-range=192.168.0.100,192.168.0.200,12h
which tells dnsmasq to us the addresses 192.168.0.100 to 192.168.0.200
for dynamic IP addresses, and to issue twelve hour leases.
Each host will have its default route and DNS server set to be the
address of the host running dnsmasq, and its netmask and broadcast
address set correctly, so nothing else at all is required for a
minimal system. Hosts which include a hostname in their DHCP request
will have that name and their allocated address inserted into the DNS,
in the same way as before.
Having started dnsmasq, tell any hosts on the network to renew their
DHCP lease, so that dnsmasq's DHCP server becomes aware of them. For
Linux, this is best done by killing-and-restarting the DHCP client
daemon or taking the network interface down and then back up. For
Windows 9x/Me, use the graphical tool "winipcfg". For Windows
NT/2000/XP, use the command-line "ipconfig /renew"
For more complex DHCP configuration, refer to the doc/setup.html, the
dnsmasq manpage and the annotated example configuration file. Also
note that for some ISC dhcpd to dnsmasq DHCP upgrades there may be
firewall issues: see the FAQ for details of this.

19
bld/Makefile Normal file
View File

@@ -0,0 +1,19 @@
CFLAGS ?= -O2
PKG_CONFIG ?= pkg-config
OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(I18N) `echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --cflags dbus-1` $(RPM_OPT_FLAGS) -Wall -W -c $<
dnsmasq : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) `echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --libs dbus-1` $(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 -

9
bld/install-man Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
for f in *; do
if [ -d $f ]; then
install -d $1/$f/man8
install -m 644 $f/dnsmasq.8 $1/$f/man8
echo installing $1/$f/man8/dnsmasq.8
fi
done

9
bld/install-mo Executable file
View File

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

7
bld/pkg-wrapper Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
if grep -q "^\#.*define.*HAVE_DBUS" config.h || grep -q HAVE_DBUS ; then
exec $*
fi

6
contrib/Suse/README Normal file
View File

@@ -0,0 +1,6 @@
This packaging is now unmaintained in the dnsmasq source: dnsmasq is
included in Suse proper, and up-to-date packages are now available
from
ftp://ftp.suse.com/pub/people/ug/

View File

@@ -0,0 +1,23 @@
--- man/dnsmasq.8 2004-08-08 20:57:56.000000000 +0200
+++ man/dnsmasq.8 2004-08-12 00:40:01.000000000 +0200
@@ -69,7 +69,7 @@
.TP
.B \-g, --group=<groupname>
Specify the group which dnsmasq will run
-as. The defaults to "dip", if available, to facilitate access to
+as. The defaults to "dialout", if available, to facilitate access to
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
-#define CHGRP "dip"
+#define CHGRP "dialout"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68

View File

@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.16
Version: 2.33
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
@@ -43,7 +43,7 @@ patch -p0 <rpm/%{name}-SuSE.patch
%build
%{?suse_update_config:%{suse_update_config -f}}
make
make all-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
###############################################################################
#
@@ -54,15 +54,11 @@ make
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p ${RPM_BUILD_ROOT}/etc/init.d
mkdir -p ${RPM_BUILD_ROOT}/usr/sbin
mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man8
make install-i18n DESTDIR=$RPM_BUILD_ROOT PREFIX=/usr
install -o root -g root -m 755 rpm/rc.dnsmasq-suse $RPM_BUILD_ROOT/etc/init.d/dnsmasq
install -o root -g root -m 644 dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
strip src/dnsmasq
install -o root -g root -m 755 src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
strip $RPM_BUILD_ROOT/usr/sbin/dnsmasq
ln -sf ../../etc/init.d/dnsmasq $RPM_BUILD_ROOT/usr/sbin/rcdnsmasq
gzip -9 dnsmasq.8
install -o root -g root -m 644 dnsmasq.8.gz $RPM_BUILD_ROOT%{_mandir}/man8
###############################################################################
#
@@ -108,7 +104,8 @@ rm -rf $RPM_BUILD_ROOT
%config /etc/dnsmasq.conf
/usr/sbin/rcdnsmasq
/usr/sbin/dnsmasq
/usr/share/locale/*/LC_MESSAGES/*
%doc %{_mandir}/man8/dnsmasq.8.gz
%doc %{_mandir}/*/man8/dnsmasq.8.gz

12
contrib/dns-loc/README Normal file
View File

@@ -0,0 +1,12 @@
Hi Simon
Here is a patch against dnsmasq 2.39 which provides support for LOC
entries in order to assign location information to dns records
(rfc1876). I tested it on OSX and on OpenWRT.
Cheers
Lorenz
More info:
http://www.ckdhr.com/dns-loc/
http://www.faqs.org/rfcs/rfc1876.html

View File

@@ -0,0 +1,522 @@
diff -Nur dnsmasq-2.39-orig/bld/Makefile dnsmasq-2.39/bld/Makefile
--- dnsmasq-2.39-orig/bld/Makefile 2007-02-17 14:37:06.000000000 +0100
+++ dnsmasq-2.39/bld/Makefile 2007-05-20 18:23:44.000000000 +0200
@@ -2,7 +2,7 @@
PKG_CONFIG ?= pkg-config
-OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o network.o \
+OBJS = cache.o rfc1035.o rfc1876.o util.o option.o forward.o isc.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
diff -Nur dnsmasq-2.39-orig/src/dnsmasq.h dnsmasq-2.39/src/dnsmasq.h
--- dnsmasq-2.39-orig/src/dnsmasq.h 2007-04-20 12:53:38.000000000 +0200
+++ dnsmasq-2.39/src/dnsmasq.h 2007-05-20 19:50:37.000000000 +0200
@@ -162,6 +162,12 @@
struct interface_name *next;
};
+struct loc_record {
+ char *name, loc[16];
+ unsigned short class;
+ struct loc_record *next;
+};
+
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -476,6 +482,7 @@
struct mx_srv_record *mxnames;
struct txt_record *txt;
struct ptr_record *ptr;
+ struct loc_record *loc;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
@@ -725,3 +732,6 @@
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now);
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now);
#endif
+
+/* rfc1876 */
+u_int32_t loc_aton(const char *ascii, u_char *binary);
diff -Nur dnsmasq-2.39-orig/src/option.c dnsmasq-2.39/src/option.c
--- dnsmasq-2.39-orig/src/option.c 2007-04-19 23:34:49.000000000 +0200
+++ dnsmasq-2.39/src/option.c 2007-05-20 20:15:15.000000000 +0200
@@ -43,6 +43,7 @@
#define LOPT_REMOTE 269
#define LOPT_SUBSCR 270
#define LOPT_INTNAME 271
+#define LOPT_LOC 272
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -122,6 +123,7 @@
{"tftp-root", 1, 0, LOPT_PREFIX },
{"tftp-max", 1, 0, LOPT_TFTP_MAX },
{"ptr-record", 1, 0, LOPT_PTR },
+ {"loc-record", 1, 0, LOPT_LOC },
#if defined(__FreeBSD__) || defined(__DragonFly__)
{"bridge-interface", 1, 0 , LOPT_BRIDGE },
#endif
@@ -235,6 +237,7 @@
{ "-y, --localise-queries", gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
{ "-Y --txt-record=name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
{ " --ptr-record=name,target", gettext_noop("Specify PTR DNS record."), NULL },
+ { " --loc-record=name,lat lon alt", gettext_noop("Specify LOC DNS record."), NULL },
{ " --interface-name=name,interface", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
{ "-z, --bind-interfaces", gettext_noop("Bind only to interfaces in use."), NULL },
{ "-Z, --read-ethers", gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
@@ -1835,6 +1838,37 @@
new->intr = safe_string_alloc(comma);
break;
}
+
+ case LOPT_LOC:
+ {
+ struct loc_record *new;
+ unsigned char *p, *q;
+
+ comma = split(arg);
+
+ if (!canonicalise_opt(arg))
+ {
+ option = '?';
+ problem = _("bad LOC record");
+ break;
+ }
+
+ new = safe_malloc(sizeof(struct loc_record));
+ new->next = daemon->loc;
+ daemon->loc = new;
+ new->class = C_IN;
+ if (!comma || loc_aton(comma,new->loc)!=16)
+ {
+ option = '?';
+ problem = _("bad LOC record");
+ break;
+ }
+
+ if (comma)
+ *comma = 0;
+ new->name = safe_string_alloc(arg);
+ break;
+ }
case LOPT_PTR: /* --ptr-record */
{
diff -Nur dnsmasq-2.39-orig/src/rfc1035.c dnsmasq-2.39/src/rfc1035.c
--- dnsmasq-2.39-orig/src/rfc1035.c 2007-04-20 12:54:26.000000000 +0200
+++ dnsmasq-2.39/src/rfc1035.c 2007-05-20 18:22:46.000000000 +0200
@@ -1112,6 +1112,27 @@
}
}
+ if (qtype == T_LOC || qtype == T_ANY)
+ {
+ struct loc_record *t;
+ for(t = daemon->loc; t ; t = t->next)
+ {
+ if (t->class == qclass && hostname_isequal(name, t->name))
+ {
+ ans = 1;
+ if (!dryrun)
+ {
+ log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->local_ttl, NULL,
+ T_LOC, t->class, "t", 16, t->loc))
+ anscount++;
+
+ }
+ }
+ }
+ }
+
if (qclass == C_IN)
{
if (qtype == T_PTR || qtype == T_ANY)
diff -Nur dnsmasq-2.39-orig/src/rfc1876.c dnsmasq-2.39/src/rfc1876.c
--- dnsmasq-2.39-orig/src/rfc1876.c 1970-01-01 01:00:00.000000000 +0100
+++ dnsmasq-2.39/src/rfc1876.c 2007-05-20 19:50:10.000000000 +0200
@@ -0,0 +1,379 @@
+/*
+ * routines to convert between on-the-wire RR format and zone file
+ * format. Does not contain conversion to/from decimal degrees;
+ * divide or multiply by 60*60*1000 for that.
+ */
+
+#include "dnsmasq.h"
+
+static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+ 1000000,10000000,100000000,1000000000};
+
+/* takes an XeY precision/size value, returns a string representation.*/
+static const char *
+precsize_ntoa(u_int8_t prec)
+{
+ static char retbuf[sizeof("90000000.00")];
+ unsigned long val;
+ int mantissa, exponent;
+
+ mantissa = (int)((prec >> 4) & 0x0f) % 10;
+ exponent = (int)((prec >> 0) & 0x0f) % 10;
+
+ val = mantissa * poweroften[exponent];
+
+ (void) sprintf(retbuf,"%d.%.2d", val/100, val%100);
+ return (retbuf);
+}
+
+/* converts ascii size/precision X * 10**Y(cm) to 0xXY. moves pointer.*/
+static u_int8_t
+precsize_aton(char **strptr)
+{
+ unsigned int mval = 0, cmval = 0;
+ u_int8_t retval = 0;
+ register char *cp;
+ register int exponent;
+ register int mantissa;
+
+ cp = *strptr;
+
+ while (isdigit(*cp))
+ mval = mval * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* centimeters */
+ cp++;
+ if (isdigit(*cp)) {
+ cmval = (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ cmval += (*cp++ - '0');
+ }
+ }
+ }
+ cmval = (mval * 100) + cmval;
+
+ for (exponent = 0; exponent < 9; exponent++)
+ if (cmval < poweroften[exponent+1])
+ break;
+
+ mantissa = cmval / poweroften[exponent];
+ if (mantissa > 9)
+ mantissa = 9;
+
+ retval = (mantissa << 4) | exponent;
+
+ *strptr = cp;
+
+ return (retval);
+}
+
+/* converts ascii lat/lon to unsigned encoded 32-bit number.
+ * moves pointer. */
+static u_int32_t
+latlon2ul(char **latlonstrptr,int *which)
+{
+ register char *cp;
+ u_int32_t retval;
+ int deg = 0, min = 0, secs = 0, secsfrac = 0;
+
+ cp = *latlonstrptr;
+
+ while (isdigit(*cp))
+ deg = deg * 10 + (*cp++ - '0');
+
+ while (isspace(*cp))
+ cp++;
+
+ if (!(isdigit(*cp)))
+ goto fndhemi;
+
+ while (isdigit(*cp))
+ min = min * 10 + (*cp++ - '0');
+ while (isspace(*cp))
+ cp++;
+
+ if (!(isdigit(*cp)))
+ goto fndhemi;
+
+ while (isdigit(*cp))
+ secs = secs * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* decimal seconds */
+ cp++;
+ if (isdigit(*cp)) {
+ secsfrac = (*cp++ - '0') * 100;
+ if (isdigit(*cp)) {
+ secsfrac += (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ secsfrac += (*cp++ - '0');
+ }
+ }
+ }
+ }
+
+ while (!isspace(*cp)) /* if any trailing garbage */
+ cp++;
+
+ while (isspace(*cp))
+ cp++;
+
+ fndhemi:
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'E': case 'e':
+ retval = ((unsigned)1<<31)
+ + (((((deg * 60) + min) * 60) + secs) * 1000)
+ + secsfrac;
+ break;
+ case 'S': case 's':
+ case 'W': case 'w':
+ retval = ((unsigned)1<<31)
+ - (((((deg * 60) + min) * 60) + secs) * 1000)
+ - secsfrac;
+ break;
+ default:
+ retval = 0; /* invalid value -- indicates error */
+ break;
+ }
+
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'S': case 's':
+ *which = 1; /* latitude */
+ break;
+ case 'E': case 'e':
+ case 'W': case 'w':
+ *which = 2; /* longitude */
+ break;
+ default:
+ *which = 0; /* error */
+ break;
+ }
+
+ cp++; /* skip the hemisphere */
+
+ while (!isspace(*cp)) /* if any trailing garbage */
+ cp++;
+
+ while (isspace(*cp)) /* move to next field */
+ cp++;
+
+ *latlonstrptr = cp;
+
+ return (retval);
+}
+
+/* converts a zone file representation in a string to an RDATA
+ * on-the-wire representation. */
+u_int32_t
+loc_aton(const char *ascii, u_char *binary)
+{
+ const char *cp, *maxcp;
+ u_char *bcp;
+
+ u_int32_t latit = 0, longit = 0, alt = 0;
+ u_int32_t lltemp1 = 0, lltemp2 = 0;
+ int altmeters = 0, altfrac = 0, altsign = 1;
+ u_int8_t hp = 0x16; /* default = 1e6 cm = 10000.00m = 10km */
+ u_int8_t vp = 0x13; /* default = 1e3 cm = 10.00m */
+ u_int8_t siz = 0x12; /* default = 1e2 cm = 1.00m */
+ int which1 = 0, which2 = 0;
+
+ cp = ascii;
+ maxcp = cp + strlen(ascii);
+
+ lltemp1 = latlon2ul(&cp, &which1);
+ lltemp2 = latlon2ul(&cp, &which2);
+
+ switch (which1 + which2) {
+ case 3: /* 1 + 2, the only valid combination */
+ if ((which1 == 1) && (which2 == 2)) { /* normal case */
+ latit = lltemp1;
+ longit = lltemp2;
+ } else if ((which1 == 2) && (which2 == 1)) {/*reversed*/
+ longit = lltemp1;
+ latit = lltemp2;
+ } else { /* some kind of brokenness */
+ return 0;
+ }
+ break;
+ default: /* we didn't get one of each */
+ return 0;
+ }
+
+ /* altitude */
+ if (*cp == '-') {
+ altsign = -1;
+ cp++;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ while (isdigit(*cp))
+ altmeters = altmeters * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /* decimal meters */
+ cp++;
+ if (isdigit(*cp)) {
+ altfrac = (*cp++ - '0') * 10;
+ if (isdigit(*cp)) {
+ altfrac += (*cp++ - '0');
+ }
+ }
+ }
+
+ alt = (10000000 + (altsign * (altmeters * 100 + altfrac)));
+
+ while (!isspace(*cp) && (cp < maxcp))
+ /* if trailing garbage or m */
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+ if (cp >= maxcp)
+ goto defaults;
+
+ siz = precsize_aton(&cp);
+
+ while (!isspace(*cp) && (cp < maxcp))/*if trailing garbage or m*/
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ hp = precsize_aton(&cp);
+
+ while (!isspace(*cp) && (cp < maxcp))/*if trailing garbage or m*/
+ cp++;
+
+ while (isspace(*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ vp = precsize_aton(&cp);
+
+ defaults:
+
+ bcp = binary;
+ *bcp++ = (u_int8_t) 0; /* version byte */
+ *bcp++ = siz;
+ *bcp++ = hp;
+ *bcp++ = vp;
+ PUTLONG(latit,bcp);
+ PUTLONG(longit,bcp);
+ PUTLONG(alt,bcp);
+
+ return (16); /* size of RR in octets */
+}
+
+/* takes an on-the-wire LOC RR and prints it in zone file
+ * (human readable) format. */
+char *
+loc_ntoa(const u_char *binary,char *ascii)
+{
+ static char tmpbuf[255*3];
+
+ register char *cp;
+ register const u_char *rcp;
+
+ int latdeg, latmin, latsec, latsecfrac;
+ int longdeg, longmin, longsec, longsecfrac;
+ char northsouth, eastwest;
+ int altmeters, altfrac, altsign;
+
+ const int referencealt = 100000 * 100;
+
+ int32_t latval, longval, altval;
+ u_int32_t templ;
+ u_int8_t sizeval, hpval, vpval, versionval;
+
+ char *sizestr, *hpstr, *vpstr;
+
+ rcp = binary;
+ if (ascii)
+ cp = ascii;
+ else {
+ cp = tmpbuf;
+ }
+
+ versionval = *rcp++;
+
+ if (versionval) {
+ sprintf(cp,"; error: unknown LOC RR version");
+ return (cp);
+ }
+
+ sizeval = *rcp++;
+
+ hpval = *rcp++;
+ vpval = *rcp++;
+
+ GETLONG(templ,rcp);
+ latval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ,rcp);
+ longval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ,rcp);
+ if (templ < referencealt) { /* below WGS 84 spheroid */
+ altval = referencealt - templ;
+ altsign = -1;
+ } else {
+ altval = templ - referencealt;
+ altsign = 1;
+ }
+
+ if (latval < 0) {
+ northsouth = 'S';
+ latval = -latval;
+ }
+ else
+ northsouth = 'N';
+
+ latsecfrac = latval % 1000;
+ latval = latval / 1000;
+ latsec = latval % 60;
+ latval = latval / 60;
+ latmin = latval % 60;
+ latval = latval / 60;
+ latdeg = latval;
+
+ if (longval < 0) {
+ eastwest = 'W';
+ longval = -longval;
+ }
+ else
+ eastwest = 'E';
+
+ longsecfrac = longval % 1000;
+ longval = longval / 1000;
+ longsec = longval % 60;
+ longval = longval / 60;
+ longmin = longval % 60;
+ longval = longval / 60;
+ longdeg = longval;
+
+ altfrac = altval % 100;
+ altmeters = (altval / 100) * altsign;
+
+ sizestr = strdup(precsize_ntoa(sizeval));
+ hpstr = strdup(precsize_ntoa(hpval));
+ vpstr = strdup(precsize_ntoa(vpval));
+
+ sprintf(cp,
+ "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %d.%.2dm %sm %sm %sm",
+ latdeg, latmin, latsec, latsecfrac, northsouth,
+ longdeg, longmin, longsec, longsecfrac, eastwest,
+ altmeters, altfrac, sizestr, hpstr, vpstr);
+ free(sizestr);
+ free(hpstr);
+ free(vpstr);
+
+ return (cp);
+}

22
contrib/dnsmasq_MacOSX/DNSmasq Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
. /etc/rc.common
StartService() {
if [ "${DNSMASQ:=-NO-}" = "-YES-" ] ; then
/usr/local/sbin/dnsmasq -q -n
fi
}
StopService() {
pid=`GetPID dnsmasq`
if [ $? -eq 0 ]; then
kill $pid
fi
}
RestartService() {
StopService "$@"
StartService "$@"
}
RunService "$1"

View File

@@ -0,0 +1,42 @@
{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf100
{\fonttbl\f0\fswiss\fcharset77 Helvetica;\f1\fnil\fcharset77 Monaco;}
{\colortbl;\red255\green255\blue255;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11120\viewh10100\viewkind0
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\ql\qnatural\pardirnatural
\f0\fs24 \cf0 1. If you've used DNSenabler, or if you're using Mac OS X Server, or if you have in any other way activated Mac OS X's built-in DHCP and/or DNS servers, disable them. This would usually involve checking that they are either set to -NO- or absent altogether in
\f1 /etc/hostconfig
\f0 . If you've never done anything to do with DNS or DHCP servers on a client version of MacOS X, you won't need to worry about this; it will already be configured for you.\
\
2. Add a configuration item to
\f1 /etc/hostconfig
\f0 as follows:\
\
\f1 DNSMASQ=-YES-
\f0 \
\
3. Create a system-wide StartupItems directory for dnsmasq:\
\
\f1 sudo mkdir -p /Library/StartupItems/DNSmasq\
\f0 \
4. Copy the files
\f1 DNSmasq
\f0 and
\f1 StartupParameters.plist
\f0 into this directory, and make sure the former is executable:\
\
\f1 sudo cp DNSmasq StartupParameters.plist /Library/StartupItems/DNSmasq\
sudo chmod 755 /Library/StartupItems/DNSmasq/DNSmasq\
\f0 \
5. Start the service:\
\
\f1 sudo /Library/StartupItems/DNSmasq/DNSmasq start\
\f0 \cf0 \
That should be all...}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Description</key>
<string>DNSmasq</string>
<key>OrderPreference</key>
<string>None</string>
<key>Provides</key>
<array>
<string>DNSmasq</string>
</array>
<key>Uses</key>
<array>
<string>Network</string>
</array>
</dict>
</plist>

44
contrib/openvpn/README Normal file
View File

@@ -0,0 +1,44 @@
The patch I have attached lets me get the behavior I wish out of
dnsmasq. I also include my version of dhclient-enter-hooks as
required for the switchover from pre-dnsmasq and dhclient.
On 8/16/05, Joseph Tate <dragonstrider@gmail.com> wrote:
> I'm trying to use dnsmasq on a laptop in order to facilitate openvpn
> connections. As such, the only configuration option I'm concerned
> about is a single server=3D/example.com/192.168.0.1 line.
>
> The way I currently have it set up is I modified dhclient to write its
> resolv.conf data to /etc/resolv.conf.dhclient and configured
> /etc/dnsmasq.conf to look there for its upstream dns servers.
> /etc/resolv.conf is set to nameserver 127.0.0.1
>
> All of this works great. When I start the openvpn service, it the
> routes, and queries to the domain in the server=3D line work just fine.
>
> The only problem is that the hostname for my system doesn't get set
> correctly. With the resolv.conf data written to something other than
> /etc/resolv.conf, the ifup scripts don't have a valid dns server to do
> the ipcalc call to set the laptop's hostname. If I start dnsmasq
> before the network comes up, something gets fubar'd. I'm not sure how
> to describe it exactly, but network services are slow to load, and
> restarting networking and dnsmasq doesn't solve the problem. Perhaps
> dnsmasq is answering the dhcp request when the network starts?
> Certainly not desired behavior.
>
> Anyway, my question: is there a way to have the best of both worlds?
> DHCP requests to another server, and DNS lookups that work at all
> times?
>
> My current best idea on how to solve this problem is modifying the
> dnsmasq initscript to tweak /etc/dhclient-enter-hooks to change where
> dhclient writes resolv.conf data, and fixing up /etc/resolv.conf on
> the fly to set 127.0.0.1 to the nameserver (and somehow keep the
> search domains intact), but I'm hoping that I'm just missing some key
> piece of the puzzle and that this problem has been solved before. Any
> insights?
>
> --
> Joseph Tate
> Personal e-mail: jtate AT dragonstrider DOT com
> Web: http://www.dragonstrider.com
>

View File

@@ -0,0 +1,30 @@
#!/bin/bash
function save_previous() {
if [ -e $1 -a ! -e $1.predhclient ]; then
mv $1 $1.predhclient
fi
}
function write_resolv_conf() {
RESOLVCONF=$1
if [ -n "$new_domain_name" ] || [ -n "$new_domain_name_servers" ]; then
save_previous $RESOLVCONF
echo '; generated by /etc/dhclient-enter-hooks' > $RESOLVCONF
if [ -n "$SEARCH" ]; then
echo search $SEARCH >> $RESOLVCONF
else
if [ -n "$new_domain_name" ]; then
echo search $new_domain_name >> $RESOLVCONF
fi
fi
chmod 644 $RESOLVCONF
for nameserver in $new_domain_name_servers; do
echo nameserver $nameserver >>$RESOLVCONF
done
fi
}
make_resolv_conf() {
write_resolv_conf /etc/resolv.conf
}

View File

@@ -0,0 +1,61 @@
--- dnsmasq-2.22/rpm/dnsmasq.rh 2005-03-24 09:51:18.000000000 -0500
+++ dnsmasq-2.22/rpm/dnsmasq.rh.new 2005-08-25 10:52:04.310568784 -0400
@@ -2,7 +2,7 @@
#
# Startup script for the DNS caching server
#
-# chkconfig: 2345 99 01
+# chkconfig: 2345 07 89
# description: This script starts your DNS caching server
# processname: dnsmasq
# pidfile: /var/run/dnsmasq.pid
@@ -10,6 +10,25 @@
# Source function library.
. /etc/rc.d/init.d/functions
+function setup_dhclient_enter_hooks() {
+ if [ -f /etc/dhclient-enter-hooks ]; then
+ . /etc/dhclient-enter-hooks
+ cp /etc/resolv.conf /etc/resolv.conf.dnsmasq
+ cp /etc/dhclient-enter-hooks /etc/dhclient-enter-hooks.dnsmasq
+ sed -e 's/resolv\.conf$/resolv.conf.dnsmasq/' /etc/dhclient-enter-hooks.dnsmasq > /etc/dhclient-enter-hooks
+ sed -e 's/\(nameserver[ tab]\+\)[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$/\1127.0.0.1/' /etc/resolv.conf.dnsmasq > /etc/resolv.conf
+ fi
+}
+
+function teardown_dhclient_enter_hooks() {
+ if [ -f /etc/dhclient-enter-hooks -a -f /etc/dhclient-enter-hooks.dnsmasq ]; then
+ if [ -f /etc/resolv.conf.dnsmasq ]; then
+ mv /etc/resolv.conf.dnsmasq /etc/resolv.conf
+ fi
+ mv /etc/dhclient-enter-hooks.dnsmasq /etc/dhclient-enter-hooks
+ fi
+}
+
# Source networking configuration.
. /etc/sysconfig/network
@@ -24,7 +43,7 @@
MAILHOSTNAME=""
# change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
-RESOLV_CONF=""
+RESOLV_CONF="/etc/resolv.conf.dnsmasq"
# change this if you want dnsmasq to cache any "hostname" or "client-hostname" from
# a dhcpd's lease file
@@ -54,6 +73,7 @@
case "$1" in
start)
echo -n "Starting dnsmasq: "
+ setup_dhclient_enter_hooks
daemon $dnsmasq $OPTIONS
RETVAL=$?
echo
@@ -62,6 +82,7 @@
stop)
if test "x`pidof dnsmasq`" != x; then
echo -n "Shutting down dnsmasq: "
+ teardown_dhclient_enter_hooks
killproc dnsmasq
fi
RETVAL=$?

View File

@@ -0,0 +1,68 @@
#!/bin/bash
#
# /usr/sbin/dnsmasq-portforward
#
# A script which gets run when the dnsmasq DHCP lease database changes.
# It logs to $LOGFILE, if it exists, and maintains port-forwards using
# IP-tables so that they always point to the correct host. See
# $PORTSFILE for details on configuring this. dnsmasq must be version 2.34
# or later.
#
# To enable this script, add
# dhcp-script=/usr/sbin/dnsmasq-portforward
# to /etc/dnsmasq.conf
#
# To enable logging, touch $LOGFILE
#
PORTSFILE=/etc/portforward
LOGFILE=/var/log/dhcp.log
IPTABLES=/sbin/iptables
action=${1:-0}
hostname=${4}
# log what's going on.
if [ -f ${LOGFILE} ] ; then
date +"%D %T $*" >>${LOGFILE}
fi
# If a lease gets stripped of a name, we see that as an "old" action
# with DNSMASQ_OLD_HOSTNAME set, convert it into a "del"
if [ ${DNSMASQ_OLD_HOSTNAME} ] && [ ${action} = old ] ; then
action=del
hostname=${DNSMASQ_OLD_HOSTNAME}
fi
# action init is not relevant, and will only be seen when leasefile-ro is set.
if [ ${action} = init ] ; then
exit 0
fi
if [ ${hostname} ]; then
ports=$(sed -n -e "/^${hostname}\ .*/ s/^.* //p" ${PORTSFILE})
for port in $ports; do
verb=removed
protocol=tcp
if [ ${port:0:1} = u ] ; then
protocol=udp
port=${port/u/}
fi
src=${port/:*/}
dst=${port/*:/}
# delete first, to avoid multiple copies of rules.
${IPTABLES} -t nat -D PREROUTING -p $protocol --destination-port $src -j DNAT --to-destination ${3}:$dst
if [ ${action} != del ] ; then
${IPTABLES} -t nat -A PREROUTING -p $protocol --destination-port $src -j DNAT --to-destination ${3}:$dst
verb=added
fi
if [ -f ${LOGFILE} ] ; then
echo " DNAT $protocol $src to ${3}:$dst ${verb}." >>${LOGFILE}
fi
done
fi
exit 0

View File

@@ -0,0 +1,28 @@
# This file is read by /usr/sbin/dnsmasq-portforward and used to set up port
# forwarding to hostnames. If the dnsmasq-determined hostname matches the
# first column of this file, then a DNAT port-forward will be set up
# to the address which has just been allocated by DHCP . The second field
# is port number(s). If there is only one, then the port-forward goes to
# the same port on the DHCP-client, if there are two seperated with a
# colon, then the second number is the port to which the connection
# is forwarded on the DHCP-client. By default, forwarding is set up
# for TCP, but it can done for UDP instead by prefixing the port to "u".
# To forward both TCP and UDP, two lines are required.
#
# eg.
# wwwserver 80
# will set up a port forward from port 80 on this host to port 80
# at the address allocated to wwwserver whenever wwwserver gets a DHCP lease.
#
# wwwserver 8080:80
# will set up a port forward from port 8080 on this host to port 80
# on the DHCP-client.
#
# dnsserver 53
# dnsserver u53
# will port forward port 53 UDP and TCP from this host to port 53 on dnsserver.
#
# Port forwards will recreated when dnsmasq restarts after a reboot, and
# removed when DHCP leases expire. After editing this file, send
# SIGHUP to dnsmasq to install new iptables entries in the kernel.

View File

@@ -0,0 +1,56 @@
#!/bin/sh
CWD=`pwd`
PKG=/tmp/package-dnsmasq
VERSION=2.24
ARCH=${ARCH:-i486}
BUILD=${BUILD:-1}
if [ "$ARCH" = "i386" ]; then
SLKCFLAGS="-O2 -march=i386 -mcpu=i686"
elif [ "$ARCH" = "i486" ]; then
SLKCFLAGS="-O2 -march=i486 -mcpu=i686"
elif [ "$ARCH" = "s390" ]; then
SLKCFLAGS="-O2"
elif [ "$ARCH" = "x86_64" ]; then
SLKCFLAGS="-O2"
fi
rm -rf $PKG
mkdir -p $PKG
cd /tmp
rm -rf dnsmasq-$VERSION
tar xzvf $CWD/dnsmasq-$VERSION.tar.gz
cd dnsmasq-$VERSION
zcat $CWD/dnsmasq.leasedir.diff.gz | patch -p1 --verbose --backup --suffix=.orig || exit
chown -R root.root .
make install-i18n PREFIX=/usr DESTDIR=$PKG MANDIR=/usr/man
chmod 755 $PKG/usr/sbin/dnsmasq
chown -R root.bin $PKG/usr/sbin
gzip -9 $PKG/usr/man/man8/dnsmasq.8
for f in $PKG/usr/share/man/*; do
if [ -f $$f/man8/dnsmasq.8 ]; then
gzip -9 $$f/man8/dnsmasq.8 ;
fi
done
gzip -9 $PKG/usr/man/*/man8/dnsmasq.8
mkdir -p $PKG/var/state/dnsmasq
( cd $PKG
find . | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
find . | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null
)
mkdir $PKG/etc
cat dnsmasq.conf.example > $PKG/etc/dnsmasq.conf.new
mkdir $PKG/etc/rc.d
zcat $CWD/rc.dnsmasq.gz > $PKG/etc/rc.d/rc.dnsmasq.new
mkdir -p $PKG/usr/doc/dnsmasq-$VERSION
cp -a \
CHANGELOG COPYING FAQ UPGRADING_to_2.0 doc.html setup.html \
$PKG/usr/doc/dnsmasq-$VERSION
mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc
zcat $CWD/doinst.sh.gz > $PKG/install/doinst.sh
cd $PKG
makepkg -l y -c n ../dnsmasq-$VERSION-$ARCH-$BUILD.tgz

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,19 @@
# HOW TO EDIT THIS FILE:
# The "handy ruler" below makes it easier to edit a package description. Line
# up the first '|' above the ':' following the base package name, and the '|' on
# the right side marks the last column you can put a character in. You must make
# exactly 11 lines for the formatting to be correct. It's also customary to
# leave one space after the ':'.
|-----handy-ruler------------------------------------------------------|
dnsmasq: dnsmasq (small DNS and DHCP server)
dnsmasq:
dnsmasq: Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
dnsmasq: server. It is designed to provide DNS (and optionally DHCP) to a
dnsmasq: small network, and can serve the names of local machines which are not
dnsmasq: in the global DNS.
dnsmasq:
dnsmasq: Dnsmasq was written by Simon Kelley.
dnsmasq:
dnsmasq:
dnsmasq:

19
contrib/try-all-ns/README Normal file
View File

@@ -0,0 +1,19 @@
Date: Thu, 07 Dec 2006 00:41:43 -0500
From: Bob Carroll <bob.carroll@rit.edu>
Subject: dnsmasq suggestion
To: simon@thekelleys.org.uk
Hello,
I recently needed a feature in dnsmasq for a very bizarre situation. I
placed a list of name servers in a special resolve file and told dnsmasq
to use that. But I wanted it to try requests in order and treat NXDOMAIN
requests as a failed tcp connection. I wrote the feature into dnsmasq
and it seems to work. I prepared a patch in the event that others might
find it useful as well.
Thanks and keep up the good work.
--Bob

View File

@@ -0,0 +1,61 @@
diff -Nau dnsmasq-2.35/src/dnsmasq.h dnsmasq/src/dnsmasq.h
--- dnsmasq-2.35/src/dnsmasq.h 2006-10-18 16:24:50.000000000 -0400
+++ dnsmasq/src/dnsmasq.h 2006-11-16 22:06:31.000000000 -0500
@@ -112,6 +112,7 @@
#define OPT_NO_PING 2097152
#define OPT_LEASE_RO 4194304
#define OPT_RELOAD 8388608
+#define OPT_TRY_ALL_NS 16777216
struct all_addr {
union {
diff -Nau dnsmasq-2.35/src/forward.c dnsmasq/src/forward.c
--- dnsmasq-2.35/src/forward.c 2006-10-18 16:24:50.000000000 -0400
+++ dnsmasq/src/forward.c 2006-11-16 22:08:19.000000000 -0500
@@ -445,6 +445,10 @@
{
struct server *server = forward->sentto;
+ // If strict-order and try-all-ns are set, treat NXDOMAIN as a failed request
+ if( (daemon->options & OPT_ORDER) && (daemon->options && OPT_TRY_ALL_NS)
+ && header->rcode == NXDOMAIN ) header->rcode = SERVFAIL;
+
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
diff -Nau dnsmasq-2.35/src/option.c dnsmasq/src/option.c
--- dnsmasq-2.35/src/option.c 2006-10-18 16:24:50.000000000 -0400
+++ dnsmasq/src/option.c 2006-11-16 22:10:36.000000000 -0500
@@ -28,7 +28,7 @@
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
-
+#define LOPT_TRY_ALL_NS 257
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -102,6 +102,7 @@
{"leasefile-ro", 0, 0, '9'},
{"dns-forward-max", 1, 0, '0'},
{"clear-on-reload", 0, 0, LOPT_RELOAD },
+ {"try-all-ns", 0, 0, LOPT_TRY_ALL_NS },
{ NULL, 0, 0, 0 }
};
@@ -134,6 +135,7 @@
{ '5', OPT_NO_PING },
{ '9', OPT_LEASE_RO },
{ LOPT_RELOAD, OPT_RELOAD },
+ { LOPT_TRY_ALL_NS,OPT_TRY_ALL_NS },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
@@ -208,6 +210,7 @@
{ "-9, --leasefile-ro", gettext_noop("Read leases at startup, but never write the lease file."), NULL },
{ "-0, --dns-forward-max=<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
{ " --clear-on-reload", gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
+ { " --try-all-ns", gettext_noop("Try all name servers in tandem on NXDOMAIN replies (use with strict-order)."), NULL },
{ NULL, NULL, NULL }
};

54
contrib/webmin/README Normal file
View File

@@ -0,0 +1,54 @@
This is the README for the DNSmasq webmin module.
Problems:
1) There's only basic error checking - if you enter some bad
addresses or names, they will go straight into the config file
although we do check for things like IP addresses being of
the correct form (no letters, 4 groups of up to 3 digits
separated by dots etc). One thing that ISN'T CHECKED FOR is
that IP dotted quads are all numbers < 256. Another is that
netmasks are logical (you could enter a netmask of 255.0.255.0
for example). Essentially, if it'll pass the config file
regex scanner (and the above examples will), it won't be
flagged as "bad" even if it is a big no-no for dnsmasq itself.
2) Code is ugly and a kludge - I ain't a programmer! There are probably
a lot of things that could be done to tidy up the code - eg,
it probably wouldn't hurt to move some common stuff into the lib file.
3) I've used the %text hash and written an english lang file, but
I am mono-lingual so no other language support as yet.
4) for reasons unknown to me, the icon does not appear properly
on the servers page of webmin (at least it doesn't for me!)
5) icons have been shamelessly stolen from the ipfilter module,
specifically the up and down arrows.
6) if you delete an item, the config file will contain
an otherwise empty, but commented line. This means that if
you add some new stuff, then delete it, the config file
will have a number of lines at the end that are just comments.
Therefore, the config file could possibly grow quite large.
7) NO INCLUDE FILES!
if you use an include file, it'll be flagged as an error.
OK if the include file line is commented out though.
8) deprecated lines not supported (eg user and group) - they
may produce an error! (user and group don't, but you can't change
them)
IOW, it works, it's just not very elegant and not very robust.
Hope you find it useful though - I do, as I prevents me having to ever
wade through the config file and man pages again.
If you modify it, or add a language file, and you have a spare moment,
please e-mail me - I won't be upset at all if you fix my poor coding!
(rather the opposite - I'd be pleased someone found it usefull)
Cheers,
Neil Fisher <neil@magnecor.com.au>

BIN
contrib/webmin/dnsmasq.wbm Normal file

Binary file not shown.

6
contrib/wrt/Makefile Normal file
View File

@@ -0,0 +1,6 @@
CFLAGS?= -O2 -Wall -W
all: dhcp_release dhcp_lease_time
clean:
rm -f *~ *.o core dhcp_release dhcp_lease_time

81
contrib/wrt/README Normal file
View File

@@ -0,0 +1,81 @@
This script can be used to implement persistent leases on openWRT, DD-WRT
etc. Persistent leases are good: if the lease database is lost on a
reboot, then it will eventually be restored as hosts renew their
leases. Until a host renews (which may take hours/days) it will
not exist in the DNS if dnsmasq's DDNS function is in use.
*WRT systems remount all non-volatile fileystems read-only after boot,
so the normal leasefile will not work. They do, however have NV
storage, accessed with the nvram command:
/usr/lib # nvram
usage: nvram [get name] [set name=value] [unset name] [show]
The principle is that leases are kept in NV variable with data
corresponding to the line in a leasefile:
dnsmasq_lease_192.168.1.56=3600 00:41:4a:05:80:74 192.168.1.56 * *
By giving dnsmasq the leasefile-ro command, it no longer creates or writes a
leasefile; responsibility for maintaining the lease database transfers
to the lease change script. At startup, in leasefile-ro mode,
dnsmasq will run
"<lease_change_script> init"
and read whatever that command spits out, expecting it to
be in dnsmasq leasefile format.
So the lease change script, given "init" as argv[1] will
suck existing leases out of the NVRAM and emit them from
stdout in the correct format.
The second part of the problem is keeping the NVRAM up-to-date: this
is done by the lease-change script which dnsmasq runs when a lease is
updated. When it is called with argv[1] as "old", "add", or "del"
it updates the relevant nvram entry.
So, dnsmasq should be run as :
dnsmasq --leasefile-ro --dhcp-script=/path/to/lease_update.sh
or the same flags added to /etc/dnsmasq.conf
Notes:
This needs dnsmasq-2.33 or later to work.
This technique will work with, or without, compilation with
HAVE_BROKEN_RTC. Compiling with HAVE_BROKEN_RTC is
_highly_recommended_ for this application since is avoids problems
with the system clock being warped by NTP, and it vastly reduces the
number of writes to the NVRAM. With HAVE_BROKEN_RTC, NVRAM is updated
only when a lease is created or destroyed; without it, a write occurs
every time a lease is renewed.
It probably makes sense to restrict the number of active DHCP leases
to an appropriate number using dhcp-lease-max. On a new DD_WRT system,
there are about 10K bytes free in the NVRAM. Each lease record is
about 100 bytes, so restricting the number of leases to 50 will limit
use to half that. (The default limit in the distributed source is 150)
Any UI script which reads the dnsmasq leasefile will have to be
ammended, probably by changing it to read the output of
`lease_update init` instead.
Thanks:
To Steve Horbachuk for checks on the script and debugging beyond the
call of duty.
Simon Kelley
Fri Jul 28 11:51:13 BST 2006

View File

@@ -0,0 +1,214 @@
/* Copyright (c) 2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
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.
*/
/* dhcp_lease_time <address> */
/* 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.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>
#define DHCP_CHADDR_MAX 16
#define BOOTREQUEST 1
#define DHCP_COOKIE 0x63825363
#define OPTION_PAD 0
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_END 255
#define DHCPINFORM 8
#define DHCP_SERVER_PORT 67
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
#define option_ptr(opt) ((void *)&(((unsigned char *)(opt))[2]))
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];
u32 cookie;
unsigned char options[308];
};
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
{
while (*p != OPTION_END)
{
if (p >= end)
return NULL; /* malformed packet */
else if (*p == OPTION_PAD)
p++;
else
{
int opt_len;
if (p >= end - 2)
return NULL; /* malformed packet */
opt_len = option_len(p);
if (p >= end - (2 + opt_len))
return NULL; /* malformed packet */
if (*p == opt && opt_len >= minsize)
return p;
p += opt_len + 2;
}
}
return opt == OPTION_END ? p : NULL;
}
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
{
unsigned char *ret, *overload;
/* skip over DHCP cookie; */
if ((ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, minsize)))
return ret;
/* look for overload option. */
if (!(overload = option_find1(&mess->options[0], ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
return NULL;
/* Can we look in filename area ? */
if ((overload[2] & 1) &&
(ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
return ret;
/* finally try sname area */
if ((overload[2] & 2) &&
(ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
return ret;
return NULL;
}
static unsigned int option_uint(unsigned char *opt, int size)
{
/* this worries about unaligned data and byte order */
unsigned int ret = 0;
int i;
unsigned char *p = option_ptr(opt);
for (i = 0; i < size; i++)
ret = (ret << 8) | *p++;
return ret;
}
int main(int argc, char **argv)
{
struct in_addr lease;
struct dhcp_packet packet;
unsigned char *p = packet.options;
struct sockaddr_in dest;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
ssize_t rc;
if (argc < 2)
{
fprintf(stderr, "usage: dhcp_lease_time <address>\n");
exit(1);
}
if (fd == -1)
{
perror("cannot create socket");
exit(1);
}
lease.s_addr = inet_addr(argv[1]);
memset(&packet, 0, sizeof(packet));
packet.hlen = 0;
packet.htype = 0;
packet.op = BOOTREQUEST;
packet.ciaddr = lease;
packet.cookie = htonl(DHCP_COOKIE);
*(p++) = OPTION_MESSAGE_TYPE;
*(p++) = 1;
*(p++) = DHCPINFORM;
*(p++) = OPTION_END;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = ntohs(DHCP_SERVER_PORT);
if (sendto(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&dest, sizeof(dest)) == -1)
{
perror("sendto failed");
exit(1);
}
alarm(3); /* noddy timeout. */
rc = recv(fd, &packet, sizeof(packet), 0);
if (rc < (ssize_t)(sizeof(packet) - sizeof(packet.options)))
{
perror("recv failed");
exit(1);
}
if ((p = option_find(&packet, (size_t)rc, OPTION_LEASE_TIME, 4)))
{
unsigned int t = option_uint(p, 4);
if (t == 0xffffffff)
printf("infinite");
else
{
unsigned int x;
if ((x = t/86400))
printf("%dd", x);
if ((x = (t/3600)%24))
printf("%dh", x);
if ((x = (t/60)%60))
printf("%dm", x);
if ((x = t%60))
printf("%ds", x);
}
return 0;
}
return 1; /* no lease */
}

331
contrib/wrt/dhcp_release.c Normal file
View File

@@ -0,0 +1,331 @@
/* Copyright (c) 2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
/* dhcp_release <interface> <address> <MAC address> <client_id>
MUST be run as root - will fail otherwise. */
/* 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.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>
#define DHCP_CHADDR_MAX 16
#define BOOTREQUEST 1
#define DHCP_COOKIE 0x63825363
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_CLIENT_ID 61
#define OPTION_MESSAGE_TYPE 53
#define OPTION_END 255
#define DHCPRELEASE 7
#define DHCP_SERVER_PORT 67
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];
u32 cookie;
unsigned char options[308];
};
static struct iovec iov;
static int expand_buf(struct iovec *iov, size_t size)
{
void *new;
if (size <= iov->iov_len)
return 1;
if (!(new = malloc(size)))
{
errno = ENOMEM;
return 0;
}
if (iov->iov_base)
{
memcpy(new, iov->iov_base, iov->iov_len);
free(iov->iov_base);
}
iov->iov_base = new;
iov->iov_len = size;
return 1;
}
static ssize_t netlink_recv(int fd)
{
struct msghdr msg;
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_flags = 0;
while ((rc = recvmsg(fd, &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)
{
if (!expand_buf(&iov, 2000))
return -1;
break;
}
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(fd, &msg, 0)) == -1 && errno == EINTR);
return rc;
}
static int parse_hex(char *in, unsigned char *out, int maxlen, int *mac_type)
{
int i = 0;
char *r;
if (mac_type)
*mac_type = 0;
while (maxlen == -1 || i < maxlen)
{
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
if (*r == 0)
maxlen = i;
if (r != in )
{
if (*r == '-' && i == 0 && mac_type)
{
*r = 0;
*mac_type = strtol(in, NULL, 16);
mac_type = NULL;
}
else
{
*r = 0;
out[i] = strtol(in, NULL, 16);
i++;
}
}
in = r+1;
}
return i;
}
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)
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
ssize_t len;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
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 = 1;
req.g.rtgen_family = AF_INET;
if (sendto(fd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("sendto failed");
exit(1);
}
while (1)
{
if ((len = netlink_recv(fd)) == -1)
{
perror("netlink");
exit(1);
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_DONE)
exit(0);
else if (h->nlmsg_type == RTM_NEWADDR)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
struct rtattr *rta;
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
{
struct in_addr netmask, addr;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
for (rta = IFA_RTA(ifa); RTA_OK(rta, len1); rta = RTA_NEXT(rta, len1))
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
if (addr.s_addr && is_same_net(addr, client, netmask))
return addr;
}
}
}
exit(0);
}
int main(int argc, char **argv)
{
struct in_addr server, lease;
int mac_type;
struct dhcp_packet packet;
unsigned char *p = packet.options;
struct sockaddr_in dest;
struct ifreq ifr;
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
int nl = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
struct iovec iov;
iov.iov_len = 200;
iov.iov_base = malloc(iov.iov_len);
if (argc < 4 || argc > 5)
{
fprintf(stderr, "usage: dhcp_release <interface> <addr> <mac> [<client_id>]\n");
exit(1);
}
if (fd == -1 || nl == -1)
{
perror("cannot create socket");
exit(1);
}
/* This voodoo fakes up a packet coming from the correct interface, which really matters for
a DHCP server */
strcpy(ifr.ifr_name, argv[1]);
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == -1)
{
perror("cannot setup interface");
exit(1);
}
lease.s_addr = inet_addr(argv[2]);
server = find_interface(lease, nl, if_nametoindex(argv[1]));
memset(&packet, 0, sizeof(packet));
packet.hlen = parse_hex(argv[3], packet.chaddr, DHCP_CHADDR_MAX, &mac_type);
if (mac_type == 0)
packet.htype = ARPHRD_ETHER;
else
packet.htype = mac_type;
packet.op = BOOTREQUEST;
packet.ciaddr = lease;
packet.cookie = htonl(DHCP_COOKIE);
*(p++) = OPTION_MESSAGE_TYPE;
*(p++) = 1;
*(p++) = DHCPRELEASE;
*(p++) = OPTION_SERVER_IDENTIFIER;
*(p++) = sizeof(server);
memcpy(p, &server, sizeof(server));
p += sizeof(server);
if (argc == 5 && strcmp(argv[4], "*") != 0)
{
unsigned int clid_len = parse_hex(argv[4], p+2, 255, NULL);
*(p++) = OPTION_CLIENT_ID;
*(p++) = clid_len;
p += clid_len;
}
*(p++) = OPTION_END;
dest.sin_family = AF_INET;
dest.sin_port = ntohs(DHCP_SERVER_PORT);
dest.sin_addr = server;
if (sendto(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&dest, sizeof(dest)) == -1)
{
perror("sendto failed");
exit(1);
}
return 0;
}

54
contrib/wrt/lease_update.sh Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/sh
# Copyright (c) 2006 Simon Kelley
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# if $1 is add del or old, this is a dnsmasq-called lease-change
# script, update the nvram database. if $1 is init, emit a
# dnsmasq-format lease file to stdout representing the current state of the
# database, this is called by dnsmasq at startup.
NVRAM=/usr/sbin/nvram
PREFIX=dnsmasq_lease_
# Arguments.
# $1 is action (add, del, old)
# $2 is MAC
# $3 is address
# $4 is hostname (optional, may be unset)
# env.
# DNSMASQ_LEASE_LENGTH or DNSMASQ_LEASE_EXPIRES (which depends on HAVE_BROKEN_RTC)
# DNSMASQ_CLIENT_ID (optional, may be unset)
# File.
# length|expires MAC addr hostname|* CLID|*
# Primary key is address.
if [ ${1} = init ] ; then
${NVRAM} show | sed -n -e "/^${PREFIX}.*/ s/^.*=//p"
else
if [ ${1} = del ] ; then
${NVRAM} unset ${PREFIX}${3}
fi
if [ ${1} = old ] || [ ${1} = add ] ; then
${NVRAM} set ${PREFIX}${3}="${DNSMASQ_LEASE_LENGTH:-}${DNSMASQ_LEASE_EXPIRES:-} ${2} ${3} ${4:-*} ${DNSMASQ_CLIENT_ID:-*}"
fi
${NVRAM} commit
fi

94
dbus/DBus-interface Normal file
View File

@@ -0,0 +1,94 @@
DBus support must be enabled at compile-time and run-time. Ensure
that src/config.h contains the line
#define HAVE_DBUS.
and that /etc/dnsmasq.conf contains the line
enable-dbus
Because dnsmasq can operate stand-alone from the DBus, and may need to provide
service before the dbus daemon is available, it will continue to run
if the DBus connection is not available at startup. The DBus will be polled
every 250ms until a connection is established. Start of polling and final
connection establishment are both logged. When dnsmasq establishes a
connection to the dbus, it sends the signal "Up". Anything controlling
the server settings in dnsmasq should re-invoke the SetServers method
(q.v.) when it sees this signal. This allows dnsmasq to be restarted
and avoids startup races with the provider of nameserver information.
Dnsmasq provides one service on the DBus: uk.org.thekelleys.dnsmasq
and a single object: /uk/org/thekelleys/dnsmasq
Methods are of the form
uk.org.thekelleys.<method>
Available methods are:
GetVersion
----------
Returns a string containing the version of dnsmasq running.
ClearCache
----------
Returns nothing. Clears the domain name cache and re-reads
/etc/hosts. The same as sending dnsmasq a HUP signal.
SetServers
----------
Returns nothing. Takes a set of arguments representing the new
upstream DNS servers to be used by dnsmasq. IPv4 addresses are
represented as a UINT32 (in network byte order) and IPv6 addresses
are represented as sixteen BYTEs (since there is no UINT128 type).
Each server address may be followed by one or more STRINGS, which are
the domains for which the preceding server should be used.
Examples.
UINT32: <address1>
UNIT32: <address2>
is equivalent to
--server=<address1> --server=<address2>
UINT32 <address1>
UINT32 <address2>
STRING "somedomain.com"
is equivalent to
--server=<address1> --server=/somedomain.com/<address2>
UINT32 <address1>
UINT32 <address2>
STRING "somedomain.com"
UINT32 <address3>
STRING "anotherdomain.com"
STRING "thirddomain.com"
is equivalent to
--server=<address1>
--server=/somedomain.com/<address2>
--server=/anotherdomain.com/thirddomain.com/<address3>
Am IPv4 address of 0.0.0.0 is interpreted as "no address, local only",
so
UINT32: <0.0.0.0>
STRING "local.domain"
is equivalent to
--local=/local.domain/
Each call to SetServers completely replaces the set of servers
specified by via the DBus, but it leaves any servers specified via the
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.

16
dbus/dnsmasq.conf Normal file
View File

@@ -0,0 +1,16 @@
<!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"/>
<allow send_interface="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy context="default">
<deny own="uk.org.thekelleys.dnsmasq"/>
<deny send_destination="uk.org.thekelleys.dnsmasq"/>
<deny send_interface="uk.org.thekelleys.dnsmasq"/>
</policy>
</busconfig>

View File

@@ -1,132 +0,0 @@
###############################################################################
#
# General mumbojumbo
#
###############################################################################
Name: dnsmasq
Version: 2.16
Release: 1
Copyright: GPL
Group: System Environment/Daemons
Vendor: Simon Kelley
Packager: Simon Kelley
Distribution: Red Hat Linux
URL: http://www.thekelleys.org.uk/dnsmasq
Source0: %{name}-%{version}.tar.gz
Requires: chkconfig
BuildRoot: /var/tmp/%{name}-%{version}
Summary: A lightweight caching nameserver
%description
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP server. It
is designed to provide DNS and, optionally, DHCP, to a small network. It can
serve the names of local machines which are not in the global DNS. The DHCP
server integrates with the DNS server and allows machines with DHCP-allocated
addresses to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic DHCP
leases and BOOTP for network booting of diskless machines.
###############################################################################
#
# Build
#
###############################################################################
%prep
%setup -q
%build
make
###############################################################################
#
# Install
#
###############################################################################
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/sbin
mkdir -p -m 755 $RPM_BUILD_ROOT/etc/rc.d/init.d
mkdir -p -m 755 $RPM_BUILD_ROOT/usr/share/man/man8
cp rpm/dnsmasq.rh $RPM_BUILD_ROOT/etc/rc.d/init.d/dnsmasq
strip src/dnsmasq
cp src/dnsmasq $RPM_BUILD_ROOT/usr/sbin
cp dnsmasq.8 $RPM_BUILD_ROOT/usr/share/man/man8
cp dnsmasq.conf.example $RPM_BUILD_ROOT/etc/dnsmasq.conf
###############################################################################
#
# Clean up
#
###############################################################################
%clean
rm -rf $RPM_BUILD_ROOT
###############################################################################
#
# Post-install scriptlet
#
###############################################################################
%post
/sbin/chkconfig --add dnsmasq
###############################################################################
#
# Pre-uninstall scriptlet
#
# If there's a time when your package needs to have one last look around before
# the user erases it, the place to do it is in the %preun script. Anything that
# a package needs to do immediately prior to RPM taking any action to erase the
# package, can be done here.
#
###############################################################################
%preun
if [ $1 = 0 ]; then # execute this only if we are NOT doing an upgrade
service dnsmasq stop >/dev/null 2>&1
/sbin/chkconfig --del dnsmasq
fi
###############################################################################
#
# Post-uninstall scriptlet
#
# The %postun script executes after the package has been removed. It is the
# last chance for a package to clean up after itself.
#
###############################################################################
%postun
if [ "$1" -ge "1" ]; then
service dnsmasq restart >/dev/null 2>&1
fi
###############################################################################
#
# File list
#
###############################################################################
%files
%defattr(-,root,root)
%doc CHANGELOG COPYING FAQ doc.html setup.html UPGRADING_to_2.0
%config /etc/rc.d/init.d/dnsmasq
%config /etc/dnsmasq.conf
%attr(0755,root,root) /etc/rc.d/init.d/dnsmasq
%attr(0664,root,root) /etc/dnsmasq.conf
%attr(0755,root,root) /usr/sbin/dnsmasq
%attr(0644,root,root) /usr/share/man/man8/dnsmasq*

580
dnsmasq.8
View File

@@ -1,580 +0,0 @@
.TH DNSMASQ 8
.SH NAME
dnsmasq \- A lightweight DHCP and caching DNS server.
.SH SYNOPSIS
.B dnsmasq
.I [OPTION]...
.SH "DESCRIPTION"
.BR dnsmasq
is a lightweight DNS and DHCP server. It is intended to provide coupled DNS and DHCP service to a
LAN.
.PP
Dnsmasq accepts DNS queries and either answers them from a small, local,
cache or forwards them to a real, recursive, DNS server. It loads the
contents of /etc/hosts so that local hostnames
which do not appear in the global DNS can be resolved and also answers
DNS queries for DHCP configured hosts.
.PP
The dnsmasq DHCP server supports static address assignments, multiple
networks, DHCP-relay and RFC3011 subnet specifiers. It automatically
sends a sensible default set of DHCP options, and can be configured to
send any desired set of DHCP options. It also supports BOOTP.
.PP
Dnsmasq
supports IPv6.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
functions, for instance "--pid-file=" disables writing a PID file. On
BSD, unless the GNU getopt library is linked, the long form of the
options does not work on the command line; it is still recognised in
the configuration file.
.TP
.B \-h, --no-hosts
Don't read the hostnames in /etc/hosts.
.TP
.B \-H, --addn-hosts=<file>
Additional hosts file. Read the specified file as well as /etc/hosts. If -h is given, read
only the specified file. This option may be repeated for more than one
additional hosts file.
.TP
.B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
that the requestor should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a
time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale
data under some circumstances.
.TP
.B \-k, --keep-in-foreground
Do not go into the background at startup but otherwise run as
normal. This is intended for use when dnsmasq is run under daemontools.
.TP
.B \-d, --no-daemon
Debug mode: don't fork to the background, don't write a pid file,
don't change user id, generate a complete cache dump on receipt on
SIGUSR1, log to stderr as well as syslog, don't fork new processes
to handle TCP queries.
.TP
.B \-q, --log-queries
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
.TP
.B \-x, --pid-file=<path>
Specify an alternate path for dnsmasq to record its process-id in. Normally /var/run/dnsmasq.pid.
.TP
.B \-u, --user=<username>
Specify the userid to which dnsmasq will change after startup. Dnsmasq must normally be started as root, but it will drop root
priviledges after startup by changing id to another user. Normally this user is "nobody" but that
can be over-ridden with this switch.
.TP
.B \-g, --group=<groupname>
Specify the group which dnsmasq will run
as. The defaults to "dip", if available, to facilitate access to
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
Print the version number.
.TP
.B \-p, --port=<port>
Listen on <port> instead of the standard DNS port (53). Useful mainly for
debugging.
.TP
.B \-P, --edns-packet-max=<size>
Specify the largest EDNS.0 UDP packet which is supported by the DNS
forwarder. Defaults to 1280, which is the RFC2671-recommended maximum
for ethernet.
.TP
.B \-Q, --query-port=<query_port>
Send outbound DNS queries from, and listen for their replies on, the specific UDP port <query_port> instead of using one chosen at runtime. Useful to simplify your
firewall rules; without this, your firewall would have to allow connections from outside DNS servers to a range of UDP ports, or dynamically adapt to the
port being used by the current dnsmasq instance.
.TP
.B \-i, --interface=<interface name>
Listen only on the specified interface(s). Dnsmasq automatically adds
the loopback (local) interface to the list of interfaces to use when
the
.B \--interface
option is used. If no
.B \--interface
or
.B \--listen-address
options are given dnsmasq listens on all available interfaces except any
given in
.B \--except-interface
options. If IP alias interfaces (eg "eth1:0") are used with
.B --interface
or
.B --except-interface
options, then the
.B --bind-interfaces
option will be automatically set. This is required for deeply boring
sockets-API reasons.
.TP
.B \-I, --except-interface=<interface name>
Do not listen on the specified interface. Note that the order of
.B \--listen-address
.B --interface
and
.B --except-interface
options does not matter and that
.B --except-interface
options always override the others.
.TP
.B \-a, --listen-address=<ipaddr>
Listen on the given IP address(es). Both
.B \--interface
and
.B \--listen-address
options may be given, in which case the set of both interfaces and
addresses is used. Note that if no
.B \--interface
option is given, but
.B \--listen-address
is, dnsmasq will not automatically listen on the loopback
interface. To achieve this, its IP address, 127.0.0.1, must be
explicitly given as a
.B \--listen-address
option.
.TP
.B \-z, --bind-interfaces
On systems which support it, dnsmasq binds the wildcard address,
even when it is listening on only some interfaces. It then discards
requests that it shouldn't reply to. This has the advantage of
working even when interfaces come and go and change address. This
option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when
running another nameserver on the same machine or using IP
alias. Specifying interfaces with IP alias automatically turns this
option on. Note that this only applies to the DNS part of dnsmasq, the
DHCP server always binds the wildcard address in order to receive
broadcast packets.
.TP
.B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
which are not found in /etc/hosts or the DHCP leases file are answered
with "no such domain" rather than being forwarded upstream.
.TP
.B \-V, --alias=<old-ip>,<new-ip>[,<mask>]
Modify IPv4 addresses returned from upstream nameservers; old-ip is
replaced by new-ip. If the optional mask is given then any address
which matches the masked old-ip will be re-written. So, for instance
.B --alias=1.2.3.0,6.7.8.0,255.255.255.0
will map 1.2.3.56 to 6.7.8.56 and 1.2.3.67 to 6.7.8.67. This is what
Cisco PIX routers call "DNS doctoring".
.TP
.B \-B, --bogus-nxdomain=<ipaddr>
Transform replies which contain the IP address given into "No such
domain" replies. This is intended to counteract a devious move made by
Versign in September 2003 when they started returning the address of
an advertising web page in response to queries for unregistered names,
instead of the correct NXDOMAIN response. This option tells dnsmasq to
fake the correct response when it sees this behaviour. As at Sept 2003
the IP address being returnd by Verisign is 64.94.110.11
.TP
.B \-f, --filterwin2k
Later versions of windows make periodic DNS requests which don't get sensible answers from
the public DNS and can cause problems by triggering dial-on-demand links. This flag turns on an option
to filter such requests. The requests blocked are for records of types SOA and SRV, and type ANY where the
requested name has underscores, to catch LDAP requests.
.TP
.B \-r, --resolv-file=<file>
Read the IP addresses of the upstream nameservers from <file>, instead of
/etc/resolv.conf. For the format of this file see
.BR resolv.conf (5)
the only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
be told to poll more than one resolv.conf file, the first file name specified
overrides the default, subsequent ones add to the list. This is only
allowed when polling; the file with the currently latest modification
time is the one used.
.TP
.B \-R, --no-resolv
Don't read /etc/resolv.conf. Get upstream servers only from the command
line or the dnsmasq configuration file.
.TP
.B \-o, --strict-order
By default, dnsmasq will send queries to any of the upstream servers
it knows about and tries to favour servers to are known to
be up. Setting this flag forces dnsmasq to try each query with each
server strictly in the order they appear in /etc/resolv.conf
.TP
.B \-n, --no-poll
Don't poll /etc/resolv.conf for changes.
.TP
.B \-D, --domain-needed
Tells dnsmasq to never forward queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not knowm
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
.B \-S, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source>[#<port>]]]
Specify IP address of upsream severs directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use -R to do that. If one or
more
optional domains are given, that server is used only for those domains
and they are queried only using the specified server. This is
intended for private nameservers: if you have a nameserver on your
network which deals with names of the form
xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B -S /internal.thekelleys.org.uk/192.168.1.1
will send all queries for
internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. An empty domain specification,
.B //
has the special meaning of "unqualified names only" ie names without any
dots in them. A non-standard port may be specified as
part of the IP
address using a # character.
More than one -S flag is allowed, with
repeated domain or ipaddr parts as required.
Also permitted is a -S
flag which gives a domain but no IP address; this tells dnsmasq that
a domain is local and it may answer queries from /etc/hosts or DHCP
but should never forward queries on that domain to any upstream
servers.
.B local
is a synonym for
.B server
to make configuration files clearer in this case.
The optional second IP address after the @ character tells
dnsmasq how to set the source address of the queries to this
nameserver. It should be an address belonging to the machine on which
dnsmasq is running otherwise this server line will be logged and then
ignored. The query-port flag is ignored for any servers which have a
source address specified but the port may be specified directly as
part of the source address.
.TP
.B \-A, --address=/<domain>/[domain/]<ipaddr>
Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to
with the specified IP address which may be IPv4 or IPv6. To give
both IPv4 and IPv6 addresses for a domain, use repeated -A flags.
Note that /etc/hosts and DHCP leases override this for individual
names. A common use of this is to redirect the entire doubleclick.net
domain to some friendly local web server to avoid banner ads. The
domain specification works in the same was as for --server, with the
additional facility that /#/ matches any domain. Thus
--address=/#/1.2.3.4 will always return 1.2.3.4 for any query not
answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive.
.TP
.B \-m, --mx-host=<mx name>[,<hostname>]
Return an MX record named <mx name> pointing to the given hostname (if
given), or
the host specified in the --mx-target switch
or, if that switch is not given, the host on which dnsmasq
is running. This is useful for directing mail from systems on a LAN
to a central server.
.TP
.B \-t, --mx-target=<hostname>
Specify target for the MX record returned by dnsmasq. See --mx-host. Note that to turn on the MX function,
at least one of --mx-host and --mx-target must be set. If only one of --mx-host and --mx-target
is set, the other defaults to the hostname of the machine on which dnsmasq is running.
.TP
.B \-e, --selfmx
Return an MX record pointing to itself for each local
machine. Local machines are those in /etc/hosts or with DHCP leases.
.TP
.B \-L, --localmx
Return an MX record pointing to the host given by mx-target (or the
machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP
leases.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
.TP
.B \-N, --no-negcache
Disable negative caching. Negative caching allows dnsmasq to remember
"no such domain" answers from upstream nameservers and answer
identical queries without forwarding them again. This flag disables
negative caching.
.TP
.B \-F, --dhcp-range=[network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
in
.B dhcp-host
options. If the lease time is given, then leases
will be given for that length of time. The lease time is on seconds,
or minutes (eg 45m) or hours (eg 1h) or the literal "infinite". This
option may be repeated, with different addresses, to enable DHCP
service to more than one network. For directly connected networks (ie,
networks on which the machine running dnsmasq has an interface) the
netmask is optional. It is, however, required for networks which
recieve DHCP service via a relay agent. The broadcast address is
always optional. On some broken systems, dnsmasq can listen on only
one interface when using DHCP, and the name of that interface must be
given using the
.B interface
option. This limitation currently affects OpenBSD. The optional
network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis. The end address
may be replaced by the keyword
.B static
which tells dnsmasq to enable DHCP for the network specified, but not
to dynamically allocate IP addresses. Only hosts which have static
addresses given via
.B dhcp-host
or from /etc/ethers will be served.
.TP
.B \-G, --dhcp-host=[[<hwaddr>]|[id:[<client_id>][*]]][net:<netid>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this
overrides any supplied by the DHCP client on the machine. It is also
allowable to ommit the hardware address and include the hostname, in
which case the IP address and lease times will apply to any machine
claiming that name. For example
.B --dhcp-host=00:20:e0:3b:13:af,wap,infinite
tells dnsmasq to give
the machine with ethernet address 00:20:e0:3b:13:af the name wap, and
an infinite DHCP lease.
.B --dhcp-host=lap,192.168.0.199
tells
dnsmasq to always allocate the machine lap the IP address
192.168.0.199. Addresses allocated like this are not constrained to be
in the range given by the --dhcp-range option, but they must be on the
network being served by the DHCP server. It is allowed to use client identifiers rather than
hardware addresses to identify hosts by prefixing with 'id:'. Thus:
.B --dhcp-host=id:01:02:03:04,.....
refers to the host with client identifier 01:02:03:04. It is also
allowed to specify the client ID as text, like this:
.B --dhcp-host=id:clientidastext,.....
The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes
but not others.
If a name appears in /etc/hosts, the associated address can be
allocated to a DHCP lease, but only if a
.B --dhcp-host
option specifying the name also exists. The special keyword "ignore"
tells dnsmasq to never offer a DHCP lease to a machine. The machine
can be specified by hardware address, client ID or hostname, for
instance
.B --dhcp-host=00:20:e0:3b:13:af,ignore
This is
useful when there is another DHCP server on the network which should
be used by some machines. The net:<network-id> parameter enables DHCP options just
for this host in the same way as the the network-id in
.B dhcp-range.
.TP
.B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The
format of /etc/ethers is a hardware address, followed by either a
hostname or dotted-quad IP address. When read by dnsmasq these lines
have exactly the same effect as
.B --dhcp-host
options containing the same information.
.TP
.B \-O, --dhcp-option=[network-id,]<opt>,[<value>[,<value>]]
Specfify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and
the DNS server and default route are set to the address of the machine
running dnsmasq. If the domain name option has been set, that is sent.
This option allows these defaults to be overridden,
or other options specified. The <opt> is the number of the option, as
specfied in RFC2132. For example, to set the default route option to
192.168.4.4, do
.B --dhcp-option=3,192.168.4.4
and to set the time-server address to 192.168.0.4, do
.B --dhcp-option=42,192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma seperated
dotted-quad IP addresses, a decimal number, colon-seperated hex digits
and a text string. If the optional network-id is given then
this option is only sent to machines on the network whose dhcp-range
contains a matching network-id.
Be careful: no checking is done that the correct type of data for the
option number is sent, and there are option numbers for which it is not
possible to generate the correct data type; it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
of this flag.
.TP
.B \-U, --dhcp-vendorclass=<network-id>,<vendor-class>
Map from a vendor-class string to a network id. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option
maps vendor classes to network ids, so that DHCP options may be selectively delivered
to different classes of hosts. For example
.B dhcp-vendorclass=printers,Hewlett-Packard JetDirect
will allow options to be set only for HP printers like so:
.B --dhcp-option=printers,3,192.168.4.4
The vendor-class string is
substring matched against the vendor-class supplied by the client, to
allow fuzzy matching.
.TP
.B \-j, --dhcp-userclass=<network-id>,<user-class>
Map from a user-class string to a network id (with substring
matching, like vendor classes). Most DHCP clients provide a
"user class" which is configurable. This option
maps user classes to network ids, so that DHCP options may be selectively delivered
to different classes of hosts. It is possible, for instance to use
this to set a different printer server for hosts in the class
"accounts" than for hosts in the class "engineering".
.TP
.B \-M, --dhcp-boot=<filename>,[<servername>[,<server address>]]
Set BOOTP options to be returned by the DHCP server. These are needed
for machines which network boot, and tell the machine where to collect
its initial configuration.
.TP
.B \-X, --dhcp-lease-max=<number>
Limits dnsmasq to the specified maximum number of DHCP leases. The
default is 150. This limit is to prevent DoS attacks from hosts which
create thousands of leases and use lots of memory in the dnsmasq
process.
.TP
.B \-K, --dhcp-authoritative
Should be set when dnsmasq is definatively the only DHCP server on a network.
It changes the behaviour from strict RFC compliance so that DHCP requests on
unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances.
.TP
.B \-l, --dhcp-leasefile=<path>
Use the specified file to store DHCP lease information. If this option
is given but no dhcp-range option is given then dnsmasq version 1
behaviour is activated. The file given is assumed to be an ISC dhcpd
lease file and parsed for leases which are then added to the DNS
system if they have a hostname. This functionality may have been
excluded from dnsmasq at compile time, in which case an error will occur.
.TP
.B \-s, --domain=<domain>
Specifies the domain for the DHCP server. This has two effects;
firstly it causes the DHCP server to return the domain to any hosts
which request it, and secondly it sets the domain which it is legal
for DHCP-configured hosts to claim. The intention is to constrain hostnames so that an untrusted host on the LAN cannot advertise it's name via dhcp as e.g. "microsoft.com" and capture traffic not meant for it. If no domain suffix is specified, then any DHCP hostname with a domain part (ie with a period) will be disallowed and logged. If suffix is specified, then hostnames with a domain part are allowed, provided the domain part matches the suffix. In addition, when a suffix is set then hostnames without a domain part have the suffix added as an optional domain part. Eg on my network I can set
.B --domain-suffix=thekelleys.org.uk
and have a machine whose DHCP hostname is "laptop". The IP address for that machine is available from
.B dnsmasq
both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
given as "#" then the domain is read from the first "search" directive
in /etc/resolv.conf (or equivalent).
.TP
.B \-E, --expand-hosts
Add the domain-suffix to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names.
.SH CONFIG FILE
At startup, dnsmasq reads
.I /etc/dnsmasq.conf,
if it exists. (On
FreeBSD, the file is
.I /usr/local/etc/dnsmasq.conf
) The format of this
file consists of one option per line, exactly as the long options detailed
in the OPTIONS section but without the leading "--". Lines starting with # are comments and ignored. For
options which may only be specified once, the configuration file overrides
the command line. Use the --conf-file option to specify a different
configuration file. The conf-file option is also allowed in
configuration files, to include multiple configuration files. Only one
level of nesting is allowed.
.SH NOTES
When it receives a SIGHUP,
.B dnsmasq
clears its cache and then re-loads
.I /etc/hosts.
If
.B
--no-poll
is set SIGHUP also re-reads
.I /etc/resolv.conf.
SIGHUP
does NOT re-read the configuration file.
.PP
When it receives a SIGUSR1,
.B dnsmasq
writes cache statistics to the system log. It writes the cache size,
the number of names which have had to removed from the cache before
they expired in order to make room for new names and the total number
of names that have been inserted into the cache. In
.B --no-daemon
mode or when full logging is enabled (-q), a complete dump of the contents of the cache is made.
.PP
Dnsmasq is a DNS query forwarder: it it not capable of recursively
answering arbitrary queries starting from the root servers but
forwards such queries to a fully recursive upstream DNS server which is
typically provided by an ISP. By default, dnsmasq reads
.I /etc/resolv.conf
to discover the IP
addresses of the upstream nameservers it should use, since the
information is typically stored there. Unless
.B --no-poll
is used,
.B dnsmasq
checks the modification time of
.I /etc/resolv.conf
(or equivalent if
.B \--resolv-file
is used) and re-reads it if it changes. This allows the DNS servers to
be set dynamically by PPP or DHCP since both protocols provide the
information.
Absence of
.I /etc/resolv.conf
is not an error
since it may not have been created before a PPP connection exists. Dnsmasq
simply keeps checking in case
.I /etc/resolv.conf
is created at any
time. Dnsmasq can be told to parse more than one resolv.conf
file. This is useful on a laptop, where both PPP and DHCP may be used:
dnsmasq can be set to poll both
.I /etc/ppp/resolv.conf
and
.I /etc/dhcpc/resolv.conf
and will use the contents of whichever changed
last, giving automatic switching between DNS servers.
.PP
Upstream servers may also be specified on the command line or in
the configuration file. These server specifications optionally take a
domain name which tells dnsmasq to use that server only to find names
in that particular domain.
.PP
In order to configure dnsmasq to act as cache for the host on which it is running, put "nameserver 127.0.0.1" in
.I /etc/resolv.conf
to force local processes to send queries to
dnsmasq. Then either specify the upstream servers directly to dnsmasq
using
.B \--server
options or put their addresses real in another file, say
.I /etc/resolv.dnsmasq
and run dnsmasq with the
.B \-r /etc/resolv.dnsmasq
option. This second technique allows for dynamic update of the server
addresses by PPP or DHCP.
.PP
The DHCP server in dnsmasq will function as a BOOTP server also,
provided that the MAC address and IP address for clients are given,
either using
.B dhcp-host
configurations or in
.I /etc/ethers
, and a
.B dhcp-range
configuration option is present to activate the DHCP server
on a particular network. The filename
parameter in a BOOTP request is matched against netids in
.B dhcp-option
configurations, allowing some control over the options returned to
different classes of hosts.
.SH FILES
.IR /etc/dnsmasq.conf
.IR /usr/local/etc/dnsmasq.conf
.IR /etc/resolv.conf
.IR /etc/hosts
.IR /etc/ethers
.IR /var/lib/misc/dnsmasq.leases
.IR /var/db/dnsmasq.leases
.IR /var/run/dnsmasq.pid
.SH SEE ALSO
.BR hosts (5),
.BR resolver (5)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -4,34 +4,28 @@
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Change these lines if you want dnsmasq to serve MX records.
# Only one of mx-host and mx-target need be set, the other defaults
# to the name of the host running dnsmasq.
#mx-host=
#mx-target=
#selfmx
#localmx
# The following two options make you a better netizen, since they
# 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)
# 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.
# Never forward plain names (with a dot or domain part)
domain-needed
# Never forward plain names (without a dot or domain part)
#domain-needed
# Never forward addresses in the non-routed address spaces.
bogus-priv
#bogus-priv
# Uncomment this to filter useless windows-originated DNS requests
# which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests,
# Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k
# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
# somewhere other that /etc/resolv.conf
#resolv-file=
# By default, dnsmasq will send queries to any of the upstream
@@ -42,18 +36,22 @@ bogus-priv
#strict-order
# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers for this file instead (see below), then
# uncomment this
# file, getting its servers from this file instead (see below), then
# uncomment this.
#no-resolv
# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
# files for changes and re-read them then uncomment this.
#no-poll
# Add other name servers here, with domain specs if they are for
# Add other name servers here, with domain specs if they are for
# non-public domains.
#server=/localnet/192.168.0.1
# 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
# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.
#local=/localnet/
@@ -63,14 +61,14 @@ bogus-priv
# webserver.
#address=/doubleclick.net/127.0.0.1
# You no longer (as of version 1.7) need to set these to enable
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
# "dip" group to achieve this.
# If you want dnsmasq to change uid and gid to something other
# than the default, edit the following lines.
#user=
#group=
# If you want dnsmasq to listen for requests only on specified interfaces
# (and the loopback) give the name of the interface (eg eth0) here.
# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
#interface=
# Or you can specify which interface _not_ to listen on
@@ -78,15 +76,19 @@ bogus-priv
# Or which to listen on by address (remember to include 127.0.0.1 if
# you use this.)
#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.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
#bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
@@ -107,16 +109,16 @@ bogus-priv
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one network, you will need to
# repeat this for each network on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
@@ -131,7 +133,7 @@ bogus-priv
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissble to give name,adddress and MAC in any order
# Always allocate the host with ethernet address 11:22:33:44:55:66
# 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
@@ -147,7 +149,7 @@ bogus-priv
# 192.168.0.70 and an infinite lease
#dhcp-host=bert,192.168.0.70,infinite
# Always give the host with client identifier 01:02:02:04
# Always give the host with client identifier 01:02:02:04
# the IP address 192.168.0.60
#dhcp-host=id:01:02:02:04,192.168.0.60
@@ -160,28 +162,42 @@ bogus-priv
# 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
# address 11:22:33:44:55:66. This is useful to prevent a machine
# 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
# 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
# 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
# Ignore any clients which are specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unkown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=#known
# Send extra options which are tagged as "red" to any machine whose
# DHCP vendorclass string includes the substring "Linux"
#dhcp-vendorclass=red,Linux
# Send extra options which are tagged as "red" to any machine one
# 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
# Send extra options which are tagged as "red" to any machine whose
# MAC address matches the pattern.
#dhcp-mac=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
# been given as --dhcp-host options. Useful if you keep
@@ -190,22 +206,32 @@ bogus-priv
# 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:
# 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 any
# 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.
# For reference, the common options are:
# subnet mask - 1
# default router - 3
# DNS server - 6
# broadcast address - 28
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
#dhcp-option=3,1.2.3.4
# Do the same thing, but using the option name
#dhcp-option=option:router,1.2.3.4
# 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
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=42,192.168.0.4,10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
# Set the NTP time server address to be the same machine as
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
@@ -222,17 +248,18 @@ bogus-priv
#dhcp-option=128,e4:45:74:68:00:00
#dhcp-option=129,NIC=eepro100
# Specify an option which will only be sent to the "red" network
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
#dhcp-option=red,42,192.168.1.1
# Note that the net: part must precede the option: part.
#dhcp-option = net: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
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment them if you use Windows clients and Samba.
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type
@@ -240,11 +267,63 @@ bogus-priv
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
#dhcp-option=119,eng.apple.com,marketing.apple.com
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
# Set the boot filename and tftpd server name and address
# for BOOTP. You will only need this is you want to
# boot machines over the network.
# 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.
# 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 "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
# Send microsoft-specific option to tell windows to release the DHCP lease
# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
# value as a four-byte integer - that's what microsoft wants. See
# http://technet2.microsoft.com/WindowsServer/en/library/a70f1bb7-d2d4-49f0-96d6-4b7414ecfaae1033.mspx?mfr=true
#dhcp-option=vendor:MSFT,2,1i
# Send the Encapsulated-vendor-class ID needed by some configurations of
# Etherboot to allow is to recognise the DHCP server.
#dhcp-option=vendor:Etherboot,60,"Etherboot"
# 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.
# 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
# Configuration file name
#dhcp-option-force=209,configs/common
# Path prefix
#dhcp-option-force=210,/tftpboot/pxelinux/files/
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for BOOTP. 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
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files availble 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
# 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
# address of the server are given after the filename.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
@@ -255,16 +334,22 @@ bogus-priv
# the line below.
#dhcp-leasefile=/var/lib/misc/dnsmasq.leases
# Set the DHCP server to authoritative mode. In this mode it will barge in
# and take over the lease for any client which broadcasts on the network,
# Set the DHCP server to authoritative mode. In this mode it will barge in
# 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
# 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
# server for your campus/company accidentally. The ISC server uses the same
# 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
#dhcp-authoritative
# Run an executable when a DHCP lease is created or destroyed.
# 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.
#dhcp-script=/bin/echo
# Set the cachesize here.
#cache-size=150
@@ -273,8 +358,8 @@ bogus-priv
# Normally responses which come form /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in
# seconds) here.
#local-ttl=
@@ -292,14 +377,76 @@ bogus-priv
# 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
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used. (Note that expand-hosts does not need to be
# set for this to work.)
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
#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=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,"v=spf1 a -all"
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4
# For debugging purposes, log each DNS query as it passes through
# dnsmasq.
#log-queries
# Log lots of extra information about DHCP transactions.
#log-dhcp
# Include a another lot of configuration options.
#conf-file=/etc/dnsmasq.more.conf
#conf-dir=/etc/dnsmasq.d

View File

@@ -4,26 +4,29 @@
</HEAD>
<BODY BGCOLOR="WHITE">
<H1 ALIGN=center>Dnsmasq</H1>
Dnsmasq is lightweight, easy to configure DNS forwarder and DHCP
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 for network booting of diskless machines.
DHCP leases and BOOTP/TFTP 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
connection but would be a good choice for any small network where low
connection but would be a good choice for any smallish network (up to
1000 clients is known to work) where low
resource use and ease of configuration are important.
<P>
Supported platforms include Linux (with glibc and uclibc), *BSD and
Mac OS X.
Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse,
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, CoyoteLinux and
Clarkconnect. It is also available as a FreeBSD port and is used in Linksys wireless routers.
Gentoo, Debian, Slackware, Suse, Fedora,
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, fli4l,
CoyoteLinux, Endian Firewall and
Clarkconnect. It is also available as FreeBSD, OpenBSD and NetBSD ports and is used in
Linksys wireless routers (dd-wrt, openwrt and the stock firmware) and the m0n0wall project.
<P>
Dnsmasq provides the following features:
<DIR>
@@ -41,22 +44,18 @@ machine: If the names of local machines are there, then they can all
be addressed without having to maintain /etc/hosts on each machine.
</LI>
<LI>
Dnsmasq will serve names from the DHCP leases file on the firewall machine:
If machines specify a hostname when they take out a DHCP lease, then they are
addressable in the local DNS. <B>UPDATE</B> Dnsmasq version 2 now offers an integrated DHCP server
instead of the lease file reader. This gives better control of the
interaction with new functions (for example fixed IP leasess and
attaching names to ethernet addresses centrally) it's also much
smaller than dnsmasq and ISC dhcpd which is important for router distros.
The integrated DHCP server supports static and dynamic DHCP leases and
multiple networks and IP ranges. It works across BOOTP relays and
supports DHCP options including RFC3397 DNS search lists.
Machines which are configured by DHCP have their names automatically
included in the DNS and the names can specified by each machine or
centrally by associating a name with a MAC address in the dnsmasq
config file.
</LI>
<LI>
Dnsmasq caches internet addresses (A records and AAAA records) and address-to-name
mappings (PTR records), reducing the load on upstream servers and
improving performance (especially on modem connections). From version
0.95 the cache honours time-to-live information and removes old
records as they expire. From version 0.996 dnsmasq does negative
caching. From version 1.2 dnsmasq supports IPv6 addresses, both
in its cache and in /etc/hosts.
improving performance (especially on modem connections).
</LI>
<LI>
Dnsmasq can be configured to automatically pick up the addresses of
@@ -76,51 +75,36 @@ upstream servers handling only those domains. This makes integration
with private DNS systems easy.
</LI>
<LI>
Dnsmasq can be configured to return an MX record
for the firewall host. This makes it easy to configure the mailer on the local
machines to forward all mail to the central mailer on the firewall host. Never
lose root messages from your machines again!
</LI>
<LI>
For version 1.15 dnsmasq has a facility to work around Verisign's infamous wildcard A record
in the .com and .net TLDs
Dnsmasq supports MX and SRV records and can be configured to return MX records
for any or all local machines.
</LI>
</DIR>
<H2>Download.</H2>
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
The tarball includes this documentation, source, manpage and control files for building .rpms.
There are also pre-built i386 .rpms, and a
<A HREF="CHANGELOG"> CHANGELOG</A>.
The tarball includes this documentation, source, and manpage.
There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>.
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>Building rpms.</H2>
Assuming you have the relevant tools installed, you can rebuild .rpms simply by running (as root)
<PRE>
rpmbuild -ta dnsmasq-xxx.tar.gz
</PRE>
Note for Suse users: you will need to re-compress the tar file as
bzip2 before building using the commands
<PRE>
gunzip dnsmasq-xxx.tar.gz
bzip2 dnsmasq-zzz.tar
</PRE>
<H2>Links.</H2>
Ulrich Ivens has a nice HOWTO in German on installing dnsmasq at <A
HREF="http://howto.linux-hardware-shop.de/dnsmasq.html">http://howto.linux-hardware-shop.de/dnsmasq.html</A>
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>
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>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details.
<H2>Contact.</H2>
Dnsmasq was written by Simon Kelley. You can contact me at <A HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>. Bugreports, patches, and suggestions for improvements gratefully accepted.
There is a dnsmasq mailing list at <A
HREF="http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss">
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss</A> which should be the
first location for queries, bugreports, suggestions etc.
Dnsmasq was written by Simon Kelley. You can contact me at <A
HREF="mailto:simon@thekelleys.org.uk">simon@thekelleys.org.uk</A>.
</BODY>

1042
man/dnsmasq.8 Normal file

File diff suppressed because it is too large Load Diff

1089
man/es/dnsmasq.8 Normal file

File diff suppressed because it is too large Load Diff

1279
po/de.po Normal file

File diff suppressed because it is too large Load Diff

1292
po/es.po Normal file

File diff suppressed because it is too large Load Diff

1227
po/fi.po Normal file

File diff suppressed because it is too large Load Diff

1329
po/fr.po Normal file

File diff suppressed because it is too large Load Diff

1512
po/id.po Normal file

File diff suppressed because it is too large Load Diff

1227
po/it.po Normal file

File diff suppressed because it is too large Load Diff

1297
po/no.po Normal file

File diff suppressed because it is too large Load Diff

1304
po/pl.po Normal file

File diff suppressed because it is too large Load Diff

1227
po/pt_BR.po Normal file

File diff suppressed because it is too large Load Diff

1298
po/ro.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +0,0 @@
--- dnsmasq.8 2004-08-08 20:57:56.000000000 +0200
+++ dnsmasq.8 2004-08-12 00:40:01.000000000 +0200
@@ -69,7 +69,7 @@
.TP
.B \-g, --group=<groupname>
Specify the group which dnsmasq will run
-as. The defaults to "dip", if available, to facilitate access to
+as. The defaults to "dialout", if available, to facilitate access to
/etc/ppp/resolv.conf which is not normally world readable.
.TP
.B \-v, --version
--- dnsmasq.conf.example 2004-08-08 21:18:26.000000000 +0200
+++ dnsmasq.conf.example 2004-08-12 00:40:01.000000000 +0200
@@ -65,7 +65,7 @@
# You no longer (as of version 1.7) need to set these to enable
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
-# "dip" group to achieve this.
+# "dialout" group to achieve this.
#user=
#group=
--- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
-#define CHGRP "dip"
+#define CHGRP "dialout"
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
@@ -187,7 +187,7 @@
/* platform independent options. */
#undef HAVE_BROKEN_RTC
-#define HAVE_ISC_READER
+#undef HAVE_ISC_READER
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC

View File

@@ -1,93 +0,0 @@
#!/bin/sh
#
# Startup script for the DNS caching server
#
# chkconfig: 2345 99 01
# description: This script starts your DNS caching server
# processname: dnsmasq
# pidfile: /var/run/dnsmasq.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
dnsmasq=/usr/sbin/dnsmasq
[ -f $dnsmasq ] || exit 0
# change this line if you want dnsmasq to serve an MX record for
# the host it is running on.
MAILHOSTNAME=""
# change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
RESOLV_CONF=""
# change this if you want dnsmasq to cache any "hostname" or "client-hostname" from
# a dhcpd's lease file
DHCP_LEASE="/var/lib/dhcp/dhcpd.leases"
DOMAIN_SUFFIX=`dnsdomainname`
OPTIONS=""
if [ ! -z "${MAILHOSTNAME}" ]; then
OPTIONS="$OPTIONS -m $MAILHOSTNAME"
fi
if [ ! -z "${RESOLV_CONF}" ]; then
OPTIONS="$OPTIONS -r $RESOLV_CONF"
fi
if [ ! -z "${DHCP_LEASE}" ]; then
OPTIONS="$OPTIONS -l $DHCP_LEASE"
fi
if [ ! -z "${DOMAIN_SUFFIX}" ]; then
OPTIONS="$OPTIONS -s $DOMAIN_SUFFIX"
fi
RETVAL=0
# See how we were called.
case "$1" in
start)
echo -n "Starting dnsmasq: "
daemon $dnsmasq $OPTIONS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/dnsmasq
;;
stop)
if test "x`pidof dnsmasq`" != x; then
echo -n "Shutting down dnsmasq: "
killproc dnsmasq
fi
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/dnsmasq /var/run/dnsmasq.pid
;;
status)
status dnsmasq
RETVAL=$?
;;
restart|reload)
$0 stop
$0 start
RETVAL=$?
;;
condrestart)
if test "x`/sbin/pidof dnsmasq`" != x; then
$0 stop
$0 start
RETVAL=$?
fi
;;
*)
echo "Usage: $0 {start|stop|restart|reload|condrestart|status}"
exit 1
esac
exit $RETVAL

View File

@@ -1,16 +0,0 @@
# Uncomment this on Solaris.
#LIBS = -lsocket -lnsl
CFLAGS?= -O2
OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o \
network.o dnsmasq.o dhcp.o lease.o rfc2131.o
.c.o: dnsmasq.h config.h
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c
dnsmasq : $(OBJS) dnsmasq.h config.h
$(CC) -o $@ $(OBJS) $(LIBS)

239
src/bpf.c Normal file
View File

@@ -0,0 +1,239 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "dnsmasq.h"
#ifndef HAVE_LINUX_NETWORK
#include <net/bpf.h>
static struct iovec ifconf = {
.iov_base = NULL,
.iov_len = 0
};
static struct iovec ifreq = {
.iov_base = NULL,
.iov_len = 0
};
void init_bpf(void)
{
int i = 0;
while (1)
{
/* useful size which happens to be sufficient */
if (expand_buf(&ifreq, sizeof(struct ifreq)))
{
sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
return;
}
if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
}
}
void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr)
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
struct ether_header ether;
struct ip ip;
struct udphdr {
u16 uh_sport; /* source port */
u16 uh_dport; /* destination port */
u16 uh_ulen; /* udp length */
u16 uh_sum; /* udp checksum */
} udp;
u32 i, sum;
struct iovec iov[4];
/* 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"),
mess->htype, ifr->ifr_name);
return;
}
ifr->ifr_addr.sa_family = AF_LINK;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
return;
memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
ether.ether_type = htons(ETHERTYPE_IP);
if (ntohs(mess->flags) & 0x8000)
{
memset(ether.ether_dhost, 255, ETHER_ADDR_LEN);
ip.ip_dst.s_addr = INADDR_BROADCAST;
}
else
{
memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN);
ip.ip_dst.s_addr = mess->yiaddr.s_addr;
}
ip.ip_p = IPPROTO_UDP;
ip.ip_src.s_addr = iface_addr.s_addr;
ip.ip_len = htons(sizeof(struct ip) +
sizeof(struct udphdr) +
len) ;
ip.ip_hl = sizeof(struct ip) / 4;
ip.ip_v = IPVERSION;
ip.ip_tos = 0;
ip.ip_id = htons(0);
ip.ip_off = htons(0x4000); /* don't fragment */
ip.ip_ttl = IPDEFTTL;
ip.ip_sum = 0;
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
sum += ((u16 *)&ip)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
udp.uh_sport = htons(DHCP_SERVER_PORT);
udp.uh_dport = htons(DHCP_CLIENT_PORT);
if (len & 1)
((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
udp.uh_sum = 0;
udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
sum += htons(IPPROTO_UDP);
for (i = 0; i < 4; i++)
sum += ((u16 *)&ip.ip_src)[i];
for (i = 0; i < sizeof(struct udphdr)/2; i++)
sum += ((u16 *)&udp)[i];
for (i = 0; i < (len + 1) / 2; i++)
sum += ((u16 *)mess)[i];
while (sum>>16)
sum = (sum & 0xffff) + (sum >> 16);
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
iov[0].iov_base = &ether;
iov[0].iov_len = sizeof(ether);
iov[1].iov_base = &ip;
iov[1].iov_len = sizeof(ip);
iov[2].iov_base = &udp;
iov[2].iov_len = sizeof(udp);
iov[3].iov_base = mess;
iov[3].iov_len = len;
while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
}
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{
char *ptr;
struct ifreq *ifr;
struct ifconf ifc;
int fd, errsav, ret = 0;
int lastlen = 0;
size_t len = 0;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
return 0;
while(1)
{
len += 10*sizeof(struct ifreq);
if (!expand_buf(&ifconf, len))
goto err;
ifc.ifc_len = len;
ifc.ifc_buf = ifconf.iov_base;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
{
if (errno != EINVAL || lastlen != 0)
goto err;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
}
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len; ptr += len )
{
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
#ifdef HAVE_SOCKADDR_SA_LEN
len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
#else
len = sizeof(struct ifreq);
#endif
if (!expand_buf(&ifreq, len))
goto err;
ifr = ifreq.iov_base;
memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
{
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))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
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;
err:
errsav = errno;
close(fd);
errno = errsav;
return ret;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -12,80 +12,165 @@
#include "dnsmasq.h"
static struct crec *cache_head, *cache_tail, **hash_table;
static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
static int cache_inserted, cache_live_freed, insert_error;
static union bigname *big_free;
static int bignames_left, log_queries, cache_size, hash_size;
static int index;
static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
static struct crec *dhcp_spare = NULL, *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 {
unsigned int type;
const char * const name;
} typestr[] = {
{ 1, "A" },
{ 2, "NS" },
{ 5, "CNAME" },
{ 6, "SOA" },
{ 10, "NULL" },
{ 11, "WKS" },
{ 12, "PTR" },
{ 13, "HINFO" },
{ 15, "MX" },
{ 16, "TXT" },
{ 22, "NSAP" },
{ 23, "NSAP_PTR" },
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 48, "DNSKEY" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
};
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
static void cache_link(struct crec *crecp);
static char *record_source(struct hostsfile *add_hosts, int index);
static void rehash(int size);
static void cache_hash(struct crec *crecp);
void cache_init(int size, int logq)
void cache_init(void)
{
struct crec *crecp;
int i;
log_queries = logq;
cache_head = cache_tail = NULL;
dhcp_inuse = dhcp_spare = NULL;
new_chain = NULL;
cache_size = size;
big_free = NULL;
bignames_left = size/10;
index = 0;
cache_inserted = cache_live_freed = 0;
if (cache_size > 0)
if (daemon->options & OPT_LOG)
addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize/10;
if (daemon->cachesize > 0)
{
crecp = safe_malloc(size*sizeof(struct crec));
crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
for (i=0; i<size; i++, crecp++)
for (i=0; i < daemon->cachesize; i++, crecp++)
{
cache_link(crecp);
crecp->flags = 0;
crecp->uid = index++;
crecp->uid = uid++;
}
}
/* hash_size is a power of two. */
for (hash_size = 64; hash_size < cache_size/10; hash_size = hash_size << 1);
hash_table = safe_malloc(hash_size*sizeof(struct crec *));
for(i=0; i < hash_size; i++)
hash_table[i] = NULL;
/* create initial hash table*/
rehash(daemon->cachesize);
}
static struct crec **hash_bucket(unsigned char *name)
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
expand the table. */
static void rehash(int size)
{
unsigned int c, val = 0;
struct crec **new, **old, *p, *tmp;
int i, new_size, old_size;
/* hash_size is a power of two. */
for (new_size = 64; new_size < size/10; new_size = new_size << 1);
/* don't use tolower and friends here - they may be messed up by LOCALE */
while((c = *name++))
if (c >= 'A' && c <= 'Z')
val += c + 'a' - 'A';
else
val += c;
/* must succeed in getting first instance, failure later is non-fatal */
if (!hash_table)
new = safe_malloc(new_size * sizeof(struct crec *));
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
return;
for(i = 0; i < new_size; i++)
new[i] = NULL;
old = hash_table;
old_size = hash_size;
hash_table = new;
hash_size = new_size;
if (old)
{
for (i = 0; i < old_size; i++)
for (p = old[i]; p ; p = tmp)
{
tmp = p->hash_next;
cache_hash(p);
}
free(old);
}
}
static struct crec **hash_bucket(char *name)
{
unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
const unsigned char *mix_tab = (const unsigned char*)typestr;
while((c = (unsigned char) *name++))
{
/* don't use tolower and friends here - they may be messed up by LOCALE */
if (c >= 'A' && c <= 'Z')
c += 'a' - 'A';
val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
}
/* hash_size is a power of two */
return hash_table + (val & (hash_size - 1));
return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
}
static void cache_hash(struct crec *crecp)
{
struct crec **bucket = hash_bucket(cache_get_name(crecp));
crecp->hash_next = *bucket;
*bucket = crecp;
/* maintain an invariant that all entries with F_REVERSE set
are at the start of the hash-chain and all non-reverse
immortal entries are at the end of the hash-chain.
This allows reverse searches and garbage collection to be optimised */
struct crec **up = hash_bucket(cache_get_name(crecp));
if (!(crecp->flags & F_REVERSE))
{
while (*up && ((*up)->flags & F_REVERSE))
up = &((*up)->hash_next);
if (crecp->flags & F_IMMORTAL)
while (*up && !((*up)->flags & F_IMMORTAL))
up = &((*up)->hash_next);
}
crecp->hash_next = *up;
*up = crecp;
}
static void cache_free(struct crec *crecp)
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
crecp->uid = index++; /* invalidate CNAMES pointing to this. */
crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
if (cache_tail)
cache_tail->next = crecp;
@@ -142,46 +227,64 @@ char *cache_get_name(struct crec *crecp)
static int is_outdated_cname_pointer(struct crec *crecp)
{
struct crec *target = crecp->addr.cname.cache;
if (!(crecp->flags & F_CNAME))
return 0;
if (!target)
return 1;
if (crecp->addr.cname.uid == target->uid)
if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
return 0;
return 1;
}
static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
static int is_expired(time_t now, struct crec *crecp)
{
if (crecp->flags & F_IMMORTAL)
return 0;
if (difftime(now, crecp->ttd) < 0)
return 0;
return 1;
}
static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
{
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
entries but only in the same hash bucket as name.
If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache. */
#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6 | F_CNAME)
struct crec *crecp, **up;
flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4 | F_CNAME);
If (flags == 0) remove any expired entries in the whole cache.
In the flags & F_FORWARD case, the return code is valid, and returns zero if the
name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
so that when we hit an entry which isn't reverse and is immortal, we're done. */
struct crec *crecp, **up;
if (flags & F_FORWARD)
{
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
is_outdated_cname_pointer(crecp) ||
((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
{
if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
{
{
cache_unlink(crecp);
cache_free(crecp);
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else
up = &crecp->hash_next;
@@ -195,9 +298,10 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
int addrlen = INADDRSZ;
#endif
for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
for (crecp = hash_table[i], up = &hash_table[i];
crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
crecp = crecp->hash_next)
if (is_expired(now, crecp))
{
*up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -206,9 +310,20 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
cache_free(crecp);
}
}
else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
{
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else
up = &crecp->hash_next;
}
return 1;
}
/* Note: The normal calling sequence is
@@ -237,21 +352,12 @@ void cache_start_insert(void)
struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags)
{
#ifdef HAVE_IPV6
int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
#else
int addrlen = INADDRSZ;
#endif
struct crec *new;
union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE;
log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
/* name is needed as workspace by log_query in this case */
if ((flags & F_NEG) && (flags & F_REVERSE))
name = NULL;
/* CONFIG bit no needed except for logging */
flags &= ~F_CONFIG;
@@ -260,8 +366,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
return NULL;
/* First remove any expired entries and entries for the name/address we
are currently inserting. */
cache_scan_free(name, addr, now, flags);
are currently inserting. Fail is we attempt to delete a name from
/etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags))
{
insert_error = 1;
return NULL;
}
/* Now get a cache entry from the end of the LRU list */
while (1) {
@@ -300,7 +411,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
big_free = big_free->next;
}
else if (!bignames_left ||
!(big_name = (union bigname *)malloc(sizeof(union bigname))))
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{
insert_error = 1;
return NULL;
@@ -321,12 +432,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
new->name.bname = big_name;
new->flags |= F_BIGNAME;
}
if (name)
strcpy(cache_get_name(new), name);
else
*cache_get_name(new) = 0;
if (addr)
memcpy(&new->addr.addr, addr, addrlen);
new->addr.addr = *addr;
else
new->addr.cname.cache = NULL;
@@ -376,8 +489,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
{
next = crecp->hash_next;
if (!is_outdated_cname_pointer(crecp) &&
((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
(crecp->flags & prot) &&
@@ -452,16 +564,19 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
else
{
/* first search, look for relevant entries and push to top of list
also free anything which has expired */
also free anything which has expired. All the reverse entries are at the
start of the hash chain, so we can give up when we find the first
non-REVERSE one. */
int i;
struct crec **up, **chainp = &ans;
for(i=0; i<hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
for (i=0; i<hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i];
crecp && (crecp->flags & F_REVERSE);
crecp = crecp->hash_next)
if (!is_expired(now, crecp))
{
if ((crecp->flags & F_REVERSE) &&
(crecp->flags & prot) &&
if ((crecp->flags & prot) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
{
if (crecp->flags & (F_HOSTS | F_DHCP))
@@ -500,19 +615,42 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
}
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
unsigned short flags, int index)
unsigned short flags, int index, int addr_dup)
{
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
int i;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
free(cache);
else
{
/* Ensure there is only one address -> name mapping (first one trumps) */
if (cache_find_by_addr(NULL, addr, 0, flags & (F_IPV4 | F_IPV6)))
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as
the last one we saw, which eliminates most in the case of an ad-block
file with thousands of entries for the same address.
Then we search and bail at the first matching address that came from
a HOSTS file. Since the first host entry gets reverse, we know
then that it must exist without searching exhaustively for it. */
if (addr_dup)
flags &= ~F_REVERSE;
else
for (i=0; i<hash_size; i++)
{
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
if ((lookup->flags & F_HOSTS) &&
(lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
break;
}
if (lookup)
break;
}
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
@@ -520,37 +658,37 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
}
}
static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index)
static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index, int cache_size)
{
FILE *f = fopen(filename, "r");
char *line;
int count = 0, lineno = 0;
int addr_count = 0, name_count = cache_size, lineno = 0;
unsigned short flags, saved_flags = 0;
struct all_addr addr, saved_addr;
if (!f)
{
syslog(LOG_ERR, "failed to load names from %s: %m", filename);
return;
my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
return 0;
}
while ((line = fgets(buff, MAXDNAME, f)))
{
struct all_addr addr;
char *token = strtok(line, " \t\n\r");
int addrlen;
unsigned short flags;
int addrlen, addr_dup = 0;
lineno++;
if (!token || (*token == '#'))
continue;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, token, &addr) == 1)
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
}
else if (inet_pton(AF_INET6, token, &addr) == 1)
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
@@ -563,45 +701,73 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
}
#endif
else
continue;
{
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
continue;
}
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else
{
saved_flags = flags;
saved_addr = addr;
}
addr_count++;
/* rehash every 1000 names. */
if ((name_count - cache_size) > 1000)
{
rehash(name_count);
cache_size = name_count;
}
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
{
struct crec *cache;
int fqdn = !!strchr(token, '.');
if (canonicalise(token))
{
count++;
/* If set, add a version of the name with a default domain appended */
if ((opts & OPT_EXPAND) && domain_suffix && !strchr(token, '.') &&
(cache = malloc(sizeof(struct crec) +
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
if ((opts & OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
addr_dup = 1;
name_count++;
}
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
if ((cache = whine_malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
add_hosts_entry(cache, &addr, addrlen, flags, index);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
name_count++;
}
}
else
syslog(LOG_ERR, "bad name at %s line %d", filename, lineno);
my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
}
}
fclose(f);
rehash(name_count);
syslog(LOG_INFO, "read %s - %d addresses", filename, count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
return name_count;
}
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
{
struct crec *cache, **up, *tmp;
int i;
int i, total_size = daemon->cachesize;
cache_inserted = cache_live_freed = 0;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{
@@ -627,44 +793,38 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
if ((opts & OPT_NO_HOSTS) && !addn_hosts)
{
if (cache_size > 0)
syslog(LOG_INFO, "cleared cache");
if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache"));
return;
}
if (!(opts & OPT_NO_HOSTS))
read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
total_size = read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0, total_size);
while (addn_hosts)
{
read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index);
total_size = read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index, total_size);
addn_hosts = addn_hosts->next;
}
}
void cache_unhash_dhcp(void)
{
struct crec *tmp, *cache, **up;
struct crec *cache, **up;
int i;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
if (cache->flags & F_DHCP)
*up = cache->hash_next;
{
*up = cache->hash_next;
cache->next = dhcp_spare;
dhcp_spare = cache;
}
else
up = &cache->hash_next;
/* prev field links all dhcp entries */
for (cache = dhcp_inuse; cache; cache = tmp)
{
tmp = cache->prev;
cache->prev = dhcp_spare;
dhcp_spare = cache;
}
dhcp_inuse = NULL;
}
void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
void cache_add_dhcp_entry(char *host_name,
struct in_addr *host_address, time_t ttd)
{
struct crec *crec;
@@ -673,23 +833,23 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
if (!host_name)
return;
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4)))
if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4 | F_CNAME)))
{
if (crec->flags & F_HOSTS)
{
if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
{
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
syslog(LOG_WARNING,
"not giving name %s to the DHCP lease of %s because"
"the name exists in %s with address %s",
host_name, inet_ntoa(*host_address),
record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
my_syslog(LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address),
record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
}
return;
}
else if (!(crec->flags & F_DHCP))
cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
}
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
@@ -702,9 +862,9 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
}
if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->prev;
dhcp_spare = dhcp_spare->next;
else /* need new one */
crec = malloc(sizeof(struct crec));
crec = whine_malloc(sizeof(struct crec));
if (crec) /* malloc may fail */
{
@@ -715,72 +875,69 @@ void cache_add_dhcp_entry(struct daemon *daemon, char *host_name,
crec->ttd = ttd;
crec->addr.addr.addr.addr4 = *host_address;
crec->name.namep = host_name;
crec->prev = dhcp_inuse;
dhcp_inuse = crec;
cache_hash(crec);
}
}
void dump_cache(struct daemon *daemon)
void dump_cache(time_t now)
{
syslog(LOG_INFO, "cache size %d, %d/%d cache insertions re-used unexpired cache entries.",
daemon->cachesize, cache_live_freed, cache_inserted);
my_syslog(LOG_INFO, _("time %lu, cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
(unsigned long)now, daemon->cachesize, cache_live_freed, cache_inserted);
if (daemon->options & (OPT_DEBUG | OPT_LOG))
if ((daemon->options & (OPT_DEBUG | OPT_LOG)) &&
(addrbuff || (addrbuff = whine_malloc(ADDRSTRLEN))))
{
struct crec *cache ;
char addrbuff[ADDRSTRLEN];
int i;
syslog(LOG_DEBUG, "Host Address Flags Expires\n");
my_syslog(LOG_DEBUG, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
char *a, *p = daemon->namebuff;
p += sprintf(p, "%-40.40s ", cache_get_name(cache));
if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
addrbuff[0] = 0;
a = "";
else if (cache->flags & F_CNAME)
{
addrbuff[0] = 0;
addrbuff[ADDRSTRLEN-1] = 0;
a = "";
if (!is_outdated_cname_pointer(cache))
strncpy(addrbuff, cache_get_name(cache->addr.cname.cache), ADDRSTRLEN);
a = cache_get_name(cache->addr.cname.cache);
}
#ifdef HAVE_IPV6
else if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
else
{
a = addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
}
#else
else
strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr.addr4));
a = inet_ntoa(cache->addr.addr.addr.addr4);
#endif
syslog(LOG_DEBUG,
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ");
#ifdef HAVE_BROKEN_RTC
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %ld\n",
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s",
p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
cache_get_name(cache), addrbuff,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ",
#ifdef HAVE_BROKEN_RTC
cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd
#else
cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))
#endif
);
}
}
my_syslog(LOG_DEBUG, daemon->namebuff);
}
}
}
static char *record_source(struct hostsfile *addn_hosts, int index)
@@ -802,45 +959,60 @@ static char *record_source(struct hostsfile *addn_hosts, int index)
void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index)
{
char *source;
char *source, *dest = addrbuff;
char *verb = "is";
char types[20];
char addrbuff[ADDRSTRLEN];
if (!log_queries)
return;
strcpy(types, " ");
if (!(daemon->options & OPT_LOG))
return;
if (addr)
{
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
#else
strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
}
if (flags & F_REVERSE)
{
dest = name;
name = addrbuff;
}
if (flags & F_NEG)
{
if (flags & F_REVERSE)
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, name, MAXDNAME);
#else
strcpy(name, inet_ntoa(addr->addr.addr4));
#endif
if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<NXDOMAIN>");
{
if (flags & F_IPV4)
dest = "NXDOMAIN-IPv4";
else
dest = "NXDOMAIN-IPv6";
}
else
strcpy(addrbuff, "<NODATA>");
if (flags & F_IPV4)
strcat(addrbuff, "-IPv4");
else if (flags & F_IPV6)
strcat(addrbuff, "-IPv6");
{
if (flags & F_IPV4)
dest = "NODATA-IPv4";
else
dest = "NODATA-IPv6";
}
}
else if (flags & F_CNAME)
strcpy(addrbuff, "<CNAME>");
else
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(addr->addr.addr4));
#endif
{
/* nasty abuse of IPV4 and IPV6 flags */
if (flags & F_IPV4)
dest = "<MX>";
else if (flags & F_IPV6)
dest = "<SRV>";
else if (flags & F_NXDOMAIN)
dest = "<TXT>";
else if (flags & F_BIGNAME)
dest = "<PTR>";
else
dest = "<CNAME>";
}
if (flags & F_DHCP)
source = "DHCP";
@@ -858,55 +1030,23 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
else if (flags & F_QUERY)
{
unsigned int i;
static struct {
unsigned int type;
char *name;
} typestr[] = {
{ 1, "A" },
{ 2, "NS" },
{ 5, "CNAME" },
{ 6, "SOA" },
{ 10, "NULL" },
{ 11, "WKS" },
{ 12, "PTR" },
{ 13, "HINFO" },
{ 15, "MX" },
{ 16, "TXT" },
{ 22, "NSAP" },
{ 23, "NSAP_PTR" },
{ 24, "SIG" },
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
};
if (type != 0)
{
sprintf(types, "[type=%d] ", type);
sprintf(types, "query[type=%d]", type);
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(types,"[%s] ", typestr[i].name);
sprintf(types,"query[%s]", typestr[i].name);
}
source = "query";
source = types;
verb = "from";
}
else
source = "cached";
if ((flags & F_FORWARD) | (flags & F_NEG))
syslog(LOG_DEBUG, "%s %s%s%s %s", source, name, types, verb, addrbuff);
else if (flags & F_REVERSE)
syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
if (strlen(name) == 0)
name = ".";
my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -10,19 +10,20 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.40"
#define VERSION "2.16"
#define FTABSIZ 150 /* max number of outstanding requests */
#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 TIMEOUT 20 /* drop UDP queries after TIMEOUT seconds */
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
#define MAXTOK 50 /* token in DHCP leases */
#define MAXLEASES 150 /* 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 */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
@@ -32,7 +33,7 @@
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#if defined(__FreeBSD__) || defined (__OpenBSD__)
#if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__)
# define LEASEFILE "/var/db/dnsmasq.leases"
#else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
@@ -45,27 +46,22 @@
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
/* Logfile stuff - change this to change the options and facility */
/* debug is true if the --no-daemon flag is given */
#ifdef LOG_PERROR
# define DNSMASQ_LOG_OPT(debug) (debug) ? LOG_PERROR : LOG_PID
#else
# define DNSMASQ_LOG_OPT(debug) (debug) ? 0 : LOG_PID
#endif
#ifdef LOG_LOCAL0
# define DNSMASQ_LOG_FAC(debug) (debug) ? LOG_LOCAL0 : LOG_DAEMON
#else
# define DNSMASQ_LOG_FAC(debug) LOG_DAEMON
#endif
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
/* A small collection of RR-types which are missing on some platforms */
#ifndef T_SIG
# define T_SIG 24
#endif
#ifndef T_SRV
# define T_SRV 33
#endif
@@ -74,29 +70,19 @@
# define T_OPT 41
#endif
/* Decide if we're going to support IPv6 */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
# define HAVE_IPV6
# define ADDRSTRLEN INET6_ADDRSTRLEN
# if defined(SOL_IPV6)
# define IPV6_LEVEL SOL_IPV6
# else
# define IPV6_LEVEL IPPROTO_IPV6
# endif
#elif defined(INET_ADDRSTRLEN)
# undef HAVE_IPV6
# define ADDRSTRLEN INET_ADDRSTRLEN
#else
# undef HAVE_IPV6
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#ifndef T_TKEY
# define T_TKEY 249
#endif
#ifndef T_TSIG
# define T_TSIG 250
#endif
/* Get linux C library versions. */
#if defined(__linux__) && !defined(__UCLIBC__) && !defined(__uClinux__)
# include <libio.h>
/*# include <libio.h> */
# include <features.h>
#endif
@@ -104,31 +90,33 @@
new system, you may want to edit these.
May replace this with Autoconf one day.
HAVE_LINUX_IPV6_PROC
define this to do IPv6 interface discovery using
proc/net/if_inet6 ala LINUX.
HAVE_LINUX_NETWORK
define this to do networking the Linux way. When it's defined, the code will
use IP_PKTINFO, Linux capabilities and the RTnetlink system. If it's not defined,
a few facilities will be lost, namely support for multiple addresses on an interface,
DNS query retransmission, and (on some systems) wildcard interface binding.
HAVE_BROKEN_RTC
define this on embeded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime()
for timing, and keep relative time values in its leases file.
Also enables "Flash disk mode". Normally, dnsmasq tries very hard to
keep the on-disk leases file up-to-date: rewriting it after every change.
When HAVE_BROKEN_RTC is in effect, a different regime is used:
The leases file is written when dnsmasq terminates, when it receives
SIGALRM, when a brand new lease is allocated, or every n seconds,
where n is one third of the smallest time configured for leases
in a --dhcp-range or --dhcp-host option.
define this on embedded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime
for timing, and keep lease lengths rather than expiry times
in its leases file. This also make dnsmasq "flash disk friendly".
Normally, dnsmasq tries very hard to keep the on-disk leases file
up-to-date: rewriting it after every renewal. When HAVE_BROKEN_RTC
is in effect, the lease file is only written when a new lease is
created, or an old one destroyed. (Because those are the only times
it changes.) This vastly reduces the number of file writes, and makes
it viable to keep the lease file on a flash filesystem.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
This configuration currently only works on Linux, but could be made to
work on other systems by teaching dnsmasq_time() in utils.c how to
read the system uptime.
HAVE_ISC_READER
define this to include the old ISC dhcpcd integration. Note that you cannot
set both HAVE_ISC_READER and HAVE_BROKEN_RTC.
HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
@@ -152,15 +140,14 @@ HAVE_DEV_URANDOM
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
HAVE_PSELECT
If your C library implements pselect, define this.
HAVE_BPF
If your OS implements Berkeley Packet filter, define this.
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
servers via DBus.
NOTES:
For Linux you should define
HAVE_LINUX_IPV6_PROC
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
HAVE_RANDOM
HAVE_DEV_RANDOM
@@ -172,135 +159,142 @@ NOTES:
For *BSD systems you should define
HAVE_SOCKADDR_SA_LEN
HAVE_RANDOM
HAVE_BPF
you should NOT define
HAVE_LINUX_IPV6_PROC
HAVE_LINUX_NETWORK
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
HAVE_DEV_RANDOM - FreeBSD and NetBSD
(OpenBSD with hardware random number generator)
HAVE_GETOPT_LONG - NetBSD
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
*/
/* platform independent options. */
#undef HAVE_BROKEN_RTC
#define HAVE_ISC_READER
/* platform independent options- uncomment to enable */
#define HAVE_TFTP
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_ISC_READER */
/* #define HAVE_DBUS */
#if defined(HAVE_BROKEN_RTC) && defined(HAVE_ISC_READER)
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
#endif
/* Allow TFTP to be disabled with COPT=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
/* platform dependent options. */
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__) || defined(__UCLIBC__)
#undef HAVE_LINUX_IPV6_PROC
#if defined(__uClinux__)
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Don't fork into background on uClinux */
#if defined(__uClinux__)
/* Never use fork() on uClinux. Note that this is subtly different from the
--keep-in-foreground option, since it also suppresses forking new
processes for TCP connections and disables the call-a-script on leasechange
system. It's intended for use on MMU-less kernels. */
#define NO_FORK
#elif defined(__UCLIBC__)
#define HAVE_LINUX_NETWORK
#if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
# define HAVE_GETOPT_LONG
#else
# undef HAVE_GETOPT_LONG
#endif
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
# define NO_FORK
#endif
/* libc5 - must precede __linux__ too */
/* Note to build a libc5 binary on a modern Debian system:
install the packages altgcc libc5 and libc5-altdev
then run "make CC=i486-linuxlibc1-gcc" */
/* Note that compling dnsmasq 2.x under libc5 and kernel 2.0.x
is probably doomed - no packet socket for starters. */
#elif defined(__linux__) && \
defined(_LINUX_C_LIB_VERSION_MAJOR) && \
(_LINUX_C_LIB_VERSION_MAJOR == 5 )
#undef HAVE_IPV6
#undef HAVE_LINUX_IPV6_PROC
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Fix various misfeatures of libc5 headers */
typedef unsigned long in_addr_t;
typedef size_t socklen_t;
#if defined(__UCLIBC_HAS_IPV6__)
# ifndef IPV6_V6ONLY
# define IPV6_V6ONLY 26
# endif
#endif
/* This is for glibc 2.x */
#elif defined(__linux__)
#define HAVE_LINUX_IPV6_PROC
#define HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define HAVE_PSELECT
/* 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;
#if defined(HAVE_IPV6)
# define HAVE_BROKEN_SOCKADDR_IN6
#endif
#endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_GETOPT_LONG
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#undef HAVE_LINUX_NETWORK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
#else
# undef HAVE_GETOPT_LONG
#endif
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#elif defined(__APPLE__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_LINUX_NETWORK
#undef HAVE_GETOPT_LONG
#define HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#define BIND_8_COMPAT
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* This is not defined in Mac OS X arpa/nameserv.h */
#define IN6ADDRSZ 16
#elif defined(__NetBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_LINUX_NETWORK
#define HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
/* env "LIBS=-lsocket -lnsl" make */
#elif defined(__sun) || defined(__sun__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_GETOPT_LONG
#undef HAVE_ARC4RANDOM
#define HAVE_RANDOM
#undef HAVE_DEV_URANDOM
#undef HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
#define HAVE_BPF
#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)
# 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
#else
# undef HAVE_IPV6
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif

341
src/dbus.c Normal file
View File

@@ -0,0 +1,341 @@
/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "dnsmasq.h"
#ifdef HAVE_DBUS
#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus.h>
struct watch {
DBusWatch *watch;
struct watch *next;
};
static dbus_bool_t add_watch(DBusWatch *watch, void *data)
{
struct watch *w;
for (w = daemon->watches; w; w = w->next)
if (w->watch == watch)
return TRUE;
if (!(w = whine_malloc(sizeof(struct watch))))
return FALSE;
w->watch = watch;
w->next = daemon->watches;
daemon->watches = w;
w = data; /* no warning */
return TRUE;
}
static void remove_watch(DBusWatch *watch, void *data)
{
struct watch **up, *w;
for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
if (w->watch == watch)
{
*up = w->next;
free(w);
}
else
up = &(w->next);
w = data; /* no warning */
}
static void dbus_read_servers(DBusMessage *message)
{
struct server *serv, *tmp, **up;
DBusMessageIter iter;
union mysockaddr addr, source_addr;
char *domain;
dbus_message_iter_init(message, &iter);
/* mark everything from DBUS */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_FROM_DBUS)
serv->flags |= SERV_MARK;
while (1)
{
int skip = 0;
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
{
u32 a;
dbus_message_iter_get_basic(&iter, &a);
dbus_message_iter_next (&iter);
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
addr.in.sin_addr.s_addr = ntohl(a);
source_addr.in.sin_family = addr.in.sin_family = AF_INET;
addr.in.sin_port = htons(NAMESERVER_PORT);
source_addr.in.sin_addr.s_addr = INADDR_ANY;
source_addr.in.sin_port = htons(daemon->query_port);
}
else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE)
{
unsigned char p[sizeof(struct in6_addr)];
unsigned int i;
skip = 1;
for(i = 0; i < sizeof(struct in6_addr); i++)
{
dbus_message_iter_get_basic(&iter, &p[i]);
dbus_message_iter_next (&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
break;
}
#ifndef HAVE_IPV6
my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
#else
if (i == sizeof(struct in6_addr)-1)
{
memcpy(&addr.in6.sin6_addr, p, sizeof(addr.in6));
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(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_flowinfo = addr.in6.sin6_flowinfo = 0;
source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
skip = 0;
}
#endif
}
else
/* At the end */
break;
do {
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
{
dbus_message_iter_get_basic(&iter, &domain);
dbus_message_iter_next (&iter);
}
else
domain = NULL;
if (!skip)
{
/* See if this is already there, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_FROM_DBUS) &&
(serv->flags & SERV_MARK))
{
if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
{
serv->flags &= ~SERV_MARK;
break;
}
if ((serv->flags & SERV_HAS_DOMAIN) &&
domain &&
hostname_isequal(domain, serv->domain))
{
serv->flags &= ~SERV_MARK;
break;
}
}
if (!serv && (serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (domain)
serv->domain = whine_malloc(strlen(domain)+1);
if (domain && !serv->domain)
{
free(serv);
serv = NULL;
}
else
{
serv->next = daemon->servers;
daemon->servers = serv;
serv->flags = SERV_FROM_DBUS;
serv->sfd = NULL;
if (domain)
{
strcpy(serv->domain, domain);
serv->flags |= SERV_HAS_DOMAIN;
}
}
}
if (serv)
{
if (source_addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0 &&
serv->domain)
serv->flags |= SERV_NO_ADDR;
else
{
serv->flags &= ~SERV_NO_ADDR;
serv->addr = addr;
serv->source_addr = source_addr;
}
}
}
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
}
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
free(serv);
}
else
up = &serv->next;
}
}
DBusHandlerResult message_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data)
{
char *method = (char *)dbus_message_get_member(message);
if (strcmp(method, "GetVersion") == 0)
{
char *v = VERSION;
DBusMessage *reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
}
else if (strcmp(method, "SetServers") == 0)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
dbus_read_servers(message);
check_servers();
}
else if (strcmp(method, "ClearCache") == 0)
clear_cache_and_reload(dnsmasq_time());
else
return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
method = user_data; /* no warning */
return (DBUS_HANDLER_RESULT_HANDLED);
}
/* returns NULL or error message, may fail silently if dbus daemon not yet up. */
char *dbus_init(void)
{
DBusConnection *connection = NULL;
DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
DBusError dbus_error;
DBusMessage *message;
dbus_error_init (&dbus_error);
if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
return NULL;
dbus_connection_set_exit_on_disconnect(connection, FALSE);
dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
NULL, NULL, NULL);
dbus_error_init (&dbus_error);
dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
if (dbus_error_is_set (&dbus_error))
return (char *)dbus_error.message;
if (!dbus_connection_register_object_path(connection, DNSMASQ_PATH,
&dnsmasq_vtable, NULL))
return _("could not register a DBus message handler");
daemon->dbus = connection;
if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
dbus_connection_send(connection, message, NULL);
return NULL;
}
void set_dbus_listeners(int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset)
{
struct watch *w;
for (w = daemon->watches; w; w = w->next)
if (dbus_watch_get_enabled(w->watch))
{
unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_fd(w->watch);
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset);
if (flags & DBUS_WATCH_WRITABLE)
FD_SET(fd, wset);
FD_SET(fd, eset);
}
}
void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
{
DBusConnection *connection = (DBusConnection *)daemon->dbus;
struct watch *w;
for (w = daemon->watches; w; w = w->next)
if (dbus_watch_get_enabled(w->watch))
{
unsigned int flags = 0;
int fd = dbus_watch_get_fd(w->watch);
if (FD_ISSET(fd, rset))
flags |= DBUS_WATCH_READABLE;
if (FD_ISSET(fd, wset))
flags |= DBUS_WATCH_WRITABLE;
if (FD_ISSET(fd, eset))
flags |= DBUS_WATCH_ERROR;
if (flags != 0)
dbus_watch_handle(w->watch, flags);
}
if (connection)
{
dbus_connection_ref (connection);
while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS);
dbus_connection_unref (connection);
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -10,37 +10,40 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#define COPYRIGHT "Copyright (C) 2000-2007 Simon Kelley"
#define COPYRIGHT "Copyright (C) 2000-2004 Simon Kelley"
#ifdef __linux__
/* for pselect.... */
#define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */
#define _BSD_SOURCE
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <netinet/in.h>
/* get this before config.h too. */
#include <syslog.h>
#ifdef __APPLE__
/* need this before arpa/nameser.h */
# define BIND_8_COMPAT
#endif
#include <arpa/nameser.h>
/* and this. */
#include <getopt.h>
#include "config.h"
#define gettext_noop(S) (S)
#ifdef NO_GETTEXT
# define _(S) (S)
#else
# include <libintl.h>
# include <locale.h>
# define _(S) gettext(S)
#endif
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/wait.h>
#if defined(__sun) || defined(__sun__)
# include <sys/sockio.h>
#endif
#include <sys/time.h>
#include <sys/un.h>
#include <limits.h>
#include <net/if.h>
#include <unistd.h>
@@ -50,13 +53,11 @@
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#endif
#include <time.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <netinet/if_ether.h>
#else
@@ -66,36 +67,85 @@
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#ifdef HAVE_BPF
# include <net/bpf.h>
# include <net/if_dl.h>
#else
# include <netpacket/packet.h>
#endif
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
#ifdef HAVE_LINUX_NETWORK
#include <linux/capability.h>
/* There doesn't seem to be a universally-available
userpace header for this. */
extern int capset(cap_user_header_t header, cap_user_data_t data);
#include <sys/prctl.h>
#endif
/* daemon is function in teh C library.... */
#define daemon dnsmasq_daemon
/* Async event queue */
struct event_desc {
int event, data;
};
#define EVENT_RELOAD 1
#define EVENT_DUMP 2
#define EVENT_ALARM 3
#define EVENT_TERM 4
#define EVENT_CHILD 5
#define EVENT_REOPEN 6
#define EVENT_EXITED 7
#define EVENT_KILLED 8
#define EVENT_EXEC_ERR 9
#define EVENT_PIPE_ERR 10
/* Exit codes. */
#define EC_GOOD 0
#define EC_BADCONF 1
#define EC_BADNET 2
#define EC_FILE 3
#define EC_NOMEM 4
#define EC_MISC 5
#define EC_INIT_OFFSET 10
/* Min buffer size: we check after adding each record, so there must be
memory for the largest packet, and the largest record so the
min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
This might be increased is EDNS packet size if greater than the minimum.
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV 1
#define OPT_FILTER 2
#define OPT_LOG 4
#define OPT_SELFMX 8
#define OPT_NO_HOSTS 16
#define OPT_NO_POLL 32
#define OPT_DEBUG 64
#define OPT_ORDER 128
#define OPT_NO_RESOLV 256
#define OPT_EXPAND 512
#define OPT_LOCALMX 1024
#define OPT_NO_NEG 2048
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
#define OPT_RESOLV_DOMAIN 32768
#define OPT_NO_FORK 65536
#define OPT_AUTHORITATIVE 131072
#define OPT_BOGUSPRIV (1<<0)
#define OPT_FILTER (1<<1)
#define OPT_LOG (1<<2)
#define OPT_SELFMX (1<<3)
#define OPT_NO_HOSTS (1<<4)
#define OPT_NO_POLL (1<<5)
#define OPT_DEBUG (1<<6)
#define OPT_ORDER (1<<7)
#define OPT_NO_RESOLV (1<<8)
#define OPT_EXPAND (1<<9)
#define OPT_LOCALMX (1<<10)
#define OPT_NO_NEG (1<<11)
#define OPT_NODOTS_LOCAL (1<<12)
#define OPT_NOWILD (1<<13)
#define OPT_ETHERS (1<<14)
#define OPT_RESOLV_DOMAIN (1<<15)
#define OPT_NO_FORK (1<<16)
#define OPT_AUTHORITATIVE (1<<17)
#define OPT_LOCALISE (1<<18)
#define OPT_DBUS (1<<19)
#define OPT_BOOTP_DYNAMIC (1<<20)
#define OPT_NO_PING (1<<21)
#define OPT_LEASE_RO (1<<22)
#define OPT_RELOAD (1<<24)
#define OPT_TFTP (1<<25)
#define OPT_TFTP_SECURE (1<<26)
#define OPT_TFTP_NOBLOCK (1<<27)
#define OPT_LOG_OPTS (1<<28)
#define OPT_TFTP_APREF (1<<29)
struct all_addr {
union {
@@ -117,9 +167,28 @@ struct doctor {
struct doctor *next;
};
struct mx_record {
char *mxname, *mxtarget;
struct mx_record *next;
struct mx_srv_record {
char *name, *target;
int issrv, srvport, priority, weight;
unsigned int offset;
struct mx_srv_record *next;
};
struct txt_record {
char *name, *txt;
unsigned short class, len;
struct txt_record *next;
};
struct ptr_record {
char *name, *ptr;
struct ptr_record *next;
};
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
struct interface_name *next;
};
union bigname {
@@ -164,7 +233,7 @@ struct crec {
#define F_NOERR 32768
/* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold and IPv6 address.
and specifically not big enough to hold an IPv6 address.
Blech. Roll our own. */
union mysockaddr {
struct sockaddr sa;
@@ -186,12 +255,15 @@ union mysockaddr {
#endif
};
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_SOURCE 8 /* source address specified */
#define SERV_HAS_DOMAIN 16 /* server for one domain only */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_FROM_RESOLV 1 /* 1 for servers from resolv, 0 for command line. */
#define SERV_NO_ADDR 2 /* no server, this domain is local only */
#define SERV_LITERAL_ADDRESS 4 /* addr is the answer, not the server */
#define SERV_HAS_DOMAIN 8 /* server for one domain only */
#define SERV_HAS_SOURCE 16 /* source address defined */
#define SERV_FOR_NODOTS 32 /* server for names with no domain part only */
#define SERV_WARNED_RECURSIVE 64 /* avoid warning spam */
#define SERV_FROM_DBUS 128 /* 1 if source is DBus */
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
struct serverfd {
@@ -202,8 +274,7 @@ struct serverfd {
struct server {
union mysockaddr addr, source_addr;
struct serverfd *sfd; /* non-NULL if this server has its own fd bound to
a source port */
struct serverfd *sfd;
char *domain; /* set if this server only handles a domain. */
int flags, tcpfd;
struct server *next;
@@ -211,11 +282,14 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok;
struct irec *next;
};
struct listener {
int fd, tcpfd, family;
int fd, tcpfd, tftpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct listener *next;
};
@@ -230,8 +304,8 @@ struct iname {
/* resolv-file parms from command-line */
struct resolvc {
struct resolvc *next;
int is_default;
int logged;
int is_default, logged;
time_t mtime;
char *name;
};
@@ -239,28 +313,47 @@ struct resolvc {
struct hostsfile {
struct hostsfile *next;
char *fname;
int index; /* matches to cache entries fro logging */
int index; /* matches to cache entries for logging */
};
struct frec {
union mysockaddr source;
struct all_addr dest;
struct server *sentto;
struct server *sentto; /* NULL means free */
unsigned int iface;
unsigned short orig_id, new_id;
int fd;
int fd, forwardall;
unsigned int crc;
time_t time;
struct frec *next;
};
/* actions in the daemon->helper RPC */
#define ACTION_DEL 1
#define ACTION_OLD_HOSTNAME 2
#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 */
char *hostname, *fqdn; /* name from client-hostname option or config */
char *old_hostname; /* hostname before it moved to another lease */
char auth_name; /* hostname came from config, not from client */
char new; /* newly created */
char changed; /* modified */
char aux_changed; /* CLID or expiry changed */
time_t expires; /* lease expiry */
unsigned char hwaddr[ETHER_ADDR_LEN];
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct in_addr addr;
unsigned char *vendorclass, *userclass;
unsigned int vendorclass_len, userclass_len;
struct dhcp_lease *next;
};
@@ -269,91 +362,165 @@ struct dhcp_netid {
struct dhcp_netid *next;
};
struct dhcp_netid_list {
struct dhcp_netid *list;
struct dhcp_netid_list *next;
};
struct dhcp_config {
unsigned int flags;
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
unsigned char hwaddr[ETHER_ADDR_LEN];
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
char *hostname;
struct dhcp_netid netid;
struct in_addr addr;
unsigned int lease_time;
time_t decline_time;
unsigned int lease_time, wildcard_mask;
struct dhcp_config *next;
};
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_HWADDR 4
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_HWADDR 4
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
struct dhcp_opt {
int opt, len, is_addr;
unsigned char *val;
char *netid;
int opt, len, flags;
unsigned char *val, *vendor_class;
struct dhcp_netid *netid;
struct dhcp_opt *next;
};
#define DHOPT_ADDR 1
#define DHOPT_STRING 2
#define DHOPT_ENCAPSULATE 4
#define DHOPT_VENDOR_MATCH 8
#define DHOPT_FORCE 16
struct dhcp_boot {
char *file, *sname;
struct in_addr next_server;
struct dhcp_netid *netid;
struct dhcp_boot *next;
};
#define MATCH_VENDOR 1
#define MATCH_USER 2
#define MATCH_CIRCUIT 3
#define MATCH_REMOTE 4
#define MATCH_SUBSCRIBER 5
/* vendorclass, userclass, remote-id or cicuit-id */
struct dhcp_vendor {
int len, is_vendor, used;
int len, match_type;
char *data;
struct dhcp_netid netid;
struct dhcp_vendor *next;
};
struct dhcp_mac {
unsigned int mask;
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
struct dhcp_netid netid;
struct dhcp_mac *next;
};
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge {
char iface[IF_NAMESIZE];
struct dhcp_bridge *alias, *next;
};
#endif
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast, router;
struct in_addr netmask, broadcast;
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
int static_only;
struct dhcp_netid netid;
int flags;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
#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 udp_dhcp_packet {
struct ip ip;
struct udphdr {
u16 uh_sport; /* source port */
u16 uh_dport; /* destination port */
u16 uh_ulen; /* udp length */
u16 uh_sum; /* udp checksum */
} udp;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[16], sname[64], file[128];
u8 options[312];
} data;
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];
};
struct ping_result {
struct in_addr addr;
time_t time;
struct ping_result *next;
};
struct daemon {
struct tftp_file {
int refcount, fd;
off_t size;
dev_t dev;
ino_t inode;
char filename[];
};
struct tftp_transfer {
int sockfd;
time_t timeout;
int backoff;
unsigned int block, blocksize;
struct sockaddr_in peer;
char opt_blocksize, opt_transize;
struct tftp_file *file;
struct tftp_transfer *next;
};
extern struct daemon {
/* datastuctures representing the command-line and
config file arguments. All set (including defaults)
in option.c */
unsigned int options;
struct resolvc default_resolv, *resolv_files;
struct mx_record *mxnames;
struct mx_srv_record *mxnames;
struct txt_record *txt;
struct ptr_record *ptr;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
char *username, *groupname;
char *domain_suffix;
char *runfile;
struct iname *if_names, *if_addrs, *if_except;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
struct bogus_addr *bogus_addr;
struct server *servers;
int cachesize;
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port;
unsigned long local_ttl;
struct hostsfile *addn_hosts;
@@ -361,30 +528,56 @@ struct daemon {
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts;
struct dhcp_vendor *dhcp_vendors;
char *dhcp_file;
char *dhcp_sname;
struct in_addr dhcp_next_server;
int dhcp_max;
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names;
char *dhcp_hosts_file;
int dhcp_max, tftp_max;
unsigned int min_leasetime;
struct doctor *doctors;
unsigned short edns_pktsz;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
int packet_buff_sz; /* size of above */
char *namebuff; /* MAXDNAME size buffer */
struct serverfd *sfds;
struct irec *interfaces;
struct listener *listeners;
struct server *last_server;
int uptime_fd;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
pid_t tcp_pids[MAX_PROCS];
/* DHCP state */
int dhcpfd, dhcp_raw_fd, dhcp_icmp_fd, lease_fd;
struct udp_dhcp_packet *dhcp_packet;
int dhcpfd, helperfd;
#ifdef HAVE_LINUX_NETWORK
int netlinkfd;
#else
int dhcp_raw_fd, dhcp_icmp_fd;
#endif
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
};
struct ping_result *ping_results;
FILE *lease_stream;
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge *bridges;
#endif
/* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus;
#ifdef HAVE_DBUS
struct watch *watches;
#endif
/* TFTP stuff */
struct tftp_transfer *tftp_trans;
char *tftp_prefix;
} *daemon;
/* cache.c */
void cache_init(int cachesize, int log);
void cache_init(void);
void log_query(unsigned short flags, char *name, struct all_addr *addr,
unsigned short type, struct hostsfile *addn_hosts, int index);
struct crec *cache_find_by_addr(struct crec *crecp,
@@ -397,98 +590,175 @@ void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts);
void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, struct in_addr *host_address, time_t ttd);
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
void cache_unhash_dhcp(void);
void dump_cache(struct daemon *daemon);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
/* rfc1035.c */
unsigned short extract_request(HEADER *header, unsigned int qlen,
unsigned short extract_request(HEADER *header, size_t qlen,
char *name, unsigned short *typep);
int setup_reply(HEADER *header, unsigned int qlen,
struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl);
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
time_t now, struct daemon *daemon);
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now);
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl);
void extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
size_t answer_request(HEADER *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, unsigned int plen,
unsigned int *len, unsigned char **p);
int check_for_local_domain(char *name, time_t now, struct mx_record *mx);
unsigned int questions_crc(HEADER *header, unsigned int plen);
int resize_packet(HEADER *header, unsigned int plen,
unsigned char *pheader, unsigned int hlen);
unsigned char *find_pseudoheader(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 char *pheader, size_t hlen);
/* util.c */
unsigned short rand16(void);
int legal_char(char c);
int canonicalise(char *s);
int atoi_check(char *a, int *res);
void die(char *message, char *arg1);
void complain(char *message, char *arg1);
void *safe_malloc(int size);
char *safe_string_alloc(char *cp);
unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
void *safe_malloc(size_t size);
void *whine_malloc(size_t size);
int sa_len(union mysockaddr *addr);
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(unsigned char *a, unsigned char *b);
time_t dnsmasq_time(int fd);
int 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);
int retry_send(void);
void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);
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);
/* log.c */
void die(char *message, char *arg1, int exit_code);
void log_start(struct passwd *ent_pw);
int log_reopen(char *log_file);
void my_syslog(int priority, const char *format, ...);
void set_log_writer(fd_set *set, int *maxfdp);
void check_log_writer(fd_set *set);
void flush_log(void);
/* option.c */
struct daemon *read_opts (int argc, char **argv);
void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(unsigned char opt);
void one_file(char *file, int nest, int hosts);
/* forward.c */
void forward_init(int first);
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now);
void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
char *tcp_request(struct daemon *daemon, int confd, time_t now);
void reply_query(struct serverfd *sfd, 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);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
/* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
void reload_servers(char *fname, struct daemon *daemon);
void check_servers(struct daemon *daemon, struct irec *interfaces);
struct irec *enumerate_interfaces(struct daemon *daemon);
struct listener *create_wildcard_listeners(int port);
struct listener *create_bound_listeners(struct irec *interfaces, int port);
int reload_servers(char *fname);
void check_servers(void);
int enumerate_interfaces();
struct listener *create_wildcard_listeners(void);
struct listener *create_bound_listeners(void);
int iface_check(int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp);
int fix_fd(int fd);
struct in_addr get_ifaddr(char *intr);
/* dhcp.c */
void dhcp_init(struct daemon *daemon);
void dhcp_packet(struct daemon *daemon, time_t now);
void dhcp_init(void);
void dhcp_packet(time_t now);
int address_available(struct dhcp_context *context, struct in_addr addr);
int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr);
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
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_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
unsigned char *hwaddr, char *hostname);
unsigned char *hwaddr, int hw_len,
int hw_type, char *hostname);
void dhcp_update_configs(struct dhcp_config *configs);
void dhcp_read_ethers(struct daemon *daemon);
void dhcp_read_ethers(void);
void dhcp_read_hosts(void);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(char *hostname);
char *host_from_dns(struct in_addr addr);
/* lease.c */
void lease_update_file(int force, time_t now);
void lease_update_dns(struct daemon *daemon);
void lease_init(struct daemon *daemon, time_t now);
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr);
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix);
void lease_set_expires(struct dhcp_lease *lease, time_t exp);
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len);
void lease_update_file(time_t now);
void lease_update_dns();
void lease_init(time_t now);
struct dhcp_lease *lease_allocate(struct in_addr addr);
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name,
char *suffix, int auth);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
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);
void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain);
void lease_update_from_configs(void);
int do_script_run(time_t now);
void rerun_scripts(void);
/* rfc2131.c */
int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now);
size_t dhcp_reply(struct dhcp_context *context, char *iface_name,
size_t sz, time_t now, int unicast_dest, int *is_inform);
/* dnsmasq.c */
int icmp_ping(struct daemon *daemon, struct in_addr addr);
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
void send_event(int fd, int event, int data);
void clear_cache_and_reload(time_t now);
/* isc.c */
#ifdef HAVE_ISC_READER
void load_dhcp(struct daemon *daemon, time_t now);
void load_dhcp(time_t now);
#endif
/* netlink.c */
#ifdef HAVE_LINUX_NETWORK
void netlink_init(void);
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
void netlink_multicast(void);
#endif
/* bpf.c */
#ifndef HAVE_LINUX_NETWORK
void init_bpf(void);
void send_via_bpf(struct dhcp_packet *mess, size_t len,
struct in_addr iface_addr, struct ifreq *ifr);
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
#endif
/* 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);
#endif
/* helper.c */
#ifndef NO_FORK
int create_helper(int log_fd, long max_fd);
void helper_write(void);
void queue_script(int action, struct dhcp_lease *lease,
char *hostname, time_t now);
int helper_buf_empty(void);
#endif
/* tftp.c */
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 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,33 +10,20 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static struct frec *frec_list;
static struct frec *frec_list = NULL;
static struct frec *get_new_frec(time_t now);
static struct frec *lookup_frec(unsigned short id);
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(void);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
/* May be called more than once. */
void forward_init(int first)
{
struct frec *f;
if (first)
frec_list = NULL;
for (f = frec_list; f; f = f->next)
f->new_id = 0;
}
/* Send a UDP packet with it's source address set as "source"
unless nowild is true, when we just send it with the kernel default */
static void send_from(int fd, int nowild, char *packet, int len,
static void send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
{
@@ -44,7 +31,7 @@ static void send_from(int fd, int nowild, char *packet, int len,
struct iovec iov[1];
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_SENDSRCADDR)
char control[CMSG_SPACE(sizeof(struct in_addr))];
@@ -65,45 +52,44 @@ static void send_from(int fd, int nowild, char *packet, int len,
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if (!nowild && to->sa.sa_family == AF_INET)
if (!nowild)
{
struct cmsghdr *cmptr;
msg.msg_control = &control_u;
msg.msg_controllen = sizeof(control_u);
{
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
#if defined(IP_PKTINFO)
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
*a = source->addr.addr4;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_SENDSRCADDR;
#endif
}
}
cmptr = CMSG_FIRSTHDR(&msg);
#ifdef HAVE_IPV6
if (to->sa.sa_family == AF_INET6)
{
msg.msg_control = &control_u;
msg.msg_controllen = sizeof(control_u);
{
struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
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;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = IPV6_PKTINFO;
cmptr->cmsg_level = IPV6_LEVEL;
}
}
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;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
*a = source->addr.addr4;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_SENDSRCADDR;
#endif
}
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;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = IPV6_PKTINFO;
cmptr->cmsg_level = IPV6_LEVEL;
}
#else
iface = 0; /* eliminate warning */
#endif
}
retry:
if (sendmsg(fd, &msg, 0) == -1)
@@ -121,7 +107,7 @@ static void send_from(int fd, int nowild, char *packet, int len,
}
}
static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp,
static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain)
{
@@ -136,7 +122,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
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, '.'))
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;
*type = SERV_FOR_NODOTS;
@@ -161,9 +147,11 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else if (serv->flags & SERV_HAS_DOMAIN)
{
unsigned int domainlen = strlen(serv->domain);
char *matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen &&
hostname_isequal(qdomain + namelen - domainlen, serv->domain) &&
domainlen >= matchlen)
hostname_isequal(matchstart, serv->domain) &&
domainlen >= matchlen &&
(domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_HAS_DOMAIN;
@@ -175,7 +163,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
{
if ((sflag | F_QUERY ) & qtype)
{
flags = qtype;
flags = qtype & ~F_BIGNAME;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
@@ -196,10 +184,12 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else
log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
}
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
else if (qtype && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception from NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames))
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR;
if (flags == F_NXDOMAIN || flags == F_NOERR)
@@ -209,29 +199,28 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
}
/* returns new last_server */
static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr,
static void forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
HEADER *header, int plen, time_t now)
HEADER *header, size_t plen, time_t now, struct frec *forward)
{
struct frec *forward;
char *domain = NULL;
int forwardall = 0, type = 0;
int type = 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, (unsigned int)plen, daemon->namebuff, NULL);
unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL;
unsigned int crc = questions_crc(header,(unsigned int)plen);
/* may be recursion not speced or no servers available. */
if (!header->rd || !daemon->servers)
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
{
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
if (!(daemon->options & OPT_ORDER))
{
forwardall = 1;
forward->forwardall = 1;
daemon->last_server = NULL;
}
type = forward->sentto->flags & SERV_TYPE;
@@ -242,14 +231,28 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
else
{
if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (!flags && !(forward = get_new_frec(now)))
if (!flags && !(forward = get_new_frec(now, NULL)))
/* table full - server failure. */
flags = F_NEG;
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->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
/* In strict_order mode, or when using domain specific servers
always try servers in the order specified in resolv.conf,
otherwise, use the one last known to work. */
@@ -259,17 +262,8 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
else if (!(start = daemon->last_server))
{
start = daemon->servers;
forwardall = 1;
forward->forwardall = 1;
}
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
forward->crc = crc;
header->id = htons(forward->new_id);
}
}
@@ -301,6 +295,10 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
}
else
{
/* Keep info in case we want to re-send this packet */
daemon->srv_save = start;
daemon->packet_len = plen;
if (!gotname)
strcpy(daemon->namebuff, "query");
if (start->addr.sa.sa_family == AF_INET)
@@ -315,8 +313,9 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
#endif
forwarded = 1;
forward->sentto = start;
if (!forwardall)
if (!forward->forwardall)
break;
forward->forwardall++;
}
}
@@ -332,27 +331,31 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->new_id = 0; /* cancel */
forward->sentto = NULL; /* cancel */
}
/* could not send on, return empty answer or address if known for whole domain */
plen = setup_reply(header, (unsigned int)plen, addrp, flags, daemon->local_ttl);
send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
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);
}
return;
}
static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
union mysockaddr *serveraddr, unsigned int n)
static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n)
{
unsigned char *pheader, *sizep;
unsigned int plen, munged = 0;
int munged = 0, is_sign;
size_t plen;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. */
requests for the client. We can't do this for signed packets. */
if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
{
unsigned short udpsz;
unsigned char *psave = sizep;
@@ -362,25 +365,19 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
{
char addrbuff[ADDRSTRLEN];
#ifdef HAVE_IPV6
if (serveraddr->sa.sa_family == AF_INET)
inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
else if (serveraddr->sa.sa_family == AF_INET6)
inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
#else
strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
#endif
syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
return 0;
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == 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))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
@@ -392,7 +389,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
{
if (header->rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
check_for_local_domain(daemon->namebuff, now))
{
/* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
@@ -401,8 +398,8 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
header->aa = 1;
header->rcode = NOERROR;
}
extract_addresses(header, n, daemon->namebuff, now, daemon);
extract_addresses(header, n, daemon->namebuff, now);
}
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -422,62 +419,108 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
}
/* sets new last_server */
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
void reply_query(struct serverfd *sfd, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
struct frec *forward;
HEADER *header;
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
int n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
size_t nn;
/* packet buffer overwritten */
daemon->srv_save = NULL;
/* Determine the address of the server replying so that we can mark that as good */
serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
#ifdef HAVE_IPV6
if (serveraddr.sa.sa_family == AF_INET6)
serveraddr.in6.sin6_flowinfo = htonl(0);
serveraddr.in6.sin6_flowinfo = 0;
#endif
header = (HEADER *)daemon->packet;
forward = lookup_frec(ntohs(header->id));
if (n >= (int)sizeof(HEADER) && header->qr && forward)
if (n >= (int)sizeof(HEADER) && header->qr &&
(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
{
/* find good server by address if possible, otherwise assume the last one we sent to */
struct server *server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
!(daemon->options & OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
unsigned char *pheader;
size_t plen;
int is_sign;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
if (!is_sign)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
struct server *last_server;
daemon->last_server = forward->sentto;
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
daemon->last_server = last_server;
break;
}
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
server = NULL;
else
{
struct server *last_server;
/* find good server by address if possible, otherwise assume the last one we sent to */
for (last_server = daemon->servers; last_server; last_server = last_server->next)
if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
sockaddr_isequal(&last_server->addr, &serveraddr))
{
server = last_server;
break;
}
}
daemon->last_server = server;
}
if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
/* If the answer is an error, keep the forward record in place in case
we get a good reply from another server. Kill it when we've
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
&forward->source, &forward->dest, forward->iface);
forward->new_id = 0; /* cancel */
if ((nn = process_reply(header, now, server, (size_t)n)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
forward->sentto = NULL; /* cancel */
}
}
}
void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
void receive_query(struct listener *listen, time_t now)
{
HEADER *header = (HEADER *)daemon->packet;
union mysockaddr source_addr;
unsigned short type;
struct iname *tmp;
struct all_addr dst_addr;
int check_dst = !(daemon->options & OPT_NOWILD);
int m, n, if_index = 0;
struct in_addr netmask, dst_addr_4;
size_t m;
ssize_t n;
int if_index = 0;
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmptr;
@@ -486,7 +529,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(IP_PKTINFO)
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(IP_RECVDSTADDR)
char control[CMSG_SPACE(sizeof(struct in_addr)) +
@@ -494,6 +537,20 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif
} control_u;
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
}
else
{
dst_addr_4.s_addr = 0;
netmask.s_addr = 0;
}
iov[0].iov_base = daemon->packet;
iov[0].iov_len = daemon->edns_pktsz;
@@ -508,102 +565,81 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return;
if (n < (int)sizeof(HEADER) ||
(msg.msg_flags & MSG_TRUNC) ||
header->qr)
return;
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
check_dst = 1;
source_addr.in6.sin6_flowinfo = htonl(0);
}
source_addr.in6.sin6_flowinfo = 0;
#endif
if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
return;
#if defined(IP_PKTINFO)
if (check_dst && listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (check_dst && listen->family == AF_INET)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
}
}
#endif
if (n < (int)sizeof(HEADER) || header->qr)
return;
/* enforce available interface configuration */
if (check_dst)
if (!(daemon->options & OPT_NOWILD))
{
struct ifreq ifr;
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
#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)
{
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;
}
#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)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
}
}
#endif
/* enforce available interface configuration */
if (if_index == 0)
return;
if (daemon->if_except || daemon->if_names)
{
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
return;
#else
if (!if_indextoname(if_index, ifr.ifr_name))
return;
if (!if_indextoname(if_index, ifr.ifr_name))
return;
#endif
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
if (daemon->if_names || daemon->if_addrs)
{
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
break;
if (!tmp)
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == listen->family)
{
if (tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
break;
#ifdef HAVE_IPV6
else if (tmp->addr.sa.sa_family == AF_INET6 &&
memcmp(&tmp->addr.in6.sin6_addr,
&dst_addr.addr.addr6,
sizeof(struct in6_addr)) == 0)
break;
#endif
}
if (!tmp)
return;
}
if (!iface_check(listen->family, &dst_addr, &ifr, &if_index))
return;
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (extract_request(header, (unsigned int)n, daemon->namebuff, &type))
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
@@ -615,50 +651,28 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, now);
m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
dst_addr_4, netmask, now);
if (m >= 1)
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
else
forward_query(daemon, listen->fd, &source_addr, &dst_addr, if_index,
header, n, now);
forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL);
}
static int read_write(int fd, char *packet, int size, int rw)
{
int n, done;
for (done = 0; done < size; done += n)
{
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
else if (n == -1)
{
if (retry_send())
goto retry;
else
return 0;
}
}
return 1;
}
/* The daemon forks before calling this: it should deal with one connection,
blocking as neccessary, and then return. Note, need to be a bit careful
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
char *tcp_request(struct daemon *daemon, int confd, time_t now)
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
{
int size = 0, m;
int size = 0;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
/* Max TCP packet + slop */
char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
HEADER *header;
struct server *last_server;
@@ -694,7 +708,11 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
}
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, now);
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
local_addr, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0)
{
@@ -704,7 +722,7 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
char *domain = NULL;
if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
@@ -714,7 +732,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
if (!flags && last_server)
{
struct server *firstsendto = NULL;
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
@@ -777,9 +796,13 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
#endif
/* There's no point in updating the cache, since this process will exit and
lose the information after one query. We make this call for the alias and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m);
/* If the crc of the question section doesn't match the crc we sent, then
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);
break;
}
@@ -789,6 +812,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
}
check_log_writer(NULL);
c1 = m>>8;
c2 = m;
@@ -799,64 +824,83 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
}
}
static struct frec *get_new_frec(time_t now)
static struct frec *allocate_frec(time_t now)
{
struct frec *f = frec_list, *oldest = NULL;
time_t oldtime = now;
int count = 0;
static time_t warntime = 0;
while (f)
{
if (f->new_id == 0)
{
f->time = now;
return f;
}
if (difftime(f->time, oldtime) <= 0)
{
oldtime = f->time;
oldest = f;
}
count++;
f = f->next;
}
struct frec *f;
/* can't find empty one, use oldest if there is one
and it's older than timeout */
if (oldest && difftime(now, oldtime) > TIMEOUT)
{
oldest->time = now;
return oldest;
}
if (count > FTABSIZ)
{ /* limit logging rate so syslog isn't DOSed either */
if (!warntime || difftime(now, warntime) > LOGRATE)
{
warntime = now;
syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
}
return NULL;
}
if ((f = (struct frec *)malloc(sizeof(struct frec))))
if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
{
f->next = frec_list;
f->time = now;
f->sentto = NULL;
frec_list = f;
}
return f;
}
/* if wait==NULL return a free or older than TIMEOUT record.
else return *wait zero if one available, or *wait is delay to
when the oldest in-use record will expire. */
struct frec *get_new_frec(time_t now, int *wait)
{
struct frec *f, *oldest;
int count;
if (wait)
*wait = 0;
for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
if (!f->sentto)
{
f->time = now;
return f;
}
else if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
/* can't find empty one, use oldest if there is one
and it's older than timeout */
if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* keep stuff for twice timeout if we can by allocating a new
record instead */
if (difftime(now, oldest->time) < 2*TIMEOUT &&
count <= daemon->ftabsize &&
(f = allocate_frec(now)))
return f;
if (!wait)
{
oldest->sentto = 0;
oldest->time = now;
}
return oldest;
}
/* none available, calculate time 'till oldest record expires */
if (count > daemon->ftabsize)
{
if (oldest && wait)
*wait = oldest->time + (time_t)TIMEOUT - now;
return NULL;
}
if (!(f = allocate_frec(now)) && wait)
/* wait one second on malloc failure */
*wait = 1;
return f; /* OK if malloc fails and this is NULL */
}
static struct frec *lookup_frec(unsigned short id)
/* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
{
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id == id)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
return f;
return NULL;
@@ -869,7 +913,7 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id &&
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
sockaddr_isequal(&f->source, addr))
@@ -878,21 +922,41 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
return NULL;
}
/* A server record is going away, remove references to it */
void server_gone(struct server *server)
{
struct frec *f;
for (f = frec_list; f; f = f->next)
if (f->sentto && f->sentto == server)
f->sentto = NULL;
if (daemon->last_server == server)
daemon->last_server = NULL;
/* return unique random ids between 1 and 65535 */
static unsigned short get_id(void)
if (daemon->srv_save == 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)
{
unsigned short ret = 0;
while (ret == 0)
if (force)
{
ret = rand16();
/* scrap ids already in use */
if ((ret != 0) && lookup_frec(ret))
ret = 0;
struct frec *f = lookup_frec(force_id, crc);
if (f)
f->sentto = NULL; /* free */
ret = force_id;
}
else do
ret = rand16();
while (lookup_frec(ret, crc));
return ret;
}

355
src/helper.c Normal file
View File

@@ -0,0 +1,355 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "dnsmasq.h"
/* 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.
The helper process is forked before the main process drops root, so it retains root
privs to pass on to the script. For this reason it tries to be paranoid about
data received from the main process, in case that has been compromised. We don't
want the helper to give an attacker root. In particular, the script to be run is
not settable via the pipe, once the fork has taken place it is not alterable by the
main process.
*/
#ifndef NO_FORK
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 int remaining_time;
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#else
time_t expires;
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
};
static struct script_data *buf = NULL;
static size_t bytes_in_buf = 0, buf_size = 0;
int create_helper(int event_fd, long max_fd)
{
pid_t pid;
int i, pipefd[2];
struct sigaction sigact;
if (!daemon->dhcp || !daemon->lease_change_command)
return -1;
/* create the pipe through which the main program sends us commands,
then fork our process. By now it's too late to die(), we just log
any failure via the main process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{
send_event(event_fd, EVENT_PIPE_ERR, errno);
return -1;
}
if (pid != 0)
{
close(pipefd[0]); /* close reader side */
return pipefd[1];
}
/* ignore SIGTERM, so that we can clean up when the main process gets hit
and SIGALRM so that we can use sleep() */
sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
/* close all the sockets etc, we don't need them here */
for (max_fd--; max_fd > 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
close(max_fd);
/* loop here */
while(1)
{
struct script_data data;
char *p, *action_str, *hostname = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff;
/* 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);
if (data.action == ACTION_DEL)
action_str = "del";
else if (data.action == ACTION_ADD)
action_str = "add";
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
action_str = "old";
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet */
if (!read_write(pipefd[0], buf, data.clid_len, 1))
continue;
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);
#else
sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires);
#endif
if (!read_write(pipefd[0], buf, data.hostname_len + data.uclass_len + data.vclass_len, 1))
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)
{
/* reap our children's children, if necessary */
while (1)
{
int status;
pid_t rc = wait(&status);
if (rc == pid)
{
/* 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));
break;
}
if (rc == -1 && errno != EINTR)
break;
}
continue;
}
if (data.clid_len != 0)
setenv("DNSMASQ_CLIENT_ID", daemon->packet, 1);
else
unsetenv("DNSMASQ_CLIENT_ID");
#ifdef HAVE_BROKEN_RTC
setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_EXPIRES");
#else
setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, 1);
unsetenv("DNSMASQ_LEASE_LENGTH");
#endif
if (data.vclass_len != 0)
{
buf[data.vclass_len - 1] = 0; /* don't trust zero-term */
/* cannot have = chars in env - truncate if found . */
if ((p = strchr((char *)buf, '=')))
*p = 0;
setenv("DNSMASQ_VENDOR_CLASS", (char *)buf, 1);
buf += data.vclass_len;
}
else
unsetenv("DNSMASQ_VENDOR_CLASS");
if (data.uclass_len != 0)
{
unsigned char *end = buf + data.uclass_len;
buf[data.uclass_len - 1] = 0; /* don't trust zero-term */
for (i = 0; buf < end;)
{
size_t len = strlen((char *)buf) + 1;
if ((p = strchr((char *)buf, '=')))
*p = 0;
if (strlen((char *)buf) != 0)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i++);
setenv(daemon->dhcp_buff2, (char *)buf, 1);
}
buf += len;
}
}
sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time);
setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, 1);
if (data.hostname_len != 0)
{
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!canonicalise(hostname))
hostname = NULL;
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
setenv("DNSMASQ_OLD_HOSTNAME", hostname, 1);
hostname = NULL;
}
else
unsetenv("DNSMASQ_OLD_HOSTNAME");
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
close(pipefd[0]);
p = strrchr(daemon->lease_change_command, '/');
execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
/* failed, send event so the main process logs the problem */
send_event(event_fd, EVENT_EXEC_ERR, errno);
_exit(0);
}
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{
unsigned char *p;
size_t size;
unsigned int i, hostname_len = 0, clid_len = 0, vclass_len = 0, uclass_len = 0;
/* 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->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;
if (size > buf_size)
{
struct script_data *new;
/* start with resonable size, will almost never need extending. */
if (size < sizeof(struct script_data) + 200)
size = sizeof(struct script_data) + 200;
if (!(new = whine_malloc(size)))
return;
if (buf)
free(buf);
buf = new;
buf_size = size;
}
buf->action = action;
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->hostname_len = hostname_len;
buf->addr = lease->addr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
#ifdef HAVE_BROKEN_RTC
buf->length = lease->length;
#else
buf->expires = lease->expires;
#endif
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
p = (unsigned char *)(buf+1);
if (clid_len != 0)
{
memcpy(p, lease->clid, clid_len);
p += clid_len;
}
if (vclass_len != 0)
{
memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len;
}
if (uclass_len != 0)
{
memcpy(p, lease->userclass, uclass_len);
p += uclass_len;
}
/* substitute * for space */
for (i = 0; i < hostname_len; i++)
if ((daemon->options & OPT_LEASE_RO) && hostname[i] == ' ')
*(p++) = '*';
else
*(p++) = hostname[i];
bytes_in_buf = p - (unsigned char *)buf;
}
int helper_buf_empty(void)
{
return bytes_in_buf == 0;
}
void helper_write(void)
{
ssize_t rc;
if (bytes_in_buf == 0)
return;
if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
{
if (bytes_in_buf != (size_t)rc)
memmove(buf, buf + rc, bytes_in_buf - rc);
bytes_in_buf -= rc;
}
else
{
if (errno == EAGAIN || errno == EINTR)
return;
bytes_in_buf = 0;
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 by Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,6 +17,8 @@
#ifdef HAVE_ISC_READER
#define MAXTOK 50
struct isc_lease {
char *name, *fqdn;
time_t expires;
@@ -55,7 +57,7 @@ static int next_token (char *token, int buffsize, FILE * fp)
return count ? 1 : 0;
}
void load_dhcp(struct daemon *daemon, time_t now)
void load_dhcp(time_t now)
{
char *hostname = daemon->namebuff;
char token[MAXTOK], *dot;
@@ -68,7 +70,7 @@ void load_dhcp(struct daemon *daemon, time_t now)
if (stat(daemon->lease_file, &statbuf) == -1)
{
if (!logged_lease)
syslog(LOG_WARNING, "failed to access %s: %m", daemon->lease_file);
my_syslog(LOG_WARNING, _("failed to access %s: %s"), daemon->lease_file, strerror(errno));
logged_lease = 1;
return;
}
@@ -84,11 +86,11 @@ void load_dhcp(struct daemon *daemon, time_t now)
if (!(fp = fopen (daemon->lease_file, "r")))
{
syslog (LOG_ERR, "failed to load %s: %m", daemon->lease_file);
my_syslog (LOG_ERR, _("failed to load %s: %s"), daemon->lease_file, strerror(errno));
return;
}
syslog (LOG_INFO, "reading %s", daemon->lease_file);
my_syslog (LOG_INFO, _("reading %s"), daemon->lease_file);
while ((next_token(token, MAXTOK, fp)))
{
@@ -110,7 +112,7 @@ void load_dhcp(struct daemon *daemon, time_t now)
if (!canonicalise(hostname))
{
*hostname = 0;
syslog(LOG_ERR, "bad name in %s", daemon->lease_file);
my_syslog(LOG_ERR, _("bad name in %s"), daemon->lease_file);
}
}
else if ((strcmp(token, "ends") == 0) ||
@@ -136,8 +138,8 @@ void load_dhcp(struct daemon *daemon, time_t now)
it is noted that it might not be entirely accurate for odd seconds.
Since we're trying to get the same answer as dhcpd, that's just
fine here. */
static int months [11] = { 31, 59, 90, 120, 151, 181,
212, 243, 273, 304, 334 };
static const int months [11] = { 31, 59, 90, 120, 151, 181,
212, 243, 273, 304, 334 };
time_t time = ((((((365 * (lease_time.tm_year - 1970) + /* Days in years since '70 */
(lease_time.tm_year - 1969) / 4 + /* Leap days since '70 */
(lease_time.tm_mon > 1 /* Days in months this year */
@@ -171,9 +173,9 @@ void load_dhcp(struct daemon *daemon, time_t now)
{
if (!daemon->domain_suffix || hostname_isequal(dot+1, daemon->domain_suffix))
{
syslog(LOG_WARNING,
"Ignoring DHCP lease for %s because it has an illegal domain part",
hostname);
my_syslog(LOG_WARNING,
_("Ignoring DHCP lease for %s because it has an illegal domain part"),
hostname);
continue;
}
*dot = 0;
@@ -187,20 +189,20 @@ void load_dhcp(struct daemon *daemon, time_t now)
break;
}
if (!lease && (lease = malloc(sizeof(struct isc_lease))))
if (!lease && (lease = whine_malloc(sizeof(struct isc_lease))))
{
lease->expires = ttd;
lease->addr = host_address;
lease->fqdn = NULL;
lease->next = leases;
if (!(lease->name = malloc(strlen(hostname)+1)))
if (!(lease->name = whine_malloc(strlen(hostname)+1)))
free(lease);
else
{
leases = lease;
strcpy(lease->name, hostname);
if (daemon->domain_suffix &&
(lease->fqdn = malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2)))
(lease->fqdn = whine_malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2)))
{
strcpy(lease->fqdn, hostname);
strcat(lease->fqdn, ".");
@@ -237,8 +239,8 @@ void load_dhcp(struct daemon *daemon, time_t now)
for (lease = leases; lease; lease = lease->next)
{
cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(daemon, lease->name, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires);
}
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2006 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,164 +10,237 @@
GNU General Public License for more details.
*/
/* Author's email: simon@thekelleys.org.uk */
#include "dnsmasq.h"
static struct dhcp_lease *leases;
static FILE *lease_file;
static int dns_dirty, file_dirty, new_lease;
static int leases_left;
static struct dhcp_lease *leases = NULL, *old_leases = NULL;
static int dns_dirty, file_dirty, leases_left;
void lease_init(struct daemon *daemon, time_t now)
void lease_init(time_t now)
{
unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3;
unsigned long ei;
time_t expires;
unsigned char hwaddr[ETHER_ADDR_LEN];
struct in_addr addr;
struct dhcp_lease *lease;
int clid_len = 0;
int has_old = 0;
char *buff = daemon->dhcp_buff;
char *buff2 = daemon->dhcp_buff2;
int clid_len, hw_len, hw_type;
FILE *leasestream;
/* These two 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);
leases = NULL;
leases_left = daemon->dhcp_max;
/* NOTE: need a+ mode to create file if it doesn't exist */
if (!(lease_file = fopen(daemon->lease_file, "a+")))
die("cannot open or create leases file: %s", NULL);
/* a+ mode lease pointer at end. */
rewind(lease_file);
while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %257s %257s",
&ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3,
buff, buff2) == 13)
if (daemon->options & OPT_LEASE_RO)
{
#ifdef HAVE_BROKEN_RTC
if (ei)
expires = (time_t)ei + now;
else
expires = (time_t)0;
#else
/* strictly time_t is opaque, but this hack should work on all sane systems,
even when sizeof(time_t) == 8 */
expires = (time_t)ei;
if (ei != 0 && difftime(now, expires) > 0)
/* 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)
{
has_old = 1;
continue; /* expired */
file_dirty = dns_dirty = 0;
return;
}
#endif
hwaddr[0] = e0;
hwaddr[1] = e1;
hwaddr[2] = e2;
hwaddr[3] = e3;
hwaddr[4] = e4;
hwaddr[5] = e5;
strcpy(daemon->dhcp_buff, daemon->lease_change_command);
strcat(daemon->dhcp_buff, " init");
leasestream = popen(daemon->dhcp_buff, "r");
}
else
{
/* NOTE: need a+ mode to create file if it doesn't exist */
leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
/* decode hex in place */
if (strcmp(buff2, "*") == 0)
clid_len = 0;
else
{
int s = (strlen(buff2)/3) + 1;
for (clid_len = 0; clid_len < s; clid_len++)
{
buff2[(clid_len*3)+2] = 0;
buff2[clid_len] = strtol(&buff2[clid_len*3], NULL, 16);
}
}
if (!leasestream)
die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
if (!(lease = lease_allocate(buff2, clid_len, addr)))
die ("too many stored leases", NULL);
lease->expires = expires;
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
if (strcmp(buff, "*") != 0)
lease_set_hostname(lease, buff, daemon->domain_suffix);
/* a+ mode lease pointer at end. */
rewind(leasestream);
}
dns_dirty = 1;
file_dirty = has_old;
new_lease = 0;
/* 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",
&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 */
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)))
die (_("too many stored leases"), NULL, EC_MISC);
#ifdef HAVE_BROKEN_RTC
if (ei != 0)
lease->expires = (time_t)ei + now;
else
lease->expires = (time_t)0;
lease->length = ei;
#else
/* strictly time_t is opaque, but this hack should work on all sane systems,
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);
if (strcmp(daemon->dhcp_buff, "*") != 0)
{
char *p;
/* unprotect spaces */
for (p = strchr(daemon->dhcp_buff, '*'); p; p = strchr(p, '*'))
*p = ' ';
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
}
daemon->lease_fd = fileno(lease_file);
/* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */
lease->new = lease->changed = 0;
}
if (!daemon->lease_stream)
{
int rc = 0;
/* shell returns 127 for "command not found", 126 for bad permissions. */
if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126)
{
if (WEXITSTATUS(rc) == 127)
errno = ENOENT;
else if (WEXITSTATUS(rc) == 126)
errno = EACCES;
die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
}
if (WEXITSTATUS(rc) != 0)
{
sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
}
}
/* Some leases may have expired */
file_dirty = 0;
lease_prune(NULL, now);
dns_dirty = 1;
}
void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
void lease_update_from_configs(void)
{
/* changes to the config may change current leases. */
struct dhcp_lease *lease;
struct dhcp_config *config;
char *name;
for (lease = leases; lease; lease = lease->next)
if ((config = find_config(dhcp_configs, NULL, lease->clid, lease->clid_len, lease->hwaddr, NULL)) &&
(config->flags & CONFIG_NAME))
lease_set_hostname(lease, config->hostname, domain);
if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
(config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, daemon->domain_suffix, 1);
else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, daemon->domain_suffix, 1); /* updates auth flag only */
}
void lease_update_file(int force, time_t now)
static void ourprintf(int *errp, char *format, ...)
{
va_list ap;
va_start(ap, format);
if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0)
*errp = errno;
va_end(ap);
}
void lease_update_file(time_t now)
{
struct dhcp_lease *lease;
int i = force; /* avoid warning */
unsigned long expires;
time_t next_event;
int i, err = 0;
char *p;
#ifdef HAVE_BROKEN_RTC
if (force || new_lease)
if (file_dirty != 0 && daemon->lease_stream)
{
lease_prune(NULL, now);
#else
if (file_dirty)
{
#endif
rewind(lease_file);
ftruncate(fileno(lease_file), 0);
errno = 0;
rewind(daemon->lease_stream);
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
err = errno;
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_BROKEN_RTC
if (lease->expires)
expires = (unsigned long) difftime(lease->expires, now);
else
expires = 0;
ourprintf(&err, "%u ", lease->length);
#else
expires = now; /* eliminate warning */
expires = (unsigned long)lease->expires;
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
fprintf(lease_file, "%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s ",
expires, lease->hwaddr[0], lease->hwaddr[1],
lease->hwaddr[2], lease->hwaddr[3], lease->hwaddr[4],
lease->hwaddr[5], inet_ntoa(lease->addr),
lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++)
{
ourprintf(&err, "%.2x", lease->hwaddr[i]);
if (i != lease->hwaddr_len - 1)
ourprintf(&err, ":");
}
ourprintf(&err, " %s ", inet_ntoa(lease->addr));
if (lease->clid_len)
/* 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, " ");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
fprintf(lease_file, "%.2x:", lease->clid[i]);
fprintf(lease_file, "%.2x\n", lease->clid[i]);
ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(&err, "%.2x\n", lease->clid[i]);
}
else
fprintf(lease_file, "*\n");
ourprintf(&err, "*\n");
}
fflush(lease_file);
fsync(fileno(lease_file));
file_dirty = 0;
new_lease = 0;
if (fflush(daemon->lease_stream) != 0 ||
fsync(fileno(daemon->lease_stream)) < 0)
err = errno;
if (!err)
file_dirty = 0;
}
/* Set alarm for when the first lease expires + slop. */
for (next_event = 0, lease = leases; lease; lease = lease->next)
if (lease->expires != 0 &&
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
next_event = lease->expires + 10;
if (err)
{
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)"),
daemon->lease_file, strerror(err),
(unsigned int)difftime(next_event, now));
}
if (next_event != 0)
alarm((unsigned)difftime(next_event, now));
}
void lease_update_dns(struct daemon *daemon)
void lease_update_dns(void)
{
struct dhcp_lease *lease;
@@ -177,8 +250,8 @@ void lease_update_dns(struct daemon *daemon)
for (lease = leases; lease; lease = lease->next)
{
cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(daemon, lease->hostname, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
}
dns_dirty = 0;
@@ -195,18 +268,16 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
{
file_dirty = 1;
*up = lease->next; /* unlink */
if (lease->hostname)
{
free(lease->hostname);
dns_dirty = 1;
}
if (lease->fqdn)
free(lease->fqdn);
if (lease->clid)
free(lease->clid);
free(lease);
dns_dirty = 1;
*up = lease->next; /* unlink */
/* Put on old_leases list 'till we
can run the script */
lease->next = old_leases;
old_leases = lease;
leases_left++;
}
else
@@ -215,23 +286,24 @@ void lease_prune(struct dhcp_lease *target, time_t now)
}
struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
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;
if (clid_len)
{
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
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;
}
else
{
for (lease = leases; lease; lease = lease->next)
if (memcmp(clid, lease->hwaddr, ETHER_ADDR_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;
return NULL;
}
@@ -248,90 +320,137 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
}
struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr)
struct dhcp_lease *lease_allocate(struct in_addr addr)
{
struct dhcp_lease *lease;
if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease))))
if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
return NULL;
lease->clid = NULL;
lease->clid_len = clid_len;
if (clid_len)
{
if (!(lease->clid = malloc(clid_len)))
{
free(lease);
return NULL;
}
memcpy(lease->clid, clid, clid_len);
}
lease->hostname = lease->fqdn = NULL;
memset(lease, 0, sizeof(struct dhcp_lease));
lease->new = 1;
lease->addr = addr;
memset(lease->hwaddr, 0, ETHER_ADDR_LEN);
lease->hwaddr_len = 256; /* illegal value */
lease->expires = 1;
#ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */
#endif
lease->next = leases;
leases = lease;
file_dirty = 1;
new_lease = 1;
leases_left--;
return lease;
}
void lease_set_expires(struct dhcp_lease *lease, time_t exp)
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{
if (exp != lease->expires)
file_dirty = dns_dirty = 1;
lease->expires = exp;
}
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr)
{
if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0)
time_t exp = now + (time_t)len;
if (len == 0xffffffff)
{
file_dirty = 1;
memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
exp = 0;
len = 0;
}
if (exp != lease->expires)
{
dns_dirty = 1;
lease->expires = exp;
#ifndef HAVE_BROKEN_RTC
lease->aux_changed = file_dirty = 1;
#endif
}
#ifdef HAVE_BROKEN_RTC
if (len != lease->length)
{
lease->length = len;
lease->aux_changed = file_dirty = 1;
}
#endif
}
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len)
{
if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
{
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type;
lease->changed = file_dirty = 1; /* run script on change */
}
/* only update clid when one is available, stops packets
without a clid removing the record. Lease init uses
clid_len == 0 for no clid. */
if (clid_len != 0 && clid)
{
if (!lease->clid)
lease->clid_len = 0;
if (lease->clid_len != clid_len)
{
lease->aux_changed = file_dirty = 1;
free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len)))
return;
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
lease->aux_changed = file_dirty = 1;
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int auth)
{
struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
if (lease->hostname && name && hostname_isequal(lease->hostname, name))
return;
{
lease->auth_name = auth;
return;
}
if (!name && !lease->hostname)
return;
/* If a machine turns up on a new net without dropping the old lease,
or two machines claim the same name, then we end up with two interfaces with
the same name. Check for that here and remove the name from the old lease. */
the same name. Check for that here and remove the name from the old lease.
Don't allow a name from the client to override a name from dnsmasq config. */
if (name)
{
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
if (lease_tmp->hostname && hostname_isequal(lease_tmp->hostname, name))
{
new_name = lease_tmp->hostname;
if (lease_tmp->auth_name && !auth)
return;
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
free(lease_tmp->old_hostname);
lease_tmp->old_hostname = lease_tmp->hostname;
lease_tmp->hostname = NULL;
if (lease_tmp->fqdn)
{
new_fqdn = lease_tmp->fqdn;
lease_tmp->fqdn = NULL;
}
break;
}
if (!new_name && (new_name = malloc(strlen(name) + 1)))
if (!new_name && (new_name = whine_malloc(strlen(name) + 1)))
strcpy(new_name, name);
if (suffix && !new_fqdn && (new_fqdn = malloc(strlen(name) + strlen(suffix) + 2)))
if (suffix && !new_fqdn && (new_fqdn = whine_malloc(strlen(name) + strlen(suffix) + 2)))
{
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
@@ -340,15 +459,106 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
}
if (lease->hostname)
free(lease->hostname);
if (lease->fqdn)
free(lease->fqdn);
{
/* run script to say we lost our old name */
free(lease->old_hostname);
lease->old_hostname = lease->hostname;
}
free(lease->fqdn);
lease->hostname = new_name;
lease->fqdn = new_fqdn;
lease->auth_name = auth;
file_dirty = dns_dirty = 1;
file_dirty = 1;
dns_dirty = 1;
lease->changed = 1; /* run script on change */
}
void rerun_scripts(void)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
lease->changed = 1;
}
/* deleted leases get transferred to the old_leases list.
remove them here, after calling the lease change
script. Also run the lease change script on new/modified leases.
Return zero if nothing to do. */
int do_script_run(time_t now)
{
struct dhcp_lease *lease;
if (old_leases)
{
lease = old_leases;
/* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname)
{
#ifndef NO_FORK
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
}
else
{
#ifndef NO_FORK
queue_script(ACTION_DEL, lease, lease->hostname, now);
#endif
old_leases = lease->next;
free(lease->hostname);
free(lease->fqdn);
free(lease->clid);
free(lease->vendorclass);
free(lease->userclass);
free(lease);
return 1;
}
}
/* make sure we announce the loss of a hostname before its new location. */
for (lease = leases; lease; lease = lease->next)
if (lease->old_hostname)
{
#ifndef NO_FORK
queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
#endif
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
}
for (lease = leases; lease; lease = lease->next)
if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
{
#ifndef NO_FORK
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
#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;
free(lease->userclass);
lease->userclass = NULL;
return 1;
}
return 0; /* nothing to do */
}

382
src/log.c Normal file
View File

@@ -0,0 +1,382 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
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.
*/
#include "dnsmasq.h"
/* 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
by not blocking when talking to syslog, instead we queue up to
MAX_LOGS messages. If more are queued, they will be dropped,
and the drop event itself logged. */
/* The "wire" protocol for logging is defined in RFC 3164 */
/* From RFC 3164 */
#define MAX_MESSAGE 1024
/* defaults in case we die() before we log_start() */
static int log_fac = LOG_DAEMON;
static int log_stderr = 0;
static int log_fd = -1;
static int log_to_file = 0;
static int entries_alloced = 0;
static int entries_lost = 0;
static int connection_good = 1;
static int max_logs = 0;
static int connection_type = SOCK_DGRAM;
struct log_entry {
int offset, length;
pid_t pid; /* to avoid duplicates over a fork */
struct log_entry *next;
char payload[MAX_MESSAGE];
};
static struct log_entry *entries = NULL;
static struct log_entry *free_entries = NULL;
void log_start(struct passwd *ent_pw)
{
log_stderr = !!(daemon->options & OPT_DEBUG);
if (daemon->log_fac != -1)
log_fac = daemon->log_fac;
#ifdef LOG_LOCAL0
else if (daemon->options & OPT_DEBUG)
log_fac = LOG_LOCAL0;
#endif
if (daemon->log_file)
{
log_to_file = 1;
daemon->max_logs = 0;
}
max_logs = daemon->max_logs;
if (!log_reopen(daemon->log_file))
die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
/* If we're running as root and going to change uid later,
change the ownership here so that the file is always owned by
the dnsmasq user. Then logrotate can just copy the owner.
Failure of the chown call is OK, (for instance when started as non-root) */
if (log_to_file && ent_pw && ent_pw->pw_uid != 0)
fchown(log_fd, ent_pw->pw_uid, -1);
/* if queuing is inhibited, make sure we allocate
the one required buffer now. */
if (max_logs == 0)
{
free_entries = safe_malloc(sizeof(struct log_entry));
free_entries->next = NULL;
entries_alloced = 1;
}
}
int log_reopen(char *log_file)
{
int flags;
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
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);
return 1;
}
static void free_entry(void)
{
struct log_entry *tmp = entries;
entries = tmp->next;
tmp->next = free_entries;
free_entries = tmp;
}
static void log_write(void)
{
ssize_t rc;
while (entries)
{
/* Avoid duplicates over a fork() */
if (entries->pid != getpid())
{
free_entry();
continue;
}
connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
{
entries->length -= rc;
entries->offset += rc;
if (entries->length == 0)
{
free_entry();
if (entries_lost != 0)
{
int e = entries_lost;
entries_lost = 0; /* avoid wild recursion */
my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e);
}
}
continue;
}
if (errno == EINTR)
continue;
if (errno == EAGAIN)
return; /* syslogd busy, go again when select() or poll() says so */
if (errno == ENOBUFS)
{
connection_good = 0;
return;
}
/* errors handling after this assumes sockets */
if (!log_to_file)
{
/* Once a stream socket hits EPIPE, we have to close and re-open
(we ignore SIGPIPE) */
if (errno == EPIPE)
{
if (log_reopen(NULL))
continue;
}
else if (errno == ECONNREFUSED ||
errno == ENOTCONN ||
errno == EDESTADDRREQ ||
errno == ECONNRESET)
{
/* socket went (syslogd down?), try and reconnect. If we fail,
stop trying until the next call to my_syslog()
ECONNREFUSED -> connection went down
ENOTCONN -> nobody listening
(ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
struct sockaddr_un logaddr;
#ifdef HAVE_SOCKADDR_SA_LEN
logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1;
#endif
logaddr.sun_family = AF_LOCAL;
strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
/* Got connection back? try again. */
if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
continue;
/* errors from connect which mean we should keep trying */
if (errno == ENOENT ||
errno == EALREADY ||
errno == ECONNREFUSED ||
errno == EISCONN ||
errno == EINTR ||
errno == EAGAIN)
{
/* try again on next syslog() call */
connection_good = 0;
return;
}
/* try the other sort of socket... */
if (errno == EPROTOTYPE)
{
connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM;
if (log_reopen(NULL))
continue;
}
}
}
/* give up - fall back to syslog() - this handles out-of-space
when logging to a file, for instance. */
log_fd = -1;
my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno));
return;
}
}
void my_syslog(int priority, const char *format, ...)
{
va_list ap;
struct log_entry *entry;
time_t time_now;
char *p;
size_t len;
pid_t pid = getpid();
va_start(ap, format);
if (log_stderr)
{
fprintf(stderr, "dnsmasq: ");
vfprintf(stderr, format, ap);
fputc('\n', stderr);
}
if (log_fd == -1)
{
/* fall-back to syslog if we die during startup or fail during running. */
static int isopen = 0;
if (!isopen)
{
openlog("dnsmasq", LOG_PID, log_fac);
isopen = 1;
}
vsyslog(priority, format, ap);
va_end(ap);
return;
}
if ((entry = free_entries))
free_entries = entry->next;
else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry))))
entries_alloced++;
if (!entry)
entries_lost++;
else
{
/* add to end of list, consumed from the start */
entry->next = NULL;
if (!entries)
entries = entry;
else
{
struct log_entry *tmp;
for (tmp = entries; tmp->next; tmp = tmp->next);
tmp->next = entry;
}
time(&time_now);
p = entry->payload;
if (!log_to_file)
p += sprintf(p, "<%d>", priority | log_fac);
p += sprintf(p, "%.15s dnsmasq[%d]: ", ctime(&time_now) + 4, pid);
len = p - entry->payload;
len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
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,
to save collecting too many log messages during a select loop. */
log_write();
/* Since we're doing things asynchronously, a cache-dump, for instance,
can now generate log lines very fast. With a small buffer (desirable),
that means it can overflow the log-buffer very quickly,
so that the cache dump becomes mainly a count of how many lines
overflowed. To avoid this, we delay here, the delay is controlled
by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms.
The scaling stuff ensures that when the queue is bigger than 8, the delay
only occurs for the last 8 entries. Once the queue is full, we stop delaying
to preserve performance.
*/
if (entries && max_logs != 0)
{
int d;
for (d = 0,entry = entries; entry; entry = entry->next, d++);
if (d == max_logs)
d = 0;
else if (max_logs > 8)
d -= max_logs - 8;
if (d > 0)
{
struct timespec waiter;
waiter.tv_sec = 0;
waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */
nanosleep(&waiter, NULL);
/* Have another go now */
log_write();
}
}
va_end(ap);
}
void set_log_writer(fd_set *set, int *maxfdp)
{
if (entries && log_fd != -1 && connection_good)
{
FD_SET(log_fd, set);
bump_maxfd(log_fd, maxfdp);
}
}
void check_log_writer(fd_set *set)
{
if (log_fd != -1 && (!set || FD_ISSET(log_fd, set)))
log_write();
}
void flush_log(void)
{
/* block until queue empty */
if (log_fd != -1)
{
int flags;
if ((flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags & ~O_NONBLOCK);
log_write();
close(log_fd);
}
}
void die(char *message, char *arg1, int exit_code)
{
char *errmess = strerror(errno);
if (!arg1)
arg1 = errmess;
log_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;
my_syslog(LOG_CRIT, _("FAILED to start up"));
flush_log();
exit(exit_code);
}

247
src/netlink.c Normal file
View File

@@ -0,0 +1,247 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "dnsmasq.h"
#ifdef HAVE_LINUX_NETWORK
#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
/* linux 2.6.19 buggers up the headers, patch it up here. */
#ifndef IFA_RTA
# define IFA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
# include <linux/if_addr.h>
#endif
static struct iovec iov;
static void nl_err(struct nlmsghdr *h);
static void nl_routechange(struct nlmsghdr *h);
void netlink_init(void)
{
struct sockaddr_nl addr;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0; /* autobind */
#ifdef HAVE_IPV6
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
#else
addr.nl_groups = RTMGRP_IPV4_ROUTE;
#endif
/* May not be able to have permission to set multicast groups don't die in that case */
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
{
if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
addr.nl_groups = 0;
if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
daemon->netlinkfd = -1;
}
}
if (daemon->netlinkfd == -1)
die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
iov.iov_len = 200;
iov.iov_base = safe_malloc(iov.iov_len);
}
static ssize_t netlink_recv(void)
{
struct msghdr msg;
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_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)
{
if (!expand_buf(&iov, 2000))
return -1;
break;
}
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);
return rc;
}
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
ssize_t len;
static unsigned int seq = 0;
int family = AF_INET;
struct {
struct nlmsghdr nlh;
struct rtgenmsg g;
} req;
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
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;
req.g.rtgen_family = family;
/* Don't block in recvfrom if send fails */
while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
if (len == -1)
return 0;
while (1)
{
if ((len = netlink_recv()) == -1)
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)
nl_routechange(h); /* May be multicast arriving async */
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)
{
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)
{
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 (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 && 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))
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addrp && ipv6_callback)
if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0;
}
#endif
}
}
}
void netlink_multicast(void)
{
ssize_t len;
struct nlmsghdr *h;
if ((len = netlink_recv()) != -1)
{
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
nl_routechange(h);
}
}
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)));
}
/* We arrange to receive netlink multicast messages whenever the network route is added.
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. */
static void nl_routechange(struct nlmsghdr *h)
{
if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save)
{
struct rtmsg *rtm = NLMSG_DATA(h);
if (rtm->rtm_type == RTN_UNICAST &&
rtm->rtm_scope == RT_SCOPE_LINK)
while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

526
src/tftp.c Normal file
View File

@@ -0,0 +1,526 @@
/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
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.
*/
#include "dnsmasq.h"
#ifdef HAVE_TFTP
static struct tftp_file *check_tftp_fileperm(ssize_t *len);
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);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
#define OP_RRQ 1
#define OP_WRQ 2
#define OP_DATA 3
#define OP_ACK 4
#define OP_ERR 5
#define OP_OACK 6
#define ERR_NOTDEF 0
#define ERR_FNF 1
#define ERR_PERM 2
#define ERR_FULL 3
#define ERR_ILL 4
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;
struct msghdr msg;
struct cmsghdr *cmptr;
struct iovec iov;
struct ifreq ifr;
int is_err = 1, if_index = 0;
struct iname *tmp;
struct tftp_transfer *transfer;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_LINUX_NETWORK
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &peer;
msg.msg_namelen = sizeof(peer);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = packet;
iov.iov_len = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
if (daemon->options & OPT_NOWILD)
addr = listen->iface->addr.in;
else
{
addr.sin_addr.s_addr = 0;
#if defined(HAVE_LINUX_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
}
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)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (addr.sin_addr.s_addr == 0)
return;
if (!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr,
&ifr, &if_index))
return;
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
}
/* tell kernel to use ephemeral port */
addr.sin_port = 0;
addr.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin_len = sizeof(addr);
#endif
if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return;
if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
free(transfer);
return;
}
transfer->peer = peer;
transfer->timeout = now + 2;
transfer->backoff = 1;
transfer->block = 1;
transfer->blocksize = 512;
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
!fix_fd(transfer->sockfd))
{
free_transfer(transfer);
return;
}
p = packet + 2;
end = packet + len;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
strcasecmp(mode, "octet") != 0)
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
else
{
while ((opt = next(&p, end)))
{
if (strcasecmp(opt, "blksize") == 0 &&
(opt = next(&p, end)) &&
!(daemon->options & OPT_TFTP_NOBLOCK))
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
transfer->opt_blocksize = 1;
transfer->block = 0;
}
if (strcasecmp(opt, "tsize") == 0 && next(&p, end))
{
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);
if (daemon->options & OPT_TFTP_APREF)
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), MAXDNAME);
strncat(daemon->namebuff, "/", MAXDNAME);
/* remove unique-directory if it doesn't exist */
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
daemon->namebuff[oldlen] = 0;
}
/* Absolute pathnames OK if they match prefix */
if (filename[0] == '/')
{
if (strstr(filename, daemon->namebuff) == filename)
daemon->namebuff[0] = 0;
else
filename++;
}
}
else if (filename[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, filename, MAXDNAME);
daemon->namebuff[MAXDNAME-1] = 0;
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len)))
{
if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
else
is_err = 0;
}
}
while (sendto(transfer->sockfd, packet, len, 0,
(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
if (is_err)
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)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file;
struct tftp_transfer *t;
uid_t uid = geteuid();
struct stat statbuf;
int fd = -1;
/* trick to ban moving out of the subtree */
if (daemon->tftp_prefix && strstr(namebuff, "/../"))
goto perm;
if ((fd = open(namebuff, O_RDONLY)) == -1)
{
if (errno == ENOENT)
{
*len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
return NULL;
}
else if (errno == EACCES)
goto perm;
else
goto oops;
}
/* stat the file descriptor to avoid stat->open races */
if (fstat(fd, &statbuf) == -1)
goto oops;
/* running as root, must be world-readable */
if (uid == 0)
{
if (!(statbuf.st_mode & S_IROTH))
goto perm;
}
/* in secure mode, must be owned by user running dnsmasq */
else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
goto perm;
/* If we're doing many tranfers from the same file, only
open it once this saves lots of file descriptors
when mass-booting a big cluster, for instance.
Be conservative and only share when inode and name match
this keeps error messages sane. */
for (t = daemon->tftp_trans; t; t = t->next)
if (t->file->dev == statbuf.st_dev &&
t->file->inode == statbuf.st_ino &&
strcmp(t->file->filename, namebuff) == 0)
{
close(fd);
t->file->refcount++;
return t->file;
}
if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
{
errno = ENOMEM;
goto oops;
}
file->fd = fd;
file->size = statbuf.st_size;
file->dev = statbuf.st_dev;
file->inode = statbuf.st_ino;
file->refcount = 1;
strcpy(file->filename, namebuff);
return file;
perm:
errno = EACCES;
*len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
if (fd != -1)
close(fd);
return NULL;
oops:
*len = tftp_err_oops(packet, namebuff);
if (fd != -1)
close(fd);
return NULL;
}
void check_tftp_listeners(fd_set *rset, time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
struct ack {
unsigned short op, block;
} *mess = (struct ack *)daemon->packet;
/* Check for activity on any existing transfers */
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
if (FD_ISSET(transfer->sockfd, rset))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
{
/* Got ack, ensure we take the (re)transmit path */
transfer->timeout = now;
transfer->backoff = 0;
transfer->block++;
}
else if (ntohs(mess->op) == OP_ERR)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
/* Sanitise error message */
if (!err)
err = "";
else
{
char *q, *r;
for (q = r = err; *r; r++)
if (isprint(*r))
*(q++) = *r;
*q = 0;
}
my_syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
(int)ntohs(mess->block), err,
inet_ntoa(transfer->peer.sin_addr));
/* Got err, ensure we take abort */
transfer->timeout = now;
transfer->backoff = 100;
}
}
}
if (difftime(now, transfer->timeout) >= 0.0)
{
int endcon = 0;
/* timeout, retransmit */
transfer->timeout += 1 + (1<<transfer->backoff);
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = get_block(daemon->packet, transfer)) == -1)
{
len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = 1;
}
else if (++transfer->backoff > 5)
{
/* 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;
}
if (len != 0)
while(sendto(transfer->sockfd, daemon->packet, len, 0,
(struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
if (endcon || len == 0)
{
/* unlink */
*up = tmp;
free_transfer(transfer);
continue;
}
}
up = &transfer->next;
}
}
static void free_transfer(struct tftp_transfer *transfer)
{
close(transfer->sockfd);
if (transfer->file && (--transfer->file->refcount) == 0)
{
close(transfer->file->fd);
free(transfer->file);
}
free(transfer);
}
static char *next(char **p, char *end)
{
char *ret = *p;
size_t len;
if (*(end-1) != 0 ||
*p == end ||
(len = strlen(ret)) == 0)
return NULL;
*p += len + 1;
return ret;
}
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
{
struct errmess {
unsigned short op, err;
char message[];
} *mess = (struct errmess *)packet;
ssize_t ret = 4;
char *errstr = strerror(errno);
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);
return ret;
}
static ssize_t tftp_err_oops(char *packet, char *file)
{
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
}
/* return -1 for error, zero for done. */
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
{
if (transfer->block == 0)
{
/* send OACK */
char *p;
struct oackmess {
unsigned short op;
char data[];
} *mess = (struct oackmess *)packet;
p = mess->data;
mess->op = htons(OP_OACK);
if (transfer->opt_blocksize)
{
p += (sprintf(p, "blksize") + 1);
p += (sprintf(p, "%d", transfer->blocksize) + 1);
}
if (transfer->opt_transize)
{
p += (sprintf(p,"tsize") + 1);
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
}
return p - packet;
}
else
{
/* send data packet */
struct datamess {
unsigned short op, block;
unsigned char data[];
} *mess = (struct datamess *)packet;
off_t offset = transfer->blocksize * (transfer->block - 1);
size_t size = transfer->file->size - offset;
if (offset > transfer->file->size)
return 0; /* finished */
if (size > transfer->blocksize)
size = transfer->blocksize;
lseek(transfer->file->fd, offset, SEEK_SET);
mess->op = htons(OP_DATA);
mess->block = htons((unsigned short)(transfer->block));
if (!read_write(transfer->file->fd, mess->data, size, 1))
return -1;
else
return size + 4;
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2005 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
@@ -15,6 +15,10 @@
#include "dnsmasq.h"
#ifdef HAVE_BROKEN_RTC
#include <sys/times.h>
#endif
/* Prefer arc4random(3) over random(3) over rand(3) */
/* Also prefer /dev/urandom over /dev/random, to preserve the entropy pool */
#ifdef HAVE_ARC4RANDOM
@@ -85,26 +89,15 @@ unsigned short rand16(void)
return( (unsigned short) (rand() >> 15) );
}
int atoi_check(char *a, int *res)
{
char *p;
for (p = a; *p; p++)
if (*p < '0' || *p > '9')
return 0;
*res = atoi(a);
return 1;
}
int legal_char(char c)
{
/* check for legal char a-z A-Z 0-9 -
(also / , used for RFC2317 and _ used in windows queries) */
(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 == '-' || c == '/' || c == '_' || c == ' ')
return 1;
return 0;
@@ -113,83 +106,81 @@ int legal_char(char c)
int canonicalise(char *s)
{
/* check for legal chars and remove trailing .
also fail empty string. */
int l = strlen(s);
also fail empty string and label > 63 chars */
size_t dotgap = 0, l = strlen(s);
char c;
int nowhite = 0;
if (l == 0) return 0;
if (l == 0 || l > MAXDNAME) return 0;
if (s[l-1] == '.')
{
if (l == 1) return 0;
s[l-1] = 0;
}
while ((c = *s++))
if (c != '.' && !legal_char(c))
return 0;
return 1;
while ((c = *s))
{
if (c == '.')
dotgap = 0;
else if (!legal_char(c) || (++dotgap > MAXLABEL))
return 0;
else if (c != ' ')
nowhite = 1;
s++;
}
return nowhite;
}
unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
{
int j;
while (sval && *sval)
{
unsigned char *cp = p++;
for (j = 0; *sval && (*sval != '.'); sval++, j++)
*p++ = *sval;
*cp = j;
if (*sval)
sval++;
}
return p;
}
/* for use during startup */
void *safe_malloc(int size)
void *safe_malloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
die("could not get memory", NULL);
die(_("could not get memory"), NULL, EC_NOMEM);
return ret;
}
char *safe_string_alloc(char *cp)
{
char *ret = NULL;
}
if (cp && strlen(cp) != 0)
{
ret = safe_malloc(strlen(cp)+1);
strcpy(ret, cp);
}
void *whine_malloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
return ret;
}
void complain(char *message, char *arg1)
{
char *errmess = strerror(errno);
if (!arg1)
arg1 = errmess;
fprintf(stderr, "dnsmasq: ");
fprintf(stderr, message, arg1, errmess);
fprintf(stderr, "\n");
syslog(LOG_CRIT, message, arg1, errmess);
}
void die(char *message, char *arg1)
{
complain(message, arg1);
syslog(LOG_CRIT, "FAILED to start up");
exit(1);
}
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
{
if (s1->sa.sa_family == s2->sa.sa_family)
{
if (s1->sa.sa_family == AF_INET &&
s1->in.sin_port == s2->in.sin_port &&
memcmp(&s1->in.sin_addr, &s2->in.sin_addr, sizeof(struct in_addr)) == 0)
s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
return 1;
#ifdef HAVE_IPV6
if (s1->sa.sa_family == AF_INET6 &&
s1->in6.sin6_port == s2->in6.sin6_port &&
s1->in6.sin6_flowinfo == s2->in6.sin6_flowinfo &&
memcmp(&s1->in6.sin6_addr, &s2->in6.sin6_addr, sizeof(struct in6_addr)) == 0)
IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
return 1;
#endif
}
@@ -211,13 +202,13 @@ int sa_len(union mysockaddr *addr)
}
/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
int hostname_isequal(unsigned char *a, unsigned char *b)
int hostname_isequal(char *a, char *b)
{
unsigned int c1, c2;
do {
c1 = *a++;
c2 = *b++;
c1 = (unsigned char) *a++;
c2 = (unsigned char) *b++;
if (c1 >= 'A' && c1 <= 'Z')
c1 += 'a' - 'A';
@@ -231,18 +222,17 @@ int hostname_isequal(unsigned char *a, unsigned char *b)
return 1;
}
time_t dnsmasq_time(int fd)
time_t dnsmasq_time(void)
{
#ifdef HAVE_BROKEN_RTC
/* we use uptime as a time-base, rather than epoch time
because epoch time can break when a machine contacts
a nameserver and updates it. */
char buf[30];
lseek(fd, 0, SEEK_SET);
read(fd, buf, 30);
return (time_t)atol(buf);
struct tms dummy;
static long tps = 0;
if (tps == 0)
tps = sysconf(_SC_CLK_TCK);
return (time_t)(times(&dummy)/tps);
#else
fd = 0; /* stop warning */
return time(NULL);
#endif
}
@@ -252,6 +242,149 @@ 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);
}
/* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf)
{
int port = 0;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET)
{
inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
port = ntohs(addr->in.sin_port);
}
else if (addr->sa.sa_family == AF_INET6)
{
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
port = ntohs(addr->in6.sin6_port);
}
#else
strcpy(buf, inet_ntoa(addr->in.sin_addr));
port = ntohs(addr->in.sin_port);
#endif
return port;
}
void prettyprint_time(char *buf, unsigned int t)
{
if (t == 0xffffffff)
sprintf(buf, _("infinite"));
else
{
unsigned int x, p = 0;
if ((x = t/86400))
p += sprintf(&buf[p], "%dd", x);
if ((x = (t/3600)%24))
p += sprintf(&buf[p], "%dh", x);
if ((x = (t/60)%60))
p += sprintf(&buf[p], "%dm", x);
if ((x = t%60))
p += sprintf(&buf[p], "%ds", x);
}
}
/* in may equal out, when maxlen may be -1 (No max len). */
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type)
{
int mask = 0, i = 0;
char *r;
if (mac_type)
*mac_type = 0;
while (maxlen == -1 || i < maxlen)
{
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
if (*r == 0)
maxlen = i;
if (r != in )
{
if (*r == '-' && i == 0 && mac_type)
{
*r = 0;
*mac_type = strtol(in, NULL, 16);
mac_type = NULL;
}
else
{
*r = 0;
mask = mask << 1;
if (strcmp(in, "*") == 0)
mask |= 1;
else
out[i] = strtol(in, NULL, 16);
i++;
}
}
in = r+1;
}
if (wildcard_mask)
*wildcard_mask = mask;
return i;
}
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;
}
/* _note_ may copy buffer */
int expand_buf(struct iovec *iov, size_t size)
{
void *new;
if (size <= iov->iov_len)
return 1;
if (!(new = whine_malloc(size)))
{
errno = ENOMEM;
return 0;
}
if (iov->iov_base)
{
memcpy(new, iov->iov_base, iov->iov_len);
free(iov->iov_base);
}
iov->iov_base = new;
iov->iov_len = size;
return 1;
}
char *print_mac(char *buff, unsigned char *mac, int len)
{
char *p = buff;
int i;
if (len == 0)
sprintf(p, "<null>");
else
for (i = 0; i < len; i++)
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
return buff;
}
void bump_maxfd(int fd, int *max)
{
if (fd > *max)
*max = fd;
}
int retry_send(void)
{
struct timespec waiter;
@@ -268,3 +401,28 @@ int retry_send(void)
return 0;
}
int read_write(int fd, unsigned char *packet, int size, int rw)
{
ssize_t n, done;
for (done = 0; done < size; done += n)
{
retry:
if (rw)
n = read(fd, &packet[done], (size_t)(size - done));
else
n = write(fd, &packet[done], (size_t)(size - done));
if (n == 0)
return 0;
else if (n == -1)
{
if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
}
return 1;
}