Compare commits

..

3 Commits
v2.32 ... v2.35

Author SHA1 Message Date
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
46 changed files with 4790 additions and 2960 deletions

155
CHANGELOG
View File

@@ -1883,7 +1883,158 @@ version 2.32
Fixed gcc-4.1 strict-alias compilation warning.
version 2.33
Remove bash-specific shellcode from the Makefile.
Fix breakage with some DHCP relay implementations which
was introduced in 2.28. Believing the source port in
DHCP requests and sending the reply there is sometimes a
bad thing to do, so I've reverted to always sending to
the relay on port 68. Thanks to Daniel Hamlin and Alex
(alde) for bug reports on this.
Moved the SuSe packaging files to contrib. I will no
longer attempt to maintain this in the source tarball. It
will be done externally, in the same way as packaging for
other distros. Suse packages are available from
ftp://ftp.suse.com/pub/people/ug/
Merged patch from Gentoo to honour $LDFLAGS environment.
Fix bug in resolv.conf processing when more than one file
is being checked.
Add --dns-forward-max option.
Warn if --resolv-file flags are ignored because of
--no-resolv. Thanks to Martin F Krafft for spotting this
one.
Add --leasefile-ro option which allows the use of an
external lease database. Many thanks to Steve Horbachuk
for assistance developing this feature.
Provide extra information to lease-change script via its
environment. If the host has a client-id, then
DNSMASQ_CLIENT_ID will be set. Either the lease length (in
DNSMASQ_LEASE_LENGTH) or lease expiry time (in
DNSMASQ_LEASE_EXPIRES) will be set, depending on the
HAVE_BROKEN_RTC compile-time option. This extra
information should make it possible to maintain the lease
database in external storage such as LDAP or a relational
database. Note that while leasefile-ro is set, the script
will be called with "old" events more often, since
changes to the client-id and lease length
(HAVE_BROKEN_RTC) or lease expiry time (otherwise)
are now flagged.
Add contrib/wrt/* which is an example implementation of an
external persistent lease database for *WRT distros with
the nvram command.
Add contrib/wrt/dhcp_release.c which is a small utility
which removes DHCP leases using DHCPRELEASE operation in
the DHCP protocol.
version 2.34
Tweak network-determination code for another corner case:
in this case a host forced to move between dhcp-ranges on
the same physical interface. Thanks to Matthias Andree.
Improve handling of high DNS loads by throttling acceptance of
new queries when resources are tight. This should be a
better response than the "forwarding table full..."
message which was logged before.
Fixed intermittent infinite loop when re-reading
/etc/ethers after SIGHUP. Thanks to Eldon Ziegler for the
bug report.
Provide extra information to the lease-change script: when
a lease loses its hostname (because a new lease comes
along and claims the same new), the "old" action is called
with the current state of the lease, ie no name. The
change is to provide the former name which the lease had
in the environment variable DNSMASQ_OLD_HOSTNAME. This
helps scripts which do stuff based on hostname, rather
than IP address. Also provide vendor-class and user-class
information to the lease-change script when a new lease is
created in the DNSMASQ_VENDOR_CLASS and
DNSMASQ_USER_CLASS<n> environment variables. Suggestion
from Francois-Xavier Le Bail.
Run the lease change script as root, even when dnsmasq is
configured to change UID to an unprivileged user. Since
most uses of the lease change script need root, this
allows its use whilst keeping the security advantages of
running the daemon without privs. The script is invoked
via a small helper process which keeps root UID, and
validates all data received from the main process. To get
root, an attacker would have to break dnsmasq and then
break the helper through the restricted comms channel
linking the two.
Add contrib/port-forward/* which is a script to set up
port-forwards using the DHCP lease-change script. It's
possible to add a host to a config file by name, and when
that host gets a DHCP lease, the script will use iptables
to set up port-forwards to configured ports at the address
which the host is allocated. The script also handles
setting up the port-forward iptables entries after reboot,
using the persistent lease database, and removing them
when a host leaves and its DHCP lease expires.
Fix unaligned access problem which caused wrong log
messages with some clients on some architectures. Thanks
to Francois-Xavier Le Bail for the bugreport.
Fixed problem with DHCPRELEASE and multi-address
interfaces. Enhanced contrib/wrt/dhcp_release to cope
under these circumstances too. Thanks to Eldon Ziegler for
input on this.
Updated French translation: thanks to Gildas Le Nadan.
Upgraded the name hash function in the DNS cache. Thanks
to Oleg Khovayko for good work on this.
Added --clear-on-reload flag. Suggestion from Johannes
Stezenbach.
Treat a nameserver address of 0.0.0.0 as "nothing". Erwin
Cabrera spotted that specifying a nameserver as 0.0.0.0
breaks things badly; this is because the network stack
treats is as "this host" and an endless loop ensues.
Added Webmin module in contrib/webmin. Thanks to Neil
Fisher for that.
version 2.35
Generate an "old" script event when a client does a DHCPREQUEST
in INIT-REBOOT or SELECTING state and the lease already
exists. Supply vendor and user class information to these
script calls.
Added support for Dragonfly BSD to src/config.h
Removed "Upgrading to 2.0" document, which is ancient
history now.
Tweak DHCP networking code for BSD, esp OpenBSD. Added a
workaround for a bug in OpenBSD 4.0: there should finally
be support for multiple interfaces under OpenBSD now.
Note that no version of dnsmasq before 2.35 will work for
DHCP under OpenBSD 4.0 because of a kernel bug.
Thanks to Claudio Jeker, Jeb Campbell and Cristobal
Palmer for help with this.
Optimised the cache code for the case of large
/etc/hosts. This is mainly to remove the O(n-squared)
algorithm which made reading large (50000 lines) files
slow, but it also takes into account the size of
/etc/hosts when building hash tables, so overall
performance should be better. Thanks to "koko" for
pointing out the problem.

14
FAQ
View File

@@ -42,10 +42,12 @@ Q: Will dnsmasq compile/run on non-Linux systems?
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
probably rotted. Dnsmasq will link with uclibc to provide small
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.
Q: My companies' nameserver knows about some names which aren't in the
@@ -383,6 +385,16 @@ Q: Dnsmasq logs "running as root because setting capabilities failed"
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/

View File

@@ -9,12 +9,14 @@ MAN = man
CFLAGS?= -O2
all :
all : dnsmasq
dnsmasq :
$(MAKE) I18N=-DNO_GETTEXT -f ../bld/Makefile -C $(SRC) dnsmasq
clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
rm -f $(SRC)/*.o $(SRC)/dnsmasq core */core
rm -f $(SRC)/*.o $(SRC)/dnsmasq.a $(SRC)/dnsmasq core */core
install : all install-common
@@ -26,7 +28,7 @@ install-common :
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}; \
$(MAKE) -f ../bld/Makefile -C ../$(SRC) $${f%.po}.mo; \
done
install-i18n : all-i18n install-common

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.

View File

@@ -3,14 +3,14 @@ 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
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o helper.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) -o $@ $(OBJS) `echo $(COPTS) | ../bld/pkg-wrapper $(PKG_CONFIG) --libs dbus-1` $(LIBS)
$(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)

View File

@@ -1,9 +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
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

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

@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.32
Version: 2.33
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
@@ -106,6 +106,6 @@ rm -rf $RPM_BUILD_ROOT
/usr/sbin/dnsmasq
/usr/share/locale/*/LC_MESSAGES/*
%doc %{_mandir}/man8/dnsmasq.8.gz
%doc %{_mandir}/*/man8/dnsmasq.8.gz

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, restart dnsmasq
# to install new iptables entries in the kernel.

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.

7
contrib/wrt/Makefile Normal file
View File

@@ -0,0 +1,7 @@
CFLAGS?= -O2
all: dhcp_release.c
$(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W dhcp_release.c -o dhcp_release
clean:
rm -f *~ *.o core dhcp_release

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

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

View File

@@ -11,9 +11,9 @@
# these requests from bringing up the link uneccessarily.
# Never forward plain names (without a dot or domain part)
domain-needed
#domain-needed
# Never forward addresses in the non-routed address spaces.
bogus-priv
#bogus-priv
# Uncomment this to filter useless windows-originated DNS requests
@@ -382,3 +382,4 @@ bogus-priv
# Include a another lot of configuration options.
#conf-file=/etc/dnsmasq.more.conf
#conf-dir=/etc/dnsmasq.d

View File

@@ -21,11 +21,11 @@ resource use and ease of configuration are important.
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,
Gentoo, Debian, Slackware, Suse, Fedora,
Smoothwall, IP-Cop, floppyfw, Firebox, LEAF, Freesco, fli4l,
CoyoteLinux, Endian Firewall and
Clarkconnect. It is also available as a FreeBSD port and is used in
Linksys wireless routers and the m0n0wall project.
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>
@@ -74,7 +74,7 @@ upstream servers handling only those domains. This makes integration
with private DNS systems easy.
</LI>
<LI>
Dnsmasq supports MX records and can be configured to return MX records
Dnsmasq supports MX and SRV records and can be configured to return MX records
for any or all local machines.
</LI>
</DIR>
@@ -82,27 +82,11 @@ for any or all local machines.
<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>.
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>
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>

2
hosts Normal file
View File

@@ -0,0 +1,2 @@
127.0.0.1 host1.domain

View File

@@ -219,6 +219,11 @@ server strictly in the order they appear in /etc/resolv.conf
.B \-n, --no-poll
Don't poll /etc/resolv.conf for changes.
.TP
.B --clear-on-reload
Whenever /etc/resolv.conf is re-read, clear the DNS cache.
This is useful when new nameservers may have different
data than that held in cache.
.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 known
@@ -326,6 +331,12 @@ Disable negative caching. Negative caching allows dnsmasq to remember
identical queries without forwarding them again. This flag disables
negative caching.
.TP
.B \-0, --dns-forward-max=<queries>
Set the maximum number of concurrent DNS queries. The default value is
150, which should be fine for most setups. The only known situation
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries.
.TP
.B \-F, --dhcp-range=[[net:]network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
@@ -343,7 +354,7 @@ 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. It is always
option. This limitation currently affects OpenBSD before version 4.0. It is always
allowed to have more than one dhcp-range in a single subnet. The optional
network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis.
@@ -357,7 +368,7 @@ 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]
.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
@@ -541,22 +552,33 @@ excluded from dnsmasq at compile time, in which case an error will
occur. In any case note that ISC leasefile integration is a deprecated
feature. It should not be used in new installations, and will be
removed in a future release.
.TP
.TP
.B \-6 --dhcp-script=<path>
Whenever a new DHCP lease is created, or an old one destroyed, the
binary specified by this option is run. The arguments to the binary
binary specified by this option is run. The arguments to the process
are "add", "old" or "del", the MAC
address of the host (or "<null>"), the IP address, and the hostname,
if known. "add" means a lease has been created, "del" means it has
been destroyed, "old" is a notification of an existing lease when
dnsmasq starts or a change to MAC address or hostname of an existing lease.
The process is run as any unprivileged user which dnsmasq
runs as, so it may be necessary to inhibit dropping of the root user,
using the
.B -u
directive, if the script needs root privs.
The environment is inherited from the invoker of dnsmasq,
and all file decriptors are
dnsmasq starts or a change to MAC address or hostname of an existing
lease (also, lease length or expiry and client-id, if leasefile-ro is set).
The process is run as root (assuming that dnsmasq was originally run as
root) even if dnsmasq is configured to change UID to an unprivileged user.
The environment is inherited from the invoker of dnsmasq, and if the
host provided a client-id, this is stored in the environment variable
DNSMASQ_CLIENT_ID. If the client provides vendor-class or user-class
information, these are provided in DNSMASQ_VENDOR_CLASS and
DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn variables, but only fory
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database. If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in
DNSMASQ_LEASE_LENGTH, otherwise the time of lease expiry is stored in
DNSMASQ_LEASE_EXPIRES. If a lease used to have a hostname, which is
removed, an "old" event is generated with the new state of the lease,
ie no name, and the former name is provided in the environment
variable DNSMASQ_OLD_HOSTNAME.
All file decriptors are
closed except stdin, stdout and stderr which are open to /dev/null
(except in debug mode).
The script is not invoked concurrently: if subsequent lease
@@ -565,6 +587,20 @@ invokation exits. At dnsmasq startup, the script will be invoked for
all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". <path>
must be an absolute pathname, no PATH search occurs.
.TP
.B \-9, --leasefile-ro
Completely suppress use of the lease database file. The file will not
be created, read, or written. Change the way the lease-change
script (if one is provided) is called, so that the lease database may
be maintained in external storage by the script. In addition to the
invokations given in
.B --dhcp-script
the lease-change script is called once, at dnsmasq startup, with the
single argument "init". When called like this the script should write
the saved state of the lease database, in dnsmasq leasefile format, to
stdout and exit with zero exit code. Setting this
option also forces the leasechange script to be called on changes
to the client-id and lease length and expiry time.
.TP
.B \-s, --domain=<domain>
Specifies the domain for the DHCP server. This has two effects;

493
po/de.po

File diff suppressed because it is too large Load Diff

443
po/es.po

File diff suppressed because it is too large Load Diff

493
po/fi.po

File diff suppressed because it is too large Load Diff

863
po/fr.po

File diff suppressed because it is too large Load Diff

449
po/id.po

File diff suppressed because it is too large Load Diff

493
po/it.po

File diff suppressed because it is too large Load Diff

442
po/no.po

File diff suppressed because it is too large Load Diff

443
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

442
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -20,10 +20,46 @@ static int bignames_left, log_queries, cache_size, hash_size;
static int uid;
static char *addrbuff;
/* 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" },
{ 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)
{
@@ -38,6 +74,7 @@ void cache_init(int size, int logq)
cache_head = cache_tail = NULL;
dhcp_inuse = dhcp_spare = NULL;
new_chain = NULL;
hash_table = NULL;
cache_size = size;
big_free = NULL;
bignames_left = size/10;
@@ -57,26 +94,63 @@ void cache_init(int size, int logq)
}
}
/* 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(cache_size);
}
/* 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)
{
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);
/* 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 = 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 = 0;
/* don't use tolower and friends here - they may be messed up by LOCALE */
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++))
if (c >= 'A' && c <= 'Z')
val += c + 'a' - 'A';
else
val += c;
{
/* 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)
@@ -168,7 +242,7 @@ static int is_expired(time_t now, struct crec *crecp)
if (difftime(now, crecp->ttd) < 0)
return 0;
return 1;
}
@@ -539,19 +613,38 @@ 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;
}
cache->flags = flags;
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
@@ -559,25 +652,25 @@ 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;
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 == '#'))
@@ -607,12 +700,28 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
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;
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) +
@@ -621,12 +730,15 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
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)))
{
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
@@ -635,14 +747,17 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
}
fclose(f);
rehash(name_count);
syslog(LOG_INFO, _("read %s - %d addresses"), filename, count);
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 = cache_size;
cache_inserted = cache_live_freed = 0;
@@ -677,10 +792,10 @@ void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *a
}
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;
}
}
@@ -909,38 +1024,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
else if (flags & F_QUERY)
{
unsigned int i;
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" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
{ 253, "MAILB" },
{ 254, "MAILA" },
{ 255, "ANY" }
};
if (type != 0)
{

View File

@@ -10,15 +10,14 @@
GNU General Public License for more details.
*/
#define VERSION "2.32"
#define VERSION "2.35"
#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 TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define LOGRATE 120 /* log table overflows every LOGRATE seconds */
#define CACHESIZ 150 /* default cache size */
#define MAXLEASES 150 /* maximum number of DHCP leases */
#define PING_WAIT 3 /* wait for ping address-in-use test */
@@ -34,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"
@@ -217,7 +216,7 @@ typedef unsigned long in_addr_t;
# define HAVE_BROKEN_SOCKADDR_IN6
#endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#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)

View File

@@ -282,8 +282,8 @@ char *dbus_init(struct daemon *daemon)
}
int set_dbus_listeners(struct daemon *daemon, int maxfd,
fd_set *rset, fd_set *wset, fd_set *eset)
void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset)
{
struct watch *w;
@@ -293,8 +293,7 @@ int set_dbus_listeners(struct daemon *daemon, int maxfd,
unsigned int flags = dbus_watch_get_flags(w->watch);
int fd = dbus_watch_get_fd(w->watch);
if (fd > maxfd)
maxfd = fd;
bump_maxfd(fd, maxfdp);
if (flags & DBUS_WATCH_READABLE)
FD_SET(fd, rset);
@@ -304,7 +303,6 @@ int set_dbus_listeners(struct daemon *daemon, int maxfd,
FD_SET(fd, eset);
}
return maxfd;
}
void check_dbus_listeners(struct daemon *daemon,

View File

@@ -31,7 +31,7 @@ void dhcp_init(struct daemon *daemon)
if (fd == -1)
die (_("cannot create DHCP socket : %s"), NULL);
if (!fix_fd(fd) ||
if (!fix_fd(fd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#elif defined(IP_RECVIF)
@@ -41,11 +41,24 @@ void dhcp_init(struct daemon *daemon)
die(_("failed to set options on DHCP socket: %s"), NULL);
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's Ok if they serve different networks.
Need to set REUSEADDR to make this posible. */
if ((daemon->options & OPT_NOWILD) &&
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1)
die(_("failed to set SO_REUSEADDR on DHCP socket: %s"), NULL);
instance binding port 67. That's OK if they serve different networks.
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD.
OpenBSD <= 4.0 screws up IP_RECVIF when SO_REUSEPORT is set, but
OpenBSD <= 3.9 doesn't have IP_RECVIF anyway, so we just have to elide
this for OpenBSD 4.0, if you want more than one instance on oBSD4.0, tough. */
#ifndef OpenBSD4_0
if (daemon->options & OPT_NOWILD)
{
#ifdef SO_REUSEPORT
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
#else
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
#endif
if (rc == -1)
die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL);
}
#endif
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
@@ -116,8 +129,8 @@ void dhcp_packet(struct daemon *daemon, time_t now)
#endif
} control_u;
msg.msg_control = control_u.control;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &daemon->dhcp_packet;
@@ -134,42 +147,48 @@ void dhcp_packet(struct daemon *daemon, time_t now)
/* expand_buf may have moved buffer */
mess = daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) && errno == EINTR);
while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
if ((msg.msg_flags & MSG_TRUNC) ||
sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
if (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
return;
#if defined (HAVE_LINUX_NETWORK)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
if (!(ifr.ifr_ifindex = iface_index) ||
ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) == -1)
return;
#elif defined(IP_RECVIF)
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
if (!iface_index || !if_indextoname(iface_index, ifr.ifr_name))
return;
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST))
unicast_dest = 1;
#endif
#else
/* fallback for systems without IP_RECVIF - allow only one interface
and assume packets arrive from it - yuk. */
{
struct iname *name;
for (name = daemon->if_names; name->isloop; name = name->next);
@@ -179,7 +198,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) < 0 )
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1 )
return;
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
@@ -205,8 +224,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
iov.iov_len = dhcp_reply(daemon, parm.current, ifr.ifr_name, (size_t)sz, now, unicast_dest);
lease_update_file(daemon, now);
lease_update_dns(daemon);
lease_collect(daemon);
if (iov.iov_len == 0)
return;
@@ -227,15 +245,19 @@ void dhcp_packet(struct daemon *daemon, time_t now)
if (mess->giaddr.s_addr)
{
/* Send to BOOTP relay */
if (!dest.sin_port)
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_port = htons(DHCP_SERVER_PORT);
dest.sin_addr = mess->giaddr;
}
else if (mess->ciaddr.s_addr)
{
dest.sin_addr = mess->ciaddr;
if (!dest.sin_port)
dest.sin_port = htons(DHCP_CLIENT_PORT);
/* If the client's idea of its own address tallys with
the source address in the request packet, we believe the
source port too, and send back to that. */
if (dest.sin_addr.s_addr != mess->ciaddr.s_addr || !dest.sin_port)
{
dest.sin_port = htons(DHCP_CLIENT_PORT);
dest.sin_addr = mess->ciaddr;
}
}
#ifdef HAVE_LINUX_NETWORK
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
@@ -621,7 +643,7 @@ void dhcp_read_ethers(struct daemon *daemon)
struct in_addr addr;
unsigned char hwaddr[ETHER_ADDR_LEN];
struct dhcp_config **up, *tmp;
struct dhcp_config *config, *configs = daemon->dhcp_conf;
struct dhcp_config *config;
int count = 0, lineno = 0;
addr.s_addr = 0; /* eliminate warning */
@@ -633,7 +655,7 @@ void dhcp_read_ethers(struct daemon *daemon)
}
/* This can be called again on SIGHUP, so remove entries created last time round. */
for (up = &daemon->dhcp_conf, config = configs; config; config = tmp)
for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
{
tmp = config->next;
if (config->flags & CONFIG_FROM_ETHERS)
@@ -682,7 +704,7 @@ void dhcp_read_ethers(struct daemon *daemon)
flags = CONFIG_ADDR;
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
break;
}
@@ -696,14 +718,14 @@ void dhcp_read_ethers(struct daemon *daemon)
flags = CONFIG_NAME;
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, ip))
break;
}
if (!config)
{
for (config = configs; config; config = config->next)
for (config = daemon->dhcp_conf; config; config = config->next)
if ((config->flags & CONFIG_HWADDR) &&
config->wildcard_mask == 0 &&
config->hwaddr_len == ETHER_ADDR_LEN &&
@@ -717,8 +739,8 @@ void dhcp_read_ethers(struct daemon *daemon)
continue;
config->flags = CONFIG_FROM_ETHERS;
config->wildcard_mask = 0;
config->next = configs;
configs = config;
config->next = daemon->dhcp_conf;
daemon->dhcp_conf = config;
}
config->flags |= flags;
@@ -745,8 +767,6 @@ void dhcp_read_ethers(struct daemon *daemon)
fclose(f);
syslog(LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
daemon->dhcp_conf = configs;
}
void dhcp_update_configs(struct dhcp_config *configs)

View File

@@ -24,6 +24,9 @@ static char *compile_opts =
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_ISC_READER
"no-"
#endif
@@ -40,7 +43,7 @@ static char *compile_opts =
static pid_t pid;
static int pipewrite;
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp);
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
static void sig_handler(int sig);
@@ -195,62 +198,18 @@ int main (int argc, char **argv)
/* prime the pipe to load stuff first time. */
sig = SIGHUP;
write(pipewrite, &sig, 1);
if (daemon->options & OPT_DEBUG)
{
#ifdef LOG_PERROR
openlog("dnsmasq", LOG_PERROR, daemon->log_fac);
#else
openlog("dnsmasq", 0, daemon->log_fac);
#endif
#ifdef HAVE_LINUX_NETWORK
prctl(PR_SET_DUMPABLE, 1);
#endif
}
else
if (!(daemon->options & OPT_DEBUG))
{
FILE *pidfile;
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
fd_set test_set;
int maxfd, i;
int maxfd = -1, i;
int nullfd = open("/dev/null", O_RDWR);
#ifdef HAVE_LINUX_NETWORK
cap_user_header_t hdr = NULL;
cap_user_data_t data = NULL;
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
if (ent_pw && ent_pw->pw_uid != 0)
{
hdr = safe_malloc(sizeof(*hdr));
data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
{
bad_capabilities = errno;
ent_pw = NULL;
}
}
#endif
FD_ZERO(&test_set);
maxfd = set_dns_listeners(daemon, &test_set, -1);
#ifdef HAVE_DBUS
maxfd = set_dbus_listeners(daemon, maxfd, &test_set, &test_set, &test_set);
#endif
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
#ifndef NO_FORK
#ifndef NO_FORK
if (!(daemon->options & OPT_NO_FORK))
{
if (fork() != 0 )
@@ -274,7 +233,12 @@ int main (int argc, char **argv)
}
umask(0);
FD_ZERO(&test_set);
set_dns_listeners(daemon, now, &test_set, &maxfd);
#ifdef HAVE_DBUS
set_dbus_listeners(daemon, &maxfd, &test_set, &test_set, &test_set);
#endif
for (i=0; i<64; i++)
{
if (i == piperead || i == pipewrite)
@@ -286,7 +250,7 @@ int main (int argc, char **argv)
#endif
if (daemon->dhcp &&
(i == fileno(daemon->lease_stream) ||
((daemon->lease_stream && i == fileno(daemon->lease_stream)) ||
#ifndef HAVE_LINUX_NETWORK
i == daemon->dhcp_raw_fd ||
i == daemon->dhcp_icmp_fd ||
@@ -303,7 +267,16 @@ int main (int argc, char **argv)
else
close(i);
}
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = create_helper(daemon);
if (!(daemon->options & OPT_DEBUG))
{
/* UID changing, etc */
struct passwd *ent_pw = daemon->username ? getpwnam(daemon->username) : NULL;
if (daemon->groupname || ent_pw)
{
gid_t dummy;
@@ -318,32 +291,55 @@ int main (int argc, char **argv)
setgid(gp->gr_gid);
}
}
if (ent_pw && ent_pw->pw_uid != 0)
{
/* finally drop root */
setuid(ent_pw->pw_uid);
{
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* On linux, we keep CAP_NETADMIN (for ARP-injection) and
CAP_NET_RAW (for icmp) if we're doing dhcp */
cap_user_header_t hdr = safe_malloc(sizeof(*hdr));
cap_user_data_t data = safe_malloc(sizeof(*data));
hdr->version = _LINUX_CAPABILITY_VERSION;
hdr->pid = 0; /* this process */
data->effective = data->permitted = data->inheritable =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
(1 << CAP_SETGID) | (1 << CAP_SETUID);
/* lose the setuid and setgid capbilities */
capset(hdr, data);
/* Tell kernel to not clear capabilities when dropping root */
if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
bad_capabilities = errno;
else
#endif
{
/* finally drop root */
setuid(ent_pw->pw_uid);
#ifdef HAVE_LINUX_NETWORK
data->effective = data->permitted =
(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
data->inheritable = 0;
/* lose the setuid and setgid capbilities */
capset(hdr, data);
#endif
}
}
openlog("dnsmasq", LOG_PID, daemon->log_fac);
}
log_start(daemon);
#ifdef HAVE_LINUX_NETWORK
if (daemon->options & OPT_DEBUG)
prctl(PR_SET_DUMPABLE, 1);
#endif
if (daemon->cachesize != 0)
syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
else
syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
#ifdef HAVE_DBUS
if (daemon->options & OPT_DBUS)
{
@@ -362,6 +358,13 @@ int main (int argc, char **argv)
if (if_tmp->name && !if_tmp->used)
syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
if (daemon->options & OPT_NO_RESOLV)
{
if (daemon->resolv_files && !daemon->resolv_files->is_default)
syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
daemon->resolv_files = NULL;
}
if (daemon->dhcp)
{
struct dhcp_context *dhcp_tmp;
@@ -392,53 +395,59 @@ int main (int argc, char **argv)
pid = getpid();
/* Start lease-change script */
if (daemon->dhcp)
lease_collect(daemon);
while (1)
{
int maxfd;
int maxfd = -1;
struct timeval t, *tp = NULL;
fd_set rset, wset, eset;
t.tv_sec = 0; /* no warning */
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
maxfd = set_dns_listeners(daemon, &rset, -1);
/* if we are out of resources, find how long we have to wait
for some to come free, we'll loop around then and restart
listening for queries */
if ((t.tv_sec = set_dns_listeners(daemon, now, &rset, &maxfd)) != 0)
{
t.tv_usec = 0;
tp = &t;
}
#ifdef HAVE_DBUS
/* Whilst polling for the dbus, wake every quarter second */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
tp->tv_sec = 0;
tp->tv_usec = 250000;
}
maxfd = set_dbus_listeners(daemon, maxfd, &rset, &wset, &eset);
set_dbus_listeners(daemon, &maxfd, &rset, &wset, &eset);
#endif
if (daemon->dhcp)
{
FD_SET(daemon->dhcpfd, &rset);
if (daemon->dhcpfd > maxfd)
maxfd = daemon->dhcpfd;
bump_maxfd(daemon->dhcpfd, &maxfd);
}
#ifdef HAVE_LINUX_NETWORK
FD_SET(daemon->netlinkfd, &rset);
if (daemon->netlinkfd > maxfd)
maxfd = daemon->netlinkfd;
bump_maxfd(daemon->netlinkfd, &maxfd);
#endif
FD_SET(piperead, &rset);
if (piperead > maxfd)
maxfd = piperead;
bump_maxfd(piperead, &maxfd);
while (helper_buf_empty() && do_script_run(daemon));
if (!helper_buf_empty())
{
FD_SET(daemon->helperfd, &wset);
bump_maxfd(daemon->helperfd, &maxfd);
}
if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
{
/* otherwise undefined after error */
@@ -460,47 +469,52 @@ int main (int argc, char **argv)
if (!(daemon->options & OPT_NO_POLL))
{
struct resolvc *res = daemon->resolv_files, *latest = NULL;
struct resolvc *res, *latest;
struct stat statbuf;
time_t last_change = 0;
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
while (res)
{
if (stat(res->name, &statbuf) == -1)
{
if (!res->logged)
syslog(LOG_WARNING, _("failed to access %s: %m"), res->name);
res->logged = 1;
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime &&
difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
}
}
res = res->next;
}
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (!res->logged)
syslog(LOG_WARNING, _("failed to access %s: %m"), res->name);
res->logged = 1;
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
}
}
}
if (latest)
{
static int warned = 0;
if (reload_servers(latest->name, daemon))
{
syslog(LOG_INFO, _("reading %s"), latest->name);
latest->mtime = last_change;
warned = 0;
check_servers(daemon);
if (daemon->options & OPT_RELOAD)
cache_reload(daemon->options, daemon->namebuff, daemon->domain_suffix, daemon->addn_hosts);
}
else if (!warned)
else
{
syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
warned = 1;
latest->mtime = 0;
if (!warned)
{
syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
warned = 1;
}
}
}
}
@@ -531,22 +545,33 @@ int main (int argc, char **argv)
{
lease_prune(NULL, now);
lease_update_file(daemon, now);
lease_collect(daemon);
}
break;
case SIGTERM:
{
int i;
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
/* Knock all our children on the head. */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] != 0)
kill(daemon->tcp_pids[i], SIGALRM);
if (daemon->dhcp)
/* handle pending lease transitions */
if (daemon->helperfd != -1)
{
/* block in writes until all done */
if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK);
do {
helper_write(daemon);
} while (!helper_buf_empty() || do_script_run(daemon));
close(daemon->helperfd);
}
if (daemon->lease_stream)
fclose(daemon->lease_stream);
syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
exit(0);
}
@@ -558,22 +583,13 @@ int main (int argc, char **argv)
whose pid != script_pid are TCP server threads. */
while ((p = waitpid(-1, NULL, WNOHANG)) > 0)
{
if (p == daemon->script_pid)
{
daemon->script_pid = 0;
lease_collect(daemon);
}
else
{
int i;
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
daemon->num_kids--;
break;
}
}
int i;
for (i = 0 ; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == p)
{
daemon->tcp_pids[i] = 0;
break;
}
}
break;
}
@@ -601,6 +617,9 @@ int main (int argc, char **argv)
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(daemon, now);
if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
helper_write(daemon);
}
}
@@ -608,7 +627,8 @@ static void sig_handler(int sig)
{
if (pid == 0)
{
/* ignore anything other than TERM during startup */
/* ignore anything other than TERM during startup
and in helper proc. (helper ignore TERM too) */
if (sig == SIGTERM)
exit(0);
}
@@ -622,7 +642,7 @@ static void sig_handler(int sig)
}
else
{
/* alarm is used to kill children after a fixed time. */
/* alarm is used to kill TCP children after a fixed time. */
if (sig == SIGALRM)
_exit(0);
}
@@ -643,29 +663,42 @@ void clear_cache_and_reload(struct daemon *daemon, time_t now)
}
}
static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd)
static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int *maxfdp)
{
struct serverfd *serverfdp;
struct listener *listener;
int wait, i;
/* will we be able to get memory? */
get_new_frec(daemon, now, &wait);
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
{
FD_SET(serverfdp->fd, set);
if (serverfdp->fd > maxfd)
maxfd = serverfdp->fd;
bump_maxfd(serverfdp->fd, maxfdp);
}
for (listener = daemon->listeners; listener; listener = listener->next)
{
FD_SET(listener->fd, set);
if (listener->fd > maxfd)
maxfd = listener->fd;
FD_SET(listener->tcpfd, set);
if (listener->tcpfd > maxfd)
maxfd = listener->tcpfd;
/* only listen for queries if we have resources */
if (wait == 0)
{
FD_SET(listener->fd, set);
bump_maxfd(listener->fd, maxfdp);
}
/* death of a child goes through the select loop, so
we don't need to explicitly arrange to wake up here */
for (i = 0; i < MAX_PROCS; i++)
if (daemon->tcp_pids[i] == 0)
{
FD_SET(listener->tcpfd, set);
bump_maxfd(listener->tcpfd, maxfdp);
break;
}
}
return maxfd;
return wait;
}
static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
@@ -713,7 +746,7 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
break;
}
if ((daemon->num_kids >= MAX_PROCS) || !iface)
if (!iface)
{
shutdown(confd, SHUT_RDWR);
close(confd);
@@ -730,7 +763,6 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
daemon->tcp_pids[i] = p;
break;
}
daemon->num_kids++;
}
close(confd);
}
@@ -859,7 +891,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
struct timeval tv;
fd_set rset;
struct sockaddr_in faddr;
int maxfd;
int maxfd = fd;
socklen_t len = sizeof(faddr);
tv.tv_usec = 250000;
@@ -867,7 +899,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
FD_ZERO(&rset);
FD_SET(fd, &rset);
maxfd = set_dns_listeners(daemon, &rset, fd);
set_dns_listeners(daemon, now, &rset, &maxfd);
if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
FD_ZERO(&rset);

View File

@@ -110,6 +110,8 @@ extern int capset(cap_user_header_t header, cap_user_data_t data);
#define OPT_DBUS 524288
#define OPT_BOOTP_DYNAMIC 1048576
#define OPT_NO_PING 2097152
#define OPT_LEASE_RO 4194304
#define OPT_RELOAD 8388608
struct all_addr {
union {
@@ -228,8 +230,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;
@@ -282,15 +283,23 @@ struct frec {
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 old; /* read from leasefile */
char changed; /* modified */
char aux_changed; /* CLID or expiry changed */
time_t expires; /* lease expiry */
#ifdef HAVE_BROKEN_RTC
unsigned int length;
@@ -298,6 +307,8 @@ struct dhcp_lease {
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;
};
@@ -310,6 +321,7 @@ 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 */
@@ -422,7 +434,7 @@ struct daemon {
struct bogus_addr *bogus_addr;
struct server *servers;
int log_fac; /* log facility */
int cachesize;
int cachesize, ftabsize;
int port, query_port;
unsigned long local_ttl;
struct hostsfile *addn_hosts;
@@ -448,11 +460,10 @@ struct daemon {
struct server *last_server;
struct server *srv_save; /* Used for resend on DoD */
size_t packet_len; /* " " */
pid_t script_pid, tcp_pids[MAX_PROCS];
int num_kids;
pid_t tcp_pids[MAX_PROCS];
/* DHCP state */
int dhcpfd;
int dhcpfd, helperfd;
#ifdef HAVE_LINUX_NETWORK
int netlinkfd;
#else
@@ -532,6 +543,9 @@ 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(struct daemon *daemon, unsigned char *mac, int len);
void bump_maxfd(int fd, int *max);
void log_start(struct daemon *daemon);
int read_write(int fd, unsigned char *packet, int size, int rw);
/* option.c */
struct daemon *read_opts (int argc, char **argv, char *compile_opts);
@@ -542,6 +556,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
void server_gone(struct daemon *daemon, struct server *server);
struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait);
/* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
@@ -590,7 +605,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
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 daemon *daemon);
void lease_collect(struct daemon *daemon);
int do_script_run(struct daemon *daemon);
/* rfc2131.c */
size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, size_t sz, time_t now, int unicast_dest);
@@ -627,6 +642,13 @@ int iface_enumerate(struct daemon *daemon, void *parm,
char *dbus_init(struct daemon *daemon);
void check_dbus_listeners(struct daemon *daemon,
fd_set *rset, fd_set *wset, fd_set *eset);
int set_dbus_listeners(struct daemon *daemon, int maxfd,
fd_set *rset, fd_set *wset, fd_set *eset);
void set_dbus_listeners(struct daemon *daemon, int *maxfdp,
fd_set *rset, fd_set *wset, fd_set *eset);
#endif
/* helper.c */
int create_helper(struct daemon *daemon);
void helper_write(struct daemon *daemon);
void queue_script(struct daemon *daemon, int action,
struct dhcp_lease *lease, char *hostname);
int helper_buf_empty(void);

View File

@@ -14,7 +14,6 @@
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_by_sender(unsigned short id,
union mysockaddr *addr,
@@ -232,7 +231,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
if (gotname)
flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
if (!flags && !(forward = get_new_frec(now)))
if (!flags && !(forward = get_new_frec(daemon, now, NULL)))
/* table full - server failure. */
flags = F_NEG;
@@ -459,7 +458,6 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
forward->forwardall = 1;
header->qr = 0;
header->tc = 0;
forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward);
@@ -657,31 +655,6 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
header, (size_t)n, now, NULL);
}
static 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 (errno == EINTR)
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
@@ -837,49 +810,10 @@ unsigned 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))))
{
f->next = frec_list;
@@ -887,6 +821,61 @@ static struct frec *get_new_frec(time_t now)
f->new_id = 0;
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(struct daemon *daemon, 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->new_id == 0)
{
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->new_id = 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 */
}

327
src/helper.c Normal file
View File

@@ -0,0 +1,327 @@
/* 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.
*/
struct script_data
{
unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, uclass_len, vclass_len;
struct in_addr addr;
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#else
time_t expires;
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
};
static struct script_data *buf;
static size_t bytes_in_buf, buf_size;
int create_helper(struct daemon *daemon)
{
pid_t pid;
int i, pipefd[2];
struct sigaction sigact;
buf = NULL;
buf_size = bytes_in_buf = 0;
if (!daemon->dhcp || !daemon->lease_change_command)
return -1;
/* create the pipe through which the main program sends us commands,
then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
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 */
sigact.sa_handler = SIG_IGN;
sigact.sa_flags = 0;
sigemptyset(&sigact.sa_mask);
sigaction(SIGTERM, &sigact, NULL);
/* close all the sockets etc, we don't need them here */
for (i = 0; i < 64; i++)
if (i != STDOUT_FILENO && i != STDERR_FILENO &&
i != STDIN_FILENO && i != pipefd[0])
close(i);
/* we open our own log connection. */
log_start(daemon);
/* don't give our end of the pipe to our children */
if ((i = fcntl(pipefd[0], F_GETFD)) != -1)
fcntl(pipefd[0], F_SETFD, i | FD_CLOEXEC);
/* 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;
if ((pid = fork()) == -1)
continue;
/* wait for child to complete */
if (pid != 0)
{
int status;
waitpid(pid, &status, 0);
if (WIFSIGNALED(status))
syslog(LOG_WARNING, _("child process killed by signal %d"), WTERMSIG(status));
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
syslog(LOG_WARNING, _("child process exited with status %d"), WEXITSTATUS(status));
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;
}
}
if (data.hostname_len != 0)
{
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
canonicalise(hostname);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
setenv("DNSMASQ_OLD_HOSTNAME", hostname, 1);
hostname = NULL;
}
else
unsetenv("DNSMASQ_OLD_HOSTNAME");
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);
/* log socket should still be open, right? */
syslog(LOG_ERR, _("failed to execute %s: %m"),
daemon->lease_change_command);
_exit(0);
}
}
/* pack up lease data into a buffer */
void queue_script(struct daemon *daemon, int action, struct dhcp_lease *lease, char *hostname)
{
unsigned char *p;
size_t size;
unsigned int 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 = 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
p = (unsigned char *)(buf+1);
if (buf->clid_len != 0)
{
memcpy(p, lease->clid, clid_len);
p += clid_len;
}
if (buf->vclass_len != 0)
{
memcpy(p, lease->vendorclass, vclass_len);
p += vclass_len;
}
if (buf->uclass_len != 0)
{
memcpy(p, lease->userclass, uclass_len);
p += uclass_len;
}
if (buf->hostname_len != 0)
{
memcpy(p, hostname, hostname_len);
p += hostname_len;
}
bytes_in_buf = p - (unsigned char *)buf;
}
int helper_buf_empty(void)
{
return bytes_in_buf == 0;
}
void helper_write(struct daemon *daemon)
{
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;
}
}

View File

@@ -21,60 +21,103 @@ void lease_init(struct daemon *daemon, time_t now)
struct in_addr addr;
struct dhcp_lease *lease;
int flags, clid_len, hw_len, hw_type;
FILE *leasestream;
leases = old_leases = NULL;
leases_left = daemon->dhcp_max;
/* NOTE: need a+ mode to create file if it doesn't exist */
if (!(daemon->lease_stream = fopen(daemon->lease_file, "a+")))
die(_("cannot open or create leases file: %s"), NULL);
flags = fcntl(fileno(daemon->lease_stream), F_GETFD);
if (flags != -1)
fcntl(fileno(daemon->lease_stream), F_SETFD, flags | FD_CLOEXEC);
if (daemon->options & OPT_LEASE_RO)
{
/* run "<lease_change_script> init" once to get the
initial state of the database. If leasefile-ro is
set without a script, we just do without any
lease database. */
if (!daemon->lease_change_command)
{
file_dirty = dns_dirty = 0;
return;
}
strcpy(daemon->dhcp_buff, daemon->lease_change_command);
strcat(daemon->dhcp_buff, " init");
leasestream = popen(daemon->dhcp_buff, "r");
}
else
{
/* NOTE: need a+ mode to create file if it doesn't exist */
leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
if (!leasestream)
die(_("cannot open or create lease file %s: %s"), daemon->lease_file);
flags = fcntl(fileno(leasestream), F_GETFD);
if (flags != -1)
fcntl(fileno(leasestream), F_SETFD, flags | FD_CLOEXEC);
/* a+ mode lease pointer at end. */
rewind(leasestream);
}
/* a+ mode lease pointer at end. */
rewind(daemon->lease_stream);
/* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */
while (fscanf(daemon->lease_stream, "%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);
/* not actually new */
lease->new = 0;
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);
/* not actually new */
lease->new = 0;
#ifdef HAVE_BROKEN_RTC
if (ei != 0)
lease->expires = (time_t)ei + now;
else
lease->expires = (time_t)0;
lease->length = ei;
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;
/* 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)
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 0);
}
if (!daemon->lease_stream)
{
int rc = 0;
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)
lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix, 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);
}
if (WEXITSTATUS(rc) != 0)
{
sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
die(_("lease-init script returned exit code %s"), daemon->dhcp_buff);
}
}
/* Some leases may have expired */
@@ -117,7 +160,7 @@ void lease_update_file(struct daemon *daemon, time_t now)
time_t next_event;
int i, err = 0;
if (file_dirty != 0)
if (file_dirty != 0 && daemon->lease_stream)
{
errno = 0;
rewind(daemon->lease_stream);
@@ -298,7 +341,7 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
dns_dirty = 1;
lease->expires = exp;
#ifndef HAVE_BROKEN_RTC
file_dirty = 1;
lease->aux_changed = file_dirty = 1;
#endif
}
@@ -306,7 +349,7 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
if (len != lease->length)
{
lease->length = len;
file_dirty = 1;
lease->aux_changed = file_dirty = 1;
}
#endif
}
@@ -318,11 +361,10 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
{
file_dirty = 1;
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type;
lease->old = 1; /* run script on change */
lease->changed = file_dirty = 1; /* run script on change */
}
/* only update clid when one is available, stops packets
@@ -335,18 +377,19 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
if (lease->clid_len != clid_len)
{
file_dirty = 1;
lease->aux_changed = file_dirty = 1;
if (lease->clid)
free(lease->clid);
if (!(lease->clid = malloc(clid_len)))
return;
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
file_dirty = 1;
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, int auth)
@@ -375,8 +418,11 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
{
if (lease_tmp->auth_name && !auth)
return;
lease_tmp->old = 1; /* call script on change */
new_name = lease_tmp->hostname;
/* this shouldn't happen unless updates are very quick and the
script very slow, we just avoid a memory leak if it does. */
if (lease_tmp->old_hostname)
free(lease_tmp->old_hostname);
lease_tmp->old_hostname = lease_tmp->hostname;
lease_tmp->hostname = NULL;
if (lease_tmp->fqdn)
{
@@ -398,7 +444,13 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
}
if (lease->hostname)
free(lease->hostname);
{
/* run script to say we lost our old name */
if (lease->old_hostname)
free(lease->old_hostname);
lease->old_hostname = lease->hostname;
}
if (lease->fqdn)
free(lease->fqdn);
@@ -408,79 +460,84 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix, int
file_dirty = 1;
dns_dirty = 1;
lease->old = 1; /* run script on change */
lease->changed = 1; /* run script on change */
}
#ifndef NO_FORK
static pid_t run_script(struct daemon *daemon, char *action, struct dhcp_lease *lease)
{
if (daemon->lease_change_command)
{
char *mac = print_mac(daemon, lease->hwaddr, lease->hwaddr_len);
char *addr = inet_ntoa(lease->addr);
char *com = strrchr(daemon->lease_change_command, '/');
pid_t pid = fork();
if (pid == -1)
return 0; /* fork error */
else if (pid != 0)
return pid;
execl(daemon->lease_change_command,
com ? com+1 : daemon->lease_change_command,
action, mac, addr, lease->hostname, (char*)NULL);
/* log socket should still be open, right? */
syslog(LOG_ERR, _("failed to execute %s: %m"),
daemon->lease_change_command);
_exit(0);
}
return 0;
}
#endif
/* 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 leases */
void lease_collect(struct daemon *daemon)
script. Also run the lease change script on new/modified leases.
Return zero if nothing to do. */
int do_script_run(struct daemon *daemon)
{
struct dhcp_lease *lease;
while (old_leases)
if (old_leases)
{
if (daemon->script_pid != 0)
return; /* busy */
lease = old_leases;
old_leases = lease->next;
#ifndef NO_FORK
daemon->script_pid = run_script(daemon, "del", lease);
#endif
if (lease->hostname)
free(lease->hostname);
if (lease->fqdn)
free(lease->fqdn);
if (lease->clid)
free(lease->clid);
free(lease);
/* If the lease still has an old_hostname, do the "old" action on that first */
if (lease->old_hostname)
{
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname);
free(lease->old_hostname);
lease->old_hostname = NULL;
return 1;
}
else
{
queue_script(daemon, ACTION_DEL, lease, lease->hostname);
old_leases = lease->next;
if (lease->hostname)
free(lease->hostname);
if (lease->fqdn)
free(lease->fqdn);
if (lease->clid)
free(lease->clid);
if (lease->vendorclass)
free(lease->vendorclass);
if (lease->userclass)
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->new || lease->old)
{
if (daemon->script_pid != 0)
return; /* busy */
#ifndef NO_FORK
daemon->script_pid = run_script(daemon, lease->new ? "add" : "old", lease);
#endif
lease->new = lease->old = 0;
if (lease->old_hostname)
{
queue_script(daemon, ACTION_OLD_HOSTNAME, lease, lease->old_hostname);
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)))
{
queue_script(daemon, lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname);
lease->new = lease->changed = lease->aux_changed = 0;
/* these are used for the "add" call, then junked, since they're not in the database */
if (lease->vendorclass)
{
free(lease->vendorclass);
lease->vendorclass = NULL;
}
if (lease->userclass)
{
free(lease->userclass);
lease->userclass = NULL;
}
return 1;
}
return 0; /* nothing to do */
}

View File

@@ -403,6 +403,14 @@ void check_servers(struct daemon *daemon)
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (new->addr.sa.sa_family == AF_INET &&
new->addr.in.sin_addr.s_addr == 0)
{
free(new);
continue;
}
for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&new->addr, &iface->addr))
break;

View File

@@ -24,7 +24,11 @@ struct myoption {
};
#endif
#define OPTSTRING "531yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:"
#define OPTSTRING "9531yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:"
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -95,38 +99,43 @@ static const struct myoption opts[] =
{"dhcp-script", 1, 0, '6'},
{"conf-dir", 1, 0, '7'},
{"log-facility", 1, 0 ,'8'},
{"leasefile-ro", 0, 0, '9'},
{"dns-forward-max", 1, 0, '0'},
{"clear-on-reload", 0, 0, LOPT_RELOAD },
{ NULL, 0, 0, 0 }
};
struct optflags {
char c;
int c;
unsigned int flag;
};
static const struct optflags optmap[] = {
{ 'b', OPT_BOGUSPRIV },
{ 'f', OPT_FILTER },
{ 'q', OPT_LOG },
{ 'e', OPT_SELFMX },
{ 'h', OPT_NO_HOSTS },
{ 'n', OPT_NO_POLL },
{ 'd', OPT_DEBUG },
{ 'k', OPT_NO_FORK },
{ 'K', OPT_AUTHORITATIVE },
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
{ 'L', OPT_LOCALMX },
{ 'N', OPT_NO_NEG },
{ 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS },
{ 'y', OPT_LOCALISE },
{ '1', OPT_DBUS },
{ '3', OPT_BOOTP_DYNAMIC },
{ '5', OPT_NO_PING },
{ 'v', 0},
{ 'w', 0},
{ 'b', OPT_BOGUSPRIV },
{ 'f', OPT_FILTER },
{ 'q', OPT_LOG },
{ 'e', OPT_SELFMX },
{ 'h', OPT_NO_HOSTS },
{ 'n', OPT_NO_POLL },
{ 'd', OPT_DEBUG },
{ 'k', OPT_NO_FORK },
{ 'K', OPT_AUTHORITATIVE },
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
{ 'L', OPT_LOCALMX },
{ 'N', OPT_NO_NEG },
{ 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS },
{ 'y', OPT_LOCALISE },
{ '1', OPT_DBUS },
{ '3', OPT_BOOTP_DYNAMIC },
{ '5', OPT_NO_PING },
{ '9', OPT_LEASE_RO },
{ LOPT_RELOAD, OPT_RELOAD },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
};
@@ -195,7 +204,10 @@ static const struct {
{ "-5, --no-ping", gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
{ "-6, --dhcp-script=path", gettext_noop("Script to run on DHCP lease creation and destruction."), NULL },
{ "-7, --conf-dir=path", gettext_noop("Read configuration from all the files in this directory."), NULL },
{ "-8, --log-facility=facilty", gettext_noop("Log to this syslog facility."), NULL },
{ "-8, --log-facility=facilty", gettext_noop("Log to this syslog facility. (defaults to DAEMON)"), NULL },
{ "-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 },
{ NULL, NULL, NULL }
};
@@ -324,6 +336,8 @@ static void do_usage(void)
sprintf(buff, "%d", EDNS_PKTSZ);
else if (strcmp(usage[i].arg, "&") == 0)
sprintf(buff, "%d", MAXLEASES);
else if (strcmp(usage[i].arg, "!") == 0)
sprintf(buff, "%d", FTABSIZ);
else
strcpy(buff, usage[i].arg);
}
@@ -810,7 +824,12 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
if (!atoi_check(arg, &daemon->port))
option = '?';
break;
case '0':
if (!atoi_check(arg, &daemon->ftabsize))
option = '?';
break;
case 'P':
{
int i;
@@ -1818,6 +1837,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
/* Set defaults - everything else is zero or NULL */
daemon->cachesize = CACHESIZ;
daemon->ftabsize = FTABSIZ;
daemon->port = NAMESERVER_PORT;
daemon->default_resolv.is_default = 1;
daemon->default_resolv.name = RESOLVFILE;
@@ -1956,11 +1976,10 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
mx->target = daemon->mxtarget;
}
if (daemon->options & OPT_NO_RESOLV)
daemon->resolv_files = 0;
else if (daemon->resolv_files &&
(daemon->resolv_files)->next &&
(daemon->options & OPT_NO_POLL))
if (!(daemon->options & OPT_NO_RESOLV) &&
daemon->resolv_files &&
daemon->resolv_files->next &&
(daemon->options & OPT_NO_POLL))
die(_("only one resolv.conf file allowed in no-poll mode."), NULL);
if (daemon->options & OPT_RESOLV_DOMAIN)
@@ -1968,11 +1987,13 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
char *line;
FILE *f;
if (!daemon->resolv_files || (daemon->resolv_files)->next)
if ((daemon->options & OPT_NO_RESOLV) ||
!daemon->resolv_files ||
(daemon->resolv_files)->next)
die(_("must have exactly one resolv.conf to read domain from."), NULL);
if (!(f = fopen((daemon->resolv_files)->name, "r")))
die(_("failed to read %s: %m"), (daemon->resolv_files)->name);
die(_("failed to read %s: %s"), (daemon->resolv_files)->name);
while ((line = fgets(buff, MAXDNAME, f)))
{

View File

@@ -72,7 +72,7 @@ static void bootp_option_put(struct dhcp_packet *mess,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
static void log_packet(struct daemon *daemon, char *type, void *addr,
struct dhcp_packet *mess, char *interface, char *string);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
@@ -95,7 +95,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
struct dhcp_vendor *vendor;
struct dhcp_mac *mac;
struct dhcp_netid_list *id_list;
int clid_len = 0, ignore = 0;
int clid_len = 0, ignore = 0, do_classes = 0;
struct dhcp_packet *mess = daemon->dhcp_packet.iov_base;
unsigned char *p, *end = (unsigned char *)(mess + 1);
char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL;
@@ -214,7 +214,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
{
struct dhcp_context *context_tmp, *context_new = NULL;
struct in_addr addr = mess->ciaddr;
struct in_addr addr;
int force = 0;
if (subnet_addr.s_addr)
@@ -227,19 +227,33 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
addr = mess->giaddr;
force = 1;
}
else
{
/* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
addr = mess->ciaddr;
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_new = context;
break;
}
}
if (!context_new)
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context_new;
context_new = context_tmp;
}
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
if (context_tmp->netmask.s_addr &&
is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
is_same_net(addr, context_tmp->end, context_tmp->netmask))
{
context_tmp->current = context_new;
context_new = context_tmp;
}
if (context_new || force)
context = context_new;
}
if (!context)
@@ -461,7 +475,10 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
so zero the counts so that we don't get spurious matches between
the vendor string and the counts. If the lengths don't add up, we
assume that the option is a single string and non RFC3004 compliant
and just do the substring match. dhclient provides these broken options. */
and just do the substring match. dhclient provides these broken options.
The code, later, which sends user-class data to the lease-change script
relies on the transformation done here.
*/
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
@@ -552,7 +569,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
return 0;
case DHCPRELEASE:
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
if (!(context = narrow_context(context, mess->ciaddr)) ||
!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
(context->local.s_addr != option_addr(opt).s_addr))
return 0;
@@ -613,7 +631,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
message = _("no address available");
}
log_packet(daemon, "DISCOVER", opt ? (struct in_addr *)option_ptr(opt) : NULL, mess, iface_name, message);
log_packet(daemon, "DISCOVER", opt ? option_ptr(opt) : NULL, mess, iface_name, message);
if (message || !(context = narrow_context(context, mess->yiaddr)))
return 0;
@@ -651,6 +669,9 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
/* SELECTING or INIT_REBOOT */
mess->yiaddr = option_addr(opt);
/* send vendor and user class info for new or recreated lease */
do_classes = 1;
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
{
/* SELECTING */
@@ -743,11 +764,15 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
else if (!clid && mess->hlen == 0)
message = _("no unique-id");
else if (!lease &&
!(lease = lease_allocate(mess->yiaddr)))
message = _("no leases left");
else if (!lease)
{
if ((lease = lease_allocate(mess->yiaddr)))
do_classes = 1;
else
message = _("no leases left");
}
}
if (message)
{
log_packet(daemon, "NAK", &mess->yiaddr, mess, iface_name, message);
@@ -769,8 +794,43 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
}
else
{
if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr)))
{
if (do_classes)
{
lease->changed = 1;
/* copy user-class and vendor class into new lease, for the script */
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
if (lease->userclass)
free(lease->userclass);
if ((lease->userclass = malloc(len+1)))
{
memcpy(lease->userclass, ucp, len);
lease->userclass[len] = 0;
lease->userclass_len = len+1;
}
}
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt);
if (lease->vendorclass)
free(lease->vendorclass);
if ((lease->vendorclass = malloc(len+1)))
{
memcpy(lease->vendorclass, ucp, len);
lease->vendorclass[len] = 0;
lease->vendorclass_len = len+1;
}
}
}
if (!hostname_auth && (client_hostname = host_from_dns(daemon, mess->yiaddr)))
{
hostname = client_hostname;
hostname_auth = 1;
}
@@ -869,14 +929,20 @@ static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *
return time;
}
static void log_packet(struct daemon *daemon, char *type, struct in_addr *addr,
static void log_packet(struct daemon *daemon, char *type, void *addr,
struct dhcp_packet *mess, char *interface, char *string)
{
struct in_addr a;
/* addr may be misaligned */
if (addr)
memcpy(&a, addr, sizeof(a));
syslog(LOG_INFO, "%s%s(%s) %s%s%s %s",
type ? "DHCP" : "BOOTP",
type ? type : "",
interface,
addr ? inet_ntoa(*addr) : "",
addr ? inet_ntoa(a) : "",
addr ? " " : "",
print_mac(daemon, mess->chaddr, mess->hlen),
string ? string : "");

View File

@@ -410,3 +410,48 @@ char *print_mac(struct daemon *daemon, unsigned char *mac, int len)
return daemon->namebuff;
}
void bump_maxfd(int fd, int *max)
{
if (fd > *max)
*max = fd;
}
void log_start(struct daemon *daemon)
{
if (daemon->options & OPT_DEBUG)
{
#ifdef LOG_PERROR
openlog("dnsmasq", LOG_PERROR, daemon->log_fac);
#else
openlog("dnsmasq", 0, daemon->log_fac);
#endif
}
else
openlog("dnsmasq", LOG_PID, daemon->log_fac);
}
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 (errno == EINTR || errno == ENOMEM || errno == ENOBUFS)
goto retry;
else
return 0;
}
}
return 1;
}