import of dnsmasq-2.36.tar.gz

This commit is contained in:
Simon Kelley
2007-01-21 20:01:28 +00:00
parent 4011c4e05e
commit 832af0bafb
31 changed files with 5064 additions and 2851 deletions

View File

@@ -2036,5 +2036,54 @@ version 2.35
performance should be better. Thanks to "koko" for
pointing out the problem.
version 2.36
Added --dhcp-ignore-names flag which tells dnsmasq not to
use names provided by DHCP clients. Suggestion from
Thomas M Steenholdt.
Send netmask and broadcast address DHCP options always,
even if the client doesn't request them. This makes a few
odd clients work better.
Added simple TFTP function, optimised for net-boot. It is
now possible to net boot hosts using only dnsmasq. The
TFTP server is read-only, binary-mode only, and designed to be
secure; it adds about 4K to the dnsmasq binary.
Support DHCP option 120, SIP servers, (RFC 3361). Both
encodings are supported, so both --dhcp-option=120,192.168.2.3
and --dhcp-option=120,sip.example.net will work. Brian
Candler pointed out the need for this.
Allow spaces in domain names, to support DNS-SD.
Add --ptr-record flag, again for DNS-SD. Thanks to Stephan
Sokolow for the suggestion.
Tolerate leading space on lines in the config file. Thanks
to Luigi Rizzo for pointing this out.
Fixed netlink.c to cope with headers from the Linux 2.6.19
kernel. Thanks to Philip Wall for the bug report.
Added --dhcp-bridge option, but only to the FreeBSD
build. This fixes an oddity with a a particular bridged
network configuration on FreeBSD. Thanks to Luigi Rizzo
for the patch.
Added FAQ entry about running dnsmasq in a Linux
vserver. Thanks to Gildas le Nadan for the information.
Fixed problem with option parsing which interpreted "/" as
an address and not a string. Thanks to Luigi Rizzo
for the patch.
Ignore the --domain-needed flag when forwarding NS
and SOA queries, since NS queries of TLDs are always legit.
Marcus Better pointed out this problem.
Take care to forward signed DNS requests bit-perfect, so
as not to affect the validity of the signature. This
should allow DDNS updates to be forwarded.

24
FAQ
View File

@@ -311,7 +311,7 @@ A: Because when a Gentoo box shuts down, it releases its lease with
Q: My laptop has two network interfaces, a wired one and a wireless
one. I never use both interfaces at the same time, and I'd like the
same IP and configuration to be used irrespcetive of which
same IP and configuration to be used irrespective of which
interface is in use. How can I do that?
A: By default, the identity of a machine is determined by using the
@@ -393,6 +393,28 @@ A: Dnsmasq is in Suse itself, and the latest releases are also
available at ftp://ftp.suse.com/pub/people/ug/
Q: Can I run dnsmasq in a Linux vserver?
A: Yes, as a DNS server, dnsmasq will just work in a vserver.
To use dnsmasq's DHCP function you need to give the vserver
extra system capabilities. Please note that doing so will lesser
the overall security of your system. The capabilities
required are NET_ADMIN and NET_RAW. NET_ADMIN is essential, NET_RAW
is required to do an ICMP "ping" check on newly allocated
addresses. If you don't need this check, you can disable it with
--no-ping and omit the NET_RAW capability.
Adding the capabilities is done by adding them, one per line, to
either /etc/vservers/<vservername>/ccapabilities for a 2.4 kernel or
/etc/vservers/<vservername>/bcapabilities for a 2.6 kernel (please
refer to the vserver documentation for more information).

View File

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

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

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

View File

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

View File

@@ -37,7 +37,7 @@
# If you don't want dnsmasq to read /etc/resolv.conf or any other
# file, getting its servers from this file instead (see below), then
# uncomment this
# uncomment this.
#no-resolv
# If you don't want dnsmasq to poll /etc/resolv.conf or other resolv
@@ -206,12 +206,19 @@
# subnet mask - 1
# default router - 3
# DNS server - 6
# hostname - 12
# broadcast address - 28
# Override the default route supplied by dnsmasq, which assumes the
# router is the same machine as the one running dnsmasq.
#dhcp-option=3,1.2.3.4
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=42,192.168.0.4,10.10.0.5
@@ -262,9 +269,27 @@
# mtftp address to 0.0.0.0 for PXEClients
#dhcp-option=vendor:PXEClient,1,0.0.0.0
# Set the boot filename and tftpd server name and address
# for BOOTP. You will only need this is you want to
# boot machines over the network.
# Set the boot filename for BOOTP. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files availble via FTP.
#tftp-root=/var/ftpd
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# Set the boot file name only when the "red" tag is set.
#dhcp-boot=net:red,pxelinux.red-net
# An example of dhcp-boot with an external server: the name and IP
# address of the server are given after the filename.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
@@ -363,6 +388,11 @@
# example.com
#srv-host=_ldap._tcp.example.com
# The following line shows how to make dnsmasq serve an arbitrary PTR
# record. This is useful for DNS-SD. (Note that the
# domain-name expansion done for SRV records _does_not
# occur for PTR records.)
#ptr-record=_http._tcp.dns-sd-services,"New Employee Page._http._tcp.dns-sd-services"
# Change the following lines to enable dnsmasq to serve TXT records.
# These are used for things like SPF and zeroconf. (Note that the
@@ -370,7 +400,7 @@
# occur for TXT records.)
#Example SPF.
#txt-record=example.com,v=spf1 a -all
#txt-record=example.com,"v=spf1 a -all"
#Example zeroconf
#txt-record=_http._tcp.example.com,name=value,paper=A4

View File

@@ -11,7 +11,7 @@ Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
server and allows machines with DHCP-allocated addresses
to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic
DHCP leases and BOOTP for network booting of diskless machines.
DHCP leases and BOOTP/TFTP for network booting of diskless machines.
<P>
Dnsmasq is targeted at home networks using NAT and
connected to the internet via a modem, cable-modem or ADSL
@@ -83,7 +83,7 @@ for any or all local machines.
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
The tarball includes this documentation, source, and manpage.
There is also a <A HREF="CHANGELOG"> CHANGELOG</A>.
There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>.
Dnsmasq is part of the Debian distribution, it can be downloaded from
<A HREF="http://ftp.debian.org/debian/pool/main/d/dnsmasq/"> here</A> or installed using <TT>apt</TT>.

View File

@@ -24,7 +24,7 @@ Dnsmasq
supports IPv6.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
functions, for instance "--pid-file=" disables writing a PID file. On
functions, for instance "--pid-file" disables writing a PID file. On
BSD, unless the GNU getopt library is linked, the long form of the
options does not work on the command line; it is still recognised in
the configuration file.
@@ -37,6 +37,10 @@ Additional hosts file. Read the specified file as well as /etc/hosts. If -h is g
only the specified file. This option may be repeated for more than one
additional hosts file.
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names.
.TP
.B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
@@ -123,7 +127,7 @@ options does not matter and that
options always override the others.
.TP
.B \-2, --no-dhcp-interface=<interface name>
Do not provide DHCP on the specified interface, but do provide DNS service.
Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
.TP
.B \-a, --listen-address=<ipaddr>
Listen on the given IP address(es). Both
@@ -229,7 +233,7 @@ Tells dnsmasq to never forward queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
.B \-S, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source>[#<port>]]]
.B \-S, ,--local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source>[#<port>]]]
Specify IP address of upstream severs directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use -R to do that. If one or
more
@@ -322,6 +326,9 @@ all that match are returned.
Return a TXT DNS record. The value of TXT record is a set of strings,
so any number may be included, split by commas.
.TP
.B --ptr-record=<name>[,<target>]
Return a PTR DNS record.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
.TP
@@ -368,7 +375,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
@@ -449,9 +456,10 @@ and a text string. If the optional network-ids are given then
this option is only sent when all the network-ids are matched.
Special processing is done on a text argument for option 119, to
conform with RFC 3397, and dotted-quad IP addresses which are followed
by a slash and then a netmask size are encoded as described in RFC
3442.
conform with RFC 3397. Text or dotted-quad IP addresses as arguments
to option 120 are handled as per RFC 3361. Dotted-quad IP addresses
which are followed by a slash and then a netmask size are encoded as
described in RFC 3442.
Be careful: no checking is done that the correct type of data for the
option number is sent, it is quite possible to
@@ -508,10 +516,23 @@ When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore the host and do
not allocate it a DHCP lease.
.TP
.B --dhcp-ignore-name[=<network-id>[,<network-id>]]
When all the given network-ids match the set of network-ids derived
from the net, host, vendor and user classes, ignore any hostname
provided by the host. Note that, unlike dhcp-ignore, it is permissable
to supply no netid tags, in which case DHCP-client supplied hostnames
are always ignored, and DHCP hosts are added to the DNS using only
dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
/etc/ethers.
.TP
.B \-M, --dhcp-boot=[net:<network-id>,]<filename>,[<servername>[,<server address>]]
Set BOOTP options to be returned by the DHCP server. These are needed
for machines which network boot, and tell the machine where to collect
its initial configuration. If the optional network-id(s) are given,
Set BOOTP options to be returned by the DHCP server. Server name and
address are optional: if not provided, the name is left empty, and the
address set to the address of the machine running dnsmasq. If dnsmasq
is providing a TFTP service (see
.B --enable-tftp
) then only the filename is required here to enable network booting.
If the optional network-id(s) are given,
they must match for this configuration to be sent. Note that
network-ids are prefixed by "net:" to distinguish them.
.TP
@@ -602,6 +623,12 @@ 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 --bridge-interface=<interface>,<alias>[,<alias>]
Treat DHCP request packets arriving at any of the <alias> interfaces
as if they had arrived at <interface>. This option is only available
on FreeBSD and Dragonfly BSD, and is necessary when using "old style" bridging, since
packets arrive at tap interfaces which don't have an IP address.
.TP
.B \-s, --domain=<domain>
Specifies the domain for the DHCP server. This has two effects;
firstly it causes the DHCP server to return the domain to any hosts
@@ -614,9 +641,36 @@ both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
given as "#" then the domain is read from the first "search" directive
in /etc/resolv.conf (or equivalent).
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
in the same way as for DHCP-derived names.
.B --enable-tftp
Enable the TFTP server function. This is deliberately limited to that
needed to net-boot a client: Only reading is allowed, and only in
binary/octet mode. The tsize and blksize extensions are supported.
.TP
.B --tftp-root=<directory>
Look for files to transfer using TFTP relative to the given
directory. When this is set, TFTP paths which include ".." are
rejected, to stop clients getting outside the specified root.
.TP
.B --tftp-secure
Enable TFTP secure mode: without this, any file which is readble by
the dnsmasq process under normal unix access-control rules is
available via TFTP. When the --tftp-secure flag is given, only files
owned by the user running the dnsmasq process are accessible. If
dnsmasq is being run as root, different rules apply: --tftp-secure
has not effect, but only files which have the world-readable bit set
are accessible. It is not recommended to run dnsmasq as root with TFTP
enabled, and certainly not without specifying --tftp-root. Doing so
can expose any world-readable file on the server to any host on the net.
.TP
.B --tftp-max=<connections>
Set the maximum number of concurrent TFTP connections allowed. This
defaults to 50. When serving a large number of TFTP connections,
per-process file descriptor limits may be encountered. Dnsmasq needs
one file descriptor for each concurrent TFTP connection and one
file descriptor per unique file (plus a few others). So serving the
same file simultaneously to n clients will use require about n + 10 file
descriptors, serving different files simultaneously to n clients will
require about (2*n) + 10 descriptors.
.TP
.B \-C, --conf-file=<file>
Specify a different configuration file. The conf-file option is also allowed in

602
po/de.po

File diff suppressed because it is too large Load Diff

500
po/es.po

File diff suppressed because it is too large Load Diff

602
po/fi.po

File diff suppressed because it is too large Load Diff

500
po/fr.po

File diff suppressed because it is too large Load Diff

515
po/id.po

File diff suppressed because it is too large Load Diff

602
po/it.po

File diff suppressed because it is too large Load Diff

500
po/no.po

File diff suppressed because it is too large Load Diff

500
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

500
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,8 @@ static const struct {
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
{ 48, "DNSKEY" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
{ 252, "AXFR" },
@@ -636,6 +638,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
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)) &&
@@ -644,6 +647,9 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
flags &= ~F_REVERSE;
break;
}
if (lookup)
break;
}
cache->flags = flags;
cache->uid = index;
@@ -997,6 +1003,8 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
strcpy(addrbuff, "<SRV>");
else if (flags & F_NXDOMAIN)
strcpy(addrbuff, "<TXT>");
else if (flags & F_BIGNAME)
strcpy(addrbuff, "<PTR>");
else
strcpy(addrbuff, "<CNAME>");
}

View File

@@ -10,7 +10,7 @@
GNU General Public License for more details.
*/
#define VERSION "2.35"
#define VERSION "2.36"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -48,6 +48,8 @@
#define CHGRP "dip"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
@@ -55,6 +57,10 @@
/* A small collection of RR-types which are missing on some platforms */
#ifndef T_SIG
# define T_SIG 24
#endif
#ifndef T_SRV
# define T_SRV 33
#endif
@@ -63,6 +69,15 @@
# define T_OPT 41
#endif
#ifndef T_TKEY
# define T_TKEY 249
#endif
#ifndef T_TSIG
# define T_TSIG 250
#endif
/* Get linux C library versions. */
#if defined(__linux__) && !defined(__UCLIBC__) && !defined(__uClinux__)
/*# include <libio.h> */
@@ -98,6 +113,9 @@ HAVE_ISC_READER
define this to include the old ISC dhcpcd integration. Note that you cannot
set both HAVE_ISC_READER and HAVE_BROKEN_RTC.
HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
@@ -153,6 +171,7 @@ NOTES:
*/
/* platform independent options- uncomment to enable */
#define HAVE_TFTP
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_ISC_READER */
/* #define HAVE_DBUS */
@@ -161,6 +180,11 @@ NOTES:
# error HAVE_ISC_READER is not compatible with HAVE_BROKEN_RTC
#endif
/* Allow TFTP to be disabled with CFLAGS=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
/* platform dependent options. */
/* Must preceed __linux__ since uClinux defines __linux__ too. */

View File

@@ -117,7 +117,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0;
struct in_addr iface_addr;
struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm;
union {
@@ -198,16 +198,30 @@ void dhcp_packet(struct daemon *daemon, time_t now)
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1 )
return;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
{
addrp = &iface_addr;
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (!iface_check(daemon, AF_INET, (struct all_addr *)addrp, &ifr, &iface_index))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
if (!iface_check(daemon, AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
/* interface may have been changed by alias in iface_check */
if (!addrp)
{
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1)
{
syslog(LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)

View File

@@ -109,6 +109,11 @@ int main (int argc, char **argv)
}
#endif
#ifndef HAVE_TFTP
if (daemon->options & OPT_TFTP)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL);
#endif
daemon->interfaces = NULL;
if (!enumerate_interfaces(daemon))
die(_("failed to find list of interfaces: %s"), NULL);
@@ -128,7 +133,7 @@ int main (int argc, char **argv)
die(_("no interface with address %s"), daemon->namebuff);
}
}
else if (!(daemon->listeners = create_wildcard_listeners(daemon->port)))
else if (!(daemon->listeners = create_wildcard_listeners(daemon->port, daemon->options & OPT_TFTP)))
die(_("failed to create listening socket: %s"), NULL);
cache_init(daemon->cachesize, daemon->options & OPT_LOG);
@@ -381,6 +386,45 @@ int main (int argc, char **argv)
}
}
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
{
long max_fd = sysconf(_SC_OPEN_MAX);
#ifdef FD_SETSIZE
if (FD_SETSIZE < max_fd)
max_fd = FD_SETSIZE;
#endif
syslog(LOG_INFO, "TFTP %s%s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "",
daemon->options & OPT_TFTP_SECURE ? _("secure mode") : "");
/* This is a guess, it assumes that for small limits,
disjoint files might be servered, but for large limits,
a single file will be sent to may clients (the file only needs
one fd). */
max_fd -= 30; /* use other than TFTP */
if (max_fd < 0)
max_fd = 5;
else if (max_fd < 100)
max_fd = max_fd/2;
else
max_fd = max_fd - 20;
if (daemon->tftp_max > max_fd)
{
daemon->tftp_max = max_fd;
syslog(LOG_WARNING,
_("restricting maximum simultaneous TFTP transfers to %d"),
daemon->tftp_max);
}
}
#endif
if (!(daemon->options & OPT_DEBUG) && (getuid() == 0 || geteuid() == 0))
{
if (bad_capabilities)
@@ -414,15 +458,16 @@ int main (int argc, char **argv)
tp = &t;
}
#ifdef HAVE_DBUS
/* Whilst polling for the dbus, wake every quarter second */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
/* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
if (daemon->tftp_trans ||
((daemon->options & OPT_DBUS) && !daemon->dbus))
{
t.tv_sec = 0;
t.tv_usec = 250000;
tp = &t;
}
#ifdef HAVE_DBUS
set_dbus_listeners(daemon, &maxfd, &rset, &wset, &eset);
#endif
@@ -615,6 +660,10 @@ int main (int argc, char **argv)
check_dns_listeners(daemon, &rset, now);
#ifdef HAVE_TFTP
check_tftp_listeners(daemon, &rset, now);
#endif
if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
dhcp_packet(daemon, now);
@@ -669,6 +718,17 @@ static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int
struct listener *listener;
int wait, i;
#ifdef HAVE_TFTP
int tftp = 0;
struct tftp_transfer *transfer;
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
FD_SET(transfer->sockfd, set);
bump_maxfd(transfer->sockfd, maxfdp);
}
#endif
/* will we be able to get memory? */
get_new_frec(daemon, now, &wait);
@@ -696,6 +756,15 @@ static int set_dns_listeners(struct daemon *daemon, time_t now, fd_set *set, int
bump_maxfd(listener->tcpfd, maxfdp);
break;
}
#ifdef HAVE_TFTP
if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
{
FD_SET(listener->tftpfd, set);
bump_maxfd(listener->tftpfd, maxfdp);
}
#endif
}
return wait;
@@ -715,6 +784,11 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (FD_ISSET(listener->fd, set))
receive_query(listener, daemon, now);
#ifdef HAVE_TFTP
if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
tftp_request(listener, daemon, now);
#endif
if (FD_ISSET(listener->tcpfd, set))
{
int confd;
@@ -841,7 +915,7 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
/* Try and get an ICMP echo from a machine. */
/* Note that whilst in the three second wait, we check for
(and service) events on the DNS sockets, (so doing that
(and service) events on the DNS and TFTP sockets, (so doing that
better not use any resources our caller has in use...)
but we remain deaf to signals or further DHCP packets. */
@@ -907,6 +981,10 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
now = dnsmasq_time();
check_dns_listeners(daemon, &rset, now);
#ifdef HAVE_TFTP
check_tftp_listeners(daemon, &rset, now);
#endif
if (FD_ISSET(fd, &rset) &&
recvfrom(fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&faddr, &len) == sizeof(packet) &&

View File

@@ -88,30 +88,32 @@ extern int capset(cap_user_header_t header, cap_user_data_t data);
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV 1
#define OPT_FILTER 2
#define OPT_LOG 4
#define OPT_SELFMX 8
#define OPT_NO_HOSTS 16
#define OPT_NO_POLL 32
#define OPT_DEBUG 64
#define OPT_ORDER 128
#define OPT_NO_RESOLV 256
#define OPT_EXPAND 512
#define OPT_LOCALMX 1024
#define OPT_NO_NEG 2048
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
#define OPT_RESOLV_DOMAIN 32768
#define OPT_NO_FORK 65536
#define OPT_AUTHORITATIVE 131072
#define OPT_LOCALISE 262144
#define OPT_DBUS 524288
#define OPT_BOOTP_DYNAMIC 1048576
#define OPT_NO_PING 2097152
#define OPT_LEASE_RO 4194304
#define OPT_RELOAD 8388608
#define OPT_BOGUSPRIV (1<<0)
#define OPT_FILTER (1<<1)
#define OPT_LOG (1<<2)
#define OPT_SELFMX (1<<3)
#define OPT_NO_HOSTS (1<<4)
#define OPT_NO_POLL (1<<5)
#define OPT_DEBUG (1<<6)
#define OPT_ORDER (1<<7)
#define OPT_NO_RESOLV (1<<8)
#define OPT_EXPAND (1<<9)
#define OPT_LOCALMX (1<<10)
#define OPT_NO_NEG (1<<11)
#define OPT_NODOTS_LOCAL (1<<12)
#define OPT_NOWILD (1<<13)
#define OPT_ETHERS (1<<14)
#define OPT_RESOLV_DOMAIN (1<<15)
#define OPT_NO_FORK (1<<16)
#define OPT_AUTHORITATIVE (1<<17)
#define OPT_LOCALISE (1<<18)
#define OPT_DBUS (1<<19)
#define OPT_BOOTP_DYNAMIC (1<<20)
#define OPT_NO_PING (1<<21)
#define OPT_LEASE_RO (1<<22)
#define OPT_RELOAD (1<<24)
#define OPT_TFTP (1<<25)
#define OPT_TFTP_SECURE (1<<26)
struct all_addr {
union {
@@ -146,6 +148,11 @@ struct txt_record {
struct txt_record *next;
};
struct ptr_record {
char *name, *ptr;
struct ptr_record *next;
};
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -239,11 +246,12 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok;
struct irec *next;
};
struct listener {
int fd, tcpfd, family;
int fd, tcpfd, tftpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct listener *next;
};
@@ -274,7 +282,7 @@ struct hostsfile {
struct frec {
union mysockaddr source;
struct all_addr dest;
struct server *sentto;
struct server *sentto; /* NULL means free */
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall;
@@ -380,6 +388,13 @@ struct dhcp_mac {
struct dhcp_mac *next;
};
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge {
char iface[IF_NAMESIZE];
struct dhcp_bridge *alias, *next;
};
#endif
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast;
@@ -415,6 +430,23 @@ struct ping_result {
struct ping_result *next;
};
struct tftp_file {
int refcount, fd;
off_t size;
char filename[];
};
struct tftp_transfer {
int sockfd;
time_t timeout;
int backoff;
unsigned int block, blocksize;
struct sockaddr_in peer;
char opt_blocksize, opt_transize;
struct tftp_file *file;
struct tftp_transfer *next;
};
struct daemon {
/* datastuctures representing the command-line and
config file arguments. All set (including defaults)
@@ -424,6 +456,7 @@ struct daemon {
struct resolvc default_resolv, *resolv_files;
struct mx_srv_record *mxnames;
struct txt_record *txt;
struct ptr_record *ptr;
char *mxtarget;
char *lease_file;
char *username, *groupname;
@@ -444,8 +477,8 @@ struct daemon {
struct dhcp_vendor *dhcp_vendors;
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
struct dhcp_netid_list *dhcp_ignore;
int dhcp_max;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names;
int dhcp_max, tftp_max;
unsigned int min_leasetime;
struct doctor *doctors;
unsigned short edns_pktsz;
@@ -473,14 +506,20 @@ struct daemon {
char *dhcp_buff, *dhcp_buff2;
struct ping_result *ping_results;
FILE *lease_stream;
#if defined(__FreeBSD__) || defined(__DragonFly__)
struct dhcp_bridge *bridges;
#endif
/* DBus stuff */
#ifdef HAVE_DBUS
/* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus;
#ifdef HAVE_DBUS
struct watch *watches;
#endif
/* TFTP stuff */
struct tftp_transfer *tftp_trans;
char *tftp_prefix;
};
/* cache.c */
@@ -515,7 +554,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, size_t plen,
size_t *len, unsigned char **p);
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
unsigned int questions_crc(HEADER *header, size_t plen, char *buff);
size_t resize_packet(HEADER *header, size_t plen,
@@ -563,10 +602,10 @@ struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
int reload_servers(char *fname, struct daemon *daemon);
void check_servers(struct daemon *daemon);
int enumerate_interfaces(struct daemon *daemon);
struct listener *create_wildcard_listeners(int port);
struct listener *create_wildcard_listeners(int port, int have_tftp);
struct listener *create_bound_listeners(struct daemon *daemon);
int iface_check(struct daemon *daemon, int family,
struct all_addr *addr, char *name);
int iface_check(struct daemon *daemon, int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp);
int fix_fd(int fd);
/* dhcp.c */
@@ -652,3 +691,9 @@ 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);
/* tftp.c */
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now);
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now);
#endif

View File

@@ -14,11 +14,11 @@
static struct frec *frec_list = NULL;
static struct frec *lookup_frec(unsigned short id);
static struct frec *lookup_frec(unsigned short id, unsigned int crc);
static struct frec *lookup_frec_by_sender(unsigned short id,
union mysockaddr *addr,
unsigned int crc);
static unsigned short get_id(void);
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
/* Send a UDP packet with it's source address set as "source"
@@ -163,7 +163,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
{
if ((sflag | F_QUERY ) & qtype)
{
flags = qtype;
flags = qtype & ~F_BIGNAME;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
@@ -184,7 +184,9 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else
log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
}
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
else if (qtype && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception from NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
@@ -237,12 +239,16 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
if (forward)
{
/* force unchanging id for signed packets */
int is_sign;
find_pseudoheader(header, plen, NULL, NULL, &is_sign);
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->iface = dst_iface;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(is_sign, forward->orig_id, crc);
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
header->id = htons(forward->new_id);
@@ -325,7 +331,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->new_id = 0; /* cancel */
forward->sentto = NULL; /* cancel */
}
/* could not send on, return empty answer or address if known for whole domain */
@@ -339,17 +345,17 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
}
static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
unsigned int query_crc, struct server *server, size_t n)
struct server *server, size_t n)
{
unsigned char *pheader, *sizep;
int munged = 0;
int munged = 0, is_sign;
size_t plen;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. */
requests for the client. We can't do this for signed packets. */
if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
{
unsigned short udpsz;
unsigned char *psave = sizep;
@@ -359,7 +365,7 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
if (is_sign || header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
@@ -393,10 +399,6 @@ static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
header->rcode = NOERROR;
}
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (query_crc == questions_crc(header, n, daemon->namebuff))
extract_addresses(header, n, daemon->namebuff, now, daemon);
}
@@ -421,9 +423,9 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
struct frec *forward;
HEADER *header;
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
size_t nn;
@@ -439,9 +441,9 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
#endif
header = (HEADER *)daemon->packet;
forward = lookup_frec(ntohs(header->id));
if (n >= (int)sizeof(HEADER) && header->qr && forward)
if (n >= (int)sizeof(HEADER) && header->qr &&
(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
{
struct server *server = forward->sentto;
@@ -450,9 +452,12 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
{
unsigned char *pheader;
size_t plen;
int is_sign;
/* recreate query from reply */
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL);
pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
if (!is_sign)
{
header->ancount = htons(0);
header->nscount = htons(0);
header->arcount = htons(0);
@@ -464,6 +469,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
return;
}
}
}
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
@@ -491,14 +497,14 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
if ((nn = process_reply(daemon, header, now, forward->crc, server, (size_t)n)))
if ((nn = process_reply(daemon, header, now, server, (size_t)n)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
forward->new_id = 0; /* cancel */
forward->sentto = NULL; /* cancel */
}
}
}
@@ -611,8 +617,6 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (if_index == 0)
return;
if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
{
#ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index;
if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
@@ -622,6 +626,9 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
return;
#endif
if (!iface_check(daemon, listen->family, &dst_addr, &ifr, &if_index))
return;
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
@@ -630,10 +637,6 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
if (!iface_check(daemon, listen->family, &dst_addr, ifr.ifr_name))
return;
}
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
if (listen->family == AF_INET)
@@ -788,9 +791,13 @@ unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
#endif
/* There's no point in updating the cache, since this process will exit and
lose the information after one query. We make this call for the alias and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
m = process_reply(daemon, header, now, crc, last_server, (unsigned int)m);
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(daemon, header, now, last_server, (unsigned int)m);
break;
}
@@ -818,7 +825,7 @@ static struct frec *allocate_frec(time_t now)
{
f->next = frec_list;
f->time = now;
f->new_id = 0;
f->sentto = NULL;
frec_list = f;
}
@@ -837,7 +844,7 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
*wait = 0;
for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
if (f->new_id == 0)
if (!f->sentto)
{
f->time = now;
return f;
@@ -858,7 +865,7 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
if (!wait)
{
oldest->new_id = 0;
oldest->sentto = 0;
oldest->time = now;
}
return oldest;
@@ -879,12 +886,14 @@ struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
return f; /* OK if malloc fails and this is NULL */
}
static struct frec *lookup_frec(unsigned short id)
/* crc is all-ones if not known. */
static struct frec *lookup_frec(unsigned short id, unsigned int crc)
{
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id == id)
if (f->sentto && f->new_id == id &&
(f->crc == crc || crc == 0xffffffff))
return f;
return NULL;
@@ -897,7 +906,7 @@ static struct frec *lookup_frec_by_sender(unsigned short id,
struct frec *f;
for(f = frec_list; f; f = f->next)
if (f->new_id &&
if (f->sentto &&
f->orig_id == id &&
f->crc == crc &&
sockaddr_isequal(&f->source, addr))
@@ -912,8 +921,8 @@ void server_gone(struct daemon *daemon, struct server *server)
struct frec *f;
for (f = frec_list; f; f = f->next)
if (f->new_id != 0 && f->sentto == server)
f->new_id = 0;
if (f->sentto && f->sentto == server)
f->sentto = NULL;
if (daemon->last_server == server)
daemon->last_server = NULL;
@@ -922,19 +931,24 @@ void server_gone(struct daemon *daemon, struct server *server)
daemon->srv_save = NULL;
}
/* return unique random ids between 1 and 65535 */
static unsigned short get_id(void)
/* return unique random ids.
For signed packets we can't change the ID without breaking the
signing, so we keep the same one. In this case force is set, and this
routine degenerates into killing any conflicting forward record. */
static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
{
unsigned short ret = 0;
while (ret == 0)
if (force)
{
ret = rand16();
/* scrap ids already in use */
if ((ret != 0) && lookup_frec(ret))
ret = 0;
struct frec *f = lookup_frec(force_id, crc);
if (f)
f->sentto = NULL; /* free */
ret = force_id;
}
else do
ret = rand16();
while (lookup_frec(ret, crc));
return ret;
}

View File

@@ -18,6 +18,14 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
/* linux 2.6.19 buggers up the headers, patch it up here. */
#ifndef IFA_RTA
# define IFA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
# include <linux/if_addr.h>
#endif
static struct iovec iov;
static void nl_err(struct nlmsghdr *h);

View File

@@ -12,7 +12,8 @@
#include "dnsmasq.h"
int iface_check(struct daemon *daemon, int family, struct all_addr *addr, char *name)
int iface_check(struct daemon *daemon, int family, struct all_addr *addr,
struct ifreq *ifr, int *indexp)
{
struct iname *tmp;
int ret = 1;
@@ -20,16 +21,49 @@ int iface_check(struct daemon *daemon, int family, struct all_addr *addr, char *
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (daemon->if_names || daemon->if_addrs)
if (indexp)
{
#if defined(__FreeBSD__) || defined(__DragonFly__)
/* One form of bridging on FreeBSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(ifr->ifr_name, alias->iface, IF_NAMESIZE) == 0)
{
int newindex;
if (!(newindex = if_nametoindex(bridge->iface)))
{
syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr->ifr_name);
return 0;
}
else
{
*indexp = newindex;
strncpy(ifr->ifr_name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
#endif
}
if (daemon->if_names || (addr && daemon->if_addrs))
{
ret = 0;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
if (tmp->name && (strcmp(tmp->name, ifr->ifr_name) == 0))
ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (tmp->addr.sa.sa_family == family)
if (addr && tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
@@ -44,7 +78,7 @@ int iface_check(struct daemon *daemon, int family, struct all_addr *addr, char *
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
if (tmp->name && (strcmp(tmp->name, ifr->ifr_name) == 0))
ret = 0;
return ret;
@@ -56,6 +90,8 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
struct irec *iface;
int fd;
struct ifreq ifr;
int dhcp_ok = 1;
struct iname *tmp;
/* check whether the interface IP has been added already
we call this routine multiple times. */
@@ -109,12 +145,16 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
}
if (addr->sa.sa_family == AF_INET &&
!iface_check(daemon, AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
!iface_check(daemon, AF_INET, (struct all_addr *)&addr->in.sin_addr, &ifr, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
dhcp_ok = 0;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(daemon, AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
!iface_check(daemon, AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, &ifr, NULL))
return 1;
#endif
@@ -123,6 +163,7 @@ static int iface_allowed(struct daemon *daemon, struct irec **irecp, int if_inde
{
iface->addr = *addr;
iface->netmask = netmask;
iface->dhcp_ok = dhcp_ok;
iface->next = *irecp;
*irecp = iface;
return 1;
@@ -239,6 +280,7 @@ static int create_ipv6_listener(struct listener **link, int port)
l = safe_malloc(sizeof(struct listener));
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = -1;
l->family = AF_INET6;
l->next = NULL;
*link = l;
@@ -247,12 +289,12 @@ static int create_ipv6_listener(struct listener **link, int port)
}
#endif
struct listener *create_wildcard_listeners(int port)
struct listener *create_wildcard_listeners(int port, int have_tftp)
{
union mysockaddr addr;
int opt = 1;
struct listener *l, *l6 = NULL;
int tcpfd, fd;
int tcpfd, fd, tftpfd = -1;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
@@ -284,10 +326,31 @@ struct listener *create_wildcard_listeners(int port)
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
#ifdef HAVE_TFTP
if (have_tftp)
{
addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return NULL;
if (setsockopt(tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(tftpfd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(tftpfd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tftpfd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(tftpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
}
#endif
l = safe_malloc(sizeof(struct listener));
l->family = AF_INET;
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = tftpfd;
l->next = l6;
return l;
@@ -295,7 +358,6 @@ struct listener *create_wildcard_listeners(int port)
struct listener *create_bound_listeners(struct daemon *daemon)
{
struct listener *listeners = NULL;
struct irec *iface;
int opt = 1;
@@ -306,6 +368,8 @@ struct listener *create_bound_listeners(struct daemon *daemon)
new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = listeners;
new->tftpfd = -1;
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
@@ -347,6 +411,18 @@ struct listener *create_bound_listeners(struct daemon *daemon)
if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL);
}
if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
{
short save = iface->addr.in.sin_port;
iface->addr.in.sin_port = htons(TFTP_PORT);
if ((new->tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tftpfd) ||
bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
die(_("failed to create TFTP socket: %s"), NULL);
iface->addr.in.sin_port = save;
}
}
return listeners;

View File

@@ -28,7 +28,13 @@ struct myoption {
/* options which don't have a one-char version */
#define LOPT_RELOAD 256
#define LOPT_NO_NAMES 257
#define LOPT_TFTP 258
#define LOPT_SECURE 259
#define LOPT_PREFIX 260
#define LOPT_PTR 261
#define LOPT_BRIDGE 262
#define LOPT_TFTP_MAX 263
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -42,14 +48,14 @@ static const struct myoption opts[] =
{"help", 0, 0, 'w'},
{"no-daemon", 0, 0, 'd'},
{"log-queries", 0, 0, 'q'},
{"user", 1, 0, 'u'},
{"group", 1, 0, 'g'},
{"resolv-file", 1, 0, 'r'},
{"user", 2, 0, 'u'},
{"group", 2, 0, 'g'},
{"resolv-file", 2, 0, 'r'},
{"mx-host", 1, 0, 'm'},
{"mx-target", 1, 0, 't'},
{"cache-size", 1, 0, 'c'},
{"cache-size", 2, 0, 'c'},
{"port", 1, 0, 'p'},
{"dhcp-leasefile", 1, 0, 'l'},
{"dhcp-leasefile", 2, 0, 'l'},
{"dhcp-lease", 1, 0, 'l' },
{"dhcp-host", 1, 0, 'G'},
{"dhcp-range", 1, 0, 'F'},
@@ -63,12 +69,12 @@ static const struct myoption opts[] =
{"bogus-nxdomain", 1, 0, 'B'},
{"selfmx", 0, 0, 'e'},
{"filterwin2k", 0, 0, 'f'},
{"pid-file", 1, 0, 'x'},
{"pid-file", 2, 0, 'x'},
{"strict-order", 0, 0, 'o'},
{"server", 1, 0, 'S'},
{"local", 1, 0, 'S' },
{"address", 1, 0, 'A' },
{"conf-file", 1, 0, 'C'},
{"conf-file", 2, 0, 'C'},
{"no-resolv", 0, 0, 'R'},
{"expand-hosts", 0, 0, 'E'},
{"localmx", 0, 0, 'L'},
@@ -102,6 +108,15 @@ static const struct myoption opts[] =
{"leasefile-ro", 0, 0, '9'},
{"dns-forward-max", 1, 0, '0'},
{"clear-on-reload", 0, 0, LOPT_RELOAD },
{"dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{"enable-tftp", 0, 0, LOPT_TFTP },
{"tftp-secure", 0, 0, LOPT_SECURE },
{"tftp-root", 1, 0, LOPT_PREFIX },
{"tftp-max", 1, 0, LOPT_TFTP_MAX },
{"ptr-record", 1, 0, LOPT_PTR },
#if defined(__FreeBSD__) || defined(__DragonFly__)
{"bridge-interface", 1, 0 , LOPT_BRIDGE },
#endif
{ NULL, 0, 0, 0 }
};
@@ -134,6 +149,8 @@ static const struct optflags optmap[] = {
{ '5', OPT_NO_PING },
{ '9', OPT_LEASE_RO },
{ LOPT_RELOAD, OPT_RELOAD },
{ LOPT_TFTP, OPT_TFTP },
{ LOPT_SECURE, OPT_TFTP_SECURE },
{ 'v', 0},
{ 'w', 0},
{ 0, 0 }
@@ -191,16 +208,20 @@ static const struct {
{ "-V, --alias=addr,addr,mask", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
{ "-W, --srv-host=name,target,...", gettext_noop("Specify a SRV record."), NULL },
{ "-w, --help", gettext_noop("Display this message."), NULL },
{ "-x, --pid-file=path", gettext_noop("Specify path of PID file. (defaults to %s)."), RUNFILE },
{ "-x, --pid-file=path", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
{ "-X, --dhcp-lease-max=number", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
{ "-y, --localise-queries", gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
{ "-Y --txt-record=name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
{ " --ptr-record=name,target", gettext_noop("Specify PTR DNS record."), NULL },
{ "-z, --bind-interfaces", gettext_noop("Bind only to interfaces in use."), NULL },
{ "-Z, --read-ethers", gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
{ "-1, --enable-dbus", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
{ "-2, --no-dhcp-interface=interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
{ "-3, --bootp-dynamic", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
{ "-4, --dhcp-mac=<id>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
#if defined(__FreeBSD__) || defined(__DragonFly__)
{ " --bridge-interface=iface,alias,..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
#endif
{ "-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 },
@@ -208,6 +229,11 @@ static const struct {
{ "-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 },
{ " --dhcp-ignore-names[=<id>]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
{ " --enable-tftp", gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ " --tftp-root=<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ " --tftp-secure", gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ " --tftp-max=<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
{ NULL, NULL, NULL }
};
@@ -318,7 +344,19 @@ static void add_txt(struct daemon *daemon, char *name, char *txt)
static void do_usage(void)
{
char buff[100];
int i;
int i, j;
struct {
char handle;
int val;
} tab[] = {
{ '$', CACHESIZ },
{ '*', EDNS_PKTSZ },
{ '&', MAXLEASES },
{ '!', FTABSIZ },
{ '#', TFTP_MAX_CONNECTIONS },
{ '\0', 0 }
};
printf(_("Usage: dnsmasq [options]\n\n"));
#ifndef HAVE_GETOPT_LONG
@@ -330,16 +368,10 @@ static void do_usage(void)
{
if (usage[i].arg)
{
if (strcmp(usage[i].arg, "$") == 0)
sprintf(buff, "%d", CACHESIZ);
else if (strcmp(usage[i].arg, "*") == 0)
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);
for (j = 0; tab[j].handle; j++)
if (tab[j].handle == *(usage[i].arg))
sprintf(buff, "%d", tab[j].val);
}
printf("%-36.36s", usage[i].flag);
printf(_(usage[i].desc), buff);
@@ -347,6 +379,282 @@ static void do_usage(void)
}
}
/* This is too insanely large to keep in-line in the switch */
static char *parse_dhcp_opt(struct daemon *daemon, char *arg)
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
char lenchar = 0, *cp;
int addrs, digs, is_addr, is_hex, is_dec;
char *comma, *problem = NULL;
new->len = 0;
new->flags = 0;
new->netid = NULL;
new->val = NULL;
new->vendor_class = NULL;
if ((comma = safe_strchr(arg, ',')))
{
struct dhcp_netid *np = NULL;
*comma++ = 0;
do {
for (cp = arg; *cp; cp++)
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
break;
if (!*cp)
break;
if (strstr(arg, "vendor:") == arg)
new->vendor_class = (unsigned char *)safe_string_alloc(arg+7);
else
{
new->netid = safe_malloc(sizeof (struct dhcp_netid));
/* allow optional "net:" for consistency */
if (strstr(arg, "net:") == arg)
new->netid->net = safe_string_alloc(arg+4);
else
new->netid->net = safe_string_alloc(arg);
new->netid->next = np;
np = new->netid;
}
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*comma++ = 0;
} while (arg);
}
if (!arg || (new->opt = atoi(arg)) == 0)
problem = _("bad dhcp-option");
else if (comma)
{
/* characterise the value */
is_addr = is_hex = is_dec = 1;
addrs = digs = 1;
for (cp = comma; *cp; cp++)
if (*cp == ',')
{
addrs++;
is_dec = is_hex = 0;
}
else if (*cp == ':')
{
digs++;
is_dec = is_addr = 0;
}
else if (*cp == '/')
{
is_dec = is_hex = 0;
if (cp == comma) /* leading / means a pathname */
is_addr = 0;
}
else if (*cp == '.')
is_dec = is_hex = 0;
else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
{
is_addr = 0;
if (cp[1] == 0 && is_dec &&
(*cp == 'b' || *cp == 's' || *cp == 'i'))
{
lenchar = *cp;
*cp = 0;
}
else
is_dec = 0;
if (!((*cp >='A' && *cp <= 'F') ||
(*cp >='a' && *cp <= 'f')))
is_hex = 0;
}
if (is_hex && digs > 1)
{
new->len = digs;
new->val = safe_malloc(new->len);
parse_hex(comma, new->val, digs, NULL, NULL);
}
else if (is_dec)
{
int i, val = atoi(comma);
/* assume numeric arg is 1 byte except for
options where it is known otherwise.
For vendor class option, we have to hack. */
new->len = 1;
if (lenchar == 'b')
new->len = 1;
else if (lenchar == 's')
new->len = 2;
else if (lenchar == 'i')
new->len = 4;
else if (new->vendor_class)
{
if (val & 0xffff0000)
new->len = 4;
else if (val & 0xff00)
new->len = 2;
}
else
switch (new->opt)
{
case 13: case 22: case 25: case 26:
new->len = 2;
break;
case 2: case 24: case 35: case 38:
new->len = 4;
break;
}
new->val = safe_malloc(new->len);
for (i=0; i<new->len; i++)
new->val[i] = val>>((new->len - i - 1)*8);
}
else if (is_addr)
{
struct in_addr in;
unsigned char *op;
char *slash;
/* max length of address/subnet descriptor is five bytes,
add one for the option 120 enc byte too */
new->val = op = safe_malloc((5 * addrs) + 1);
if (!new->vendor_class)
{
if (new->opt == 120)
*(op++) = 1; /* RFC 3361 "enc byte" */
else
new->flags |= DHOPT_ADDR;
}
while (addrs--)
{
cp = comma;
if ((comma = strchr(cp, ',')))
*comma++ = 0;
if ((slash = strchr(cp, '/')))
*slash++ = 0;
in.s_addr = inet_addr(cp);
if (!slash)
{
memcpy(op, &in, INADDRSZ);
op += INADDRSZ;
}
else
{
unsigned char *p = (unsigned char *)&in;
int netsize = atoi(slash);
*op++ = netsize;
if (netsize > 0)
*op++ = *p++;
if (netsize > 8)
*op++ = *p++;
if (netsize > 16)
*op++ = *p++;
if (netsize > 24)
*op++ = *p++;
new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
}
}
new->len = op - new->val;
}
else
{
/* text arg */
if ((new->opt == 119 || new->opt == 120) && !new->vendor_class)
{
/* dns search, RFC 3397, or SIP, RFC 3361 */
unsigned char *q, *r, *tail;
unsigned char *p, *m = NULL;
size_t newlen, len = 0;
int header_size = (new->opt == 119) ? 0 : 1;
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*(comma++) = 0;
while (arg && *arg)
{
if (!canonicalise_opt(arg))
{
problem = _("bad domain in dhcp-option");
break;
}
if (!(m = realloc(m, len + strlen(arg) + 2 + header_size)))
die(_("could not get memory"), NULL);
p = m + header_size;
q = p + len;
/* add string on the end in RFC1035 format */
while (*arg)
{
unsigned char *cp = q++;
int j;
for (j = 0; *arg && (*arg != '.'); arg++, j++)
*q++ = *arg;
*cp = j;
if (*arg)
arg++;
}
*q++ = 0;
/* Now tail-compress using earlier names. */
newlen = q - p;
for (tail = p + len; *tail; tail += (*tail) + 1)
for (r = p; r - p < (int)len; r += (*r) + 1)
if (strcmp((char *)r, (char *)tail) == 0)
{
PUTSHORT((r - p) | 0xc000, tail);
newlen = tail - p;
goto end;
}
end:
len = newlen;
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*(comma++) = 0;
}
/* RFC 3361, enc byte is zero for names */
if (new->opt == 120)
m[0] = 0;
new->len = (int) len + header_size;
new->val = m;
}
else
{
new->len = strlen(comma);
/* keep terminating zero on string */
new->val = (unsigned char *)safe_string_alloc(comma);
new->flags |= DHOPT_STRING;
}
}
}
if (new->len > 255)
problem = _("dhcp-option too long");
if (problem)
{
if (new->netid)
free(new->netid);
if (new->val)
free(new->val);
if (new->vendor_class)
free(new->vendor_class);
free(new);
}
else if (new->vendor_class)
{
new->next = daemon->vendor_opts;
daemon->vendor_opts = new;
}
else
{
new->next = daemon->dhcp_opts;
daemon->dhcp_opts = new;
}
return problem;
}
static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem, int nest)
{
int i;
@@ -359,12 +667,9 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
if (option == optmap[i].c)
{
daemon->options |= optmap[i].flag;
return arg ? _("extraneous parameter") : NULL;
return NULL;
}
if (!arg)
return _("missing parameter");
switch (option)
{
case 'C':
@@ -859,6 +1164,49 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
option = '?';
break;
case LOPT_TFTP_MAX:
if (!atoi_check(arg, &daemon->tftp_max))
option = '?';
break;
case LOPT_PREFIX:
daemon->tftp_prefix = safe_string_alloc(arg);
break;
#if defined(__FreeBSD__) || defined(__DragonFly__)
case LOPT_BRIDGE:
{
struct dhcp_bridge *new = safe_malloc(sizeof(struct dhcp_bridge));
if (!(comma = strchr(arg, ',')))
{
problem = _("bad bridge-interface");
option = '?';
break;
}
*comma = 0;
strncpy(new->iface, arg, IF_NAMESIZE);
new->alias = NULL;
new->next = daemon->bridges;
daemon->bridges = new;
do {
arg = comma+1;
if ((comma = strchr(arg, ',')))
*comma = 0;
if (strlen(arg) != 0)
{
struct dhcp_bridge *b = safe_malloc(sizeof(struct dhcp_bridge));
b->next = new->alias;
new->alias = b;
strncpy(b->iface, arg, IF_NAMESIZE);
}
} while (comma);
break;
}
#endif
case 'F':
{
int k, leasepos = 2;
@@ -1150,264 +1498,9 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
}
case 'O':
{
struct dhcp_opt *new = safe_malloc(sizeof(struct dhcp_opt));
char lenchar = 0, *cp;
int addrs, digs, is_addr, is_hex, is_dec;
new->len = 0;
new->flags = 0;
new->netid = NULL;
new->val = NULL;
new->vendor_class = NULL;
if ((comma = safe_strchr(arg, ',')))
{
struct dhcp_netid *np = NULL;
*comma++ = 0;
do {
for (cp = arg; *cp; cp++)
if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
break;
if (!*cp)
break;
if (strstr(arg, "vendor:") == arg)
new->vendor_class = (unsigned char *)safe_string_alloc(arg+7);
else
{
new->netid = safe_malloc(sizeof (struct dhcp_netid));
/* allow optional "net:" for consistency */
if (strstr(arg, "net:") == arg)
new->netid->net = safe_string_alloc(arg+4);
else
new->netid->net = safe_string_alloc(arg);
new->netid->next = np;
np = new->netid;
}
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*comma++ = 0;
} while (arg);
}
if (!arg || (new->opt = atoi(arg)) == 0)
{
if ((problem = parse_dhcp_opt(daemon, arg)))
option = '?';
problem = _("bad dhcp-option");
}
else if (comma && new->opt == 119 && !new->vendor_class)
{
/* dns search, RFC 3397 */
unsigned char *q, *r, *tail;
unsigned char *p = NULL;
size_t newlen, len = 0;
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*(comma++) = 0;
while (arg && *arg)
{
if (!canonicalise_opt(arg))
{
option = '?';
problem = _("bad domain in dhcp-option");
break;
}
if (!(p = realloc(p, len + strlen(arg) + 2)))
die(_("could not get memory"), NULL);
q = p + len;
/* add string on the end in RFC1035 format */
while (*arg)
{
unsigned char *cp = q++;
int j;
for (j = 0; *arg && (*arg != '.'); arg++, j++)
*q++ = *arg;
*cp = j;
if (*arg)
arg++;
}
*q++ = 0;
/* Now tail-compress using earlier names. */
newlen = q - p;
for (tail = p + len; *tail; tail += (*tail) + 1)
for (r = p; r - p < (int)len; r += (*r) + 1)
if (strcmp((char *)r, (char *)tail) == 0)
{
PUTSHORT((r - p) | 0xc000, tail);
newlen = tail - p;
goto end;
}
end:
len = newlen;
arg = comma;
if ((comma = safe_strchr(arg, ',')))
*(comma++) = 0;
}
new->len = (int) len;
new->val = p;
}
else if (comma)
{
/* not option 119 */
/* characterise the value */
is_addr = is_hex = is_dec = 1;
addrs = digs = 1;
for (cp = comma; *cp; cp++)
if (*cp == ',')
{
addrs++;
is_dec = is_hex = 0;
}
else if (*cp == ':')
{
digs++;
is_dec = is_addr = 0;
}
else if (*cp == '.' || *cp == '/')
is_dec = is_hex = 0;
else if (!((*cp >='0' && *cp <= '9') || *cp == '-'))
{
is_addr = 0;
if (cp[1] == 0 && is_dec &&
(*cp == 'b' || *cp == 's' || *cp == 'i'))
{
lenchar = *cp;
*cp = 0;
}
else
is_dec = 0;
if (!((*cp >='A' && *cp <= 'F') ||
(*cp >='a' && *cp <= 'f')))
is_hex = 0;
}
if (is_hex && digs > 1)
{
new->len = digs;
new->val = safe_malloc(new->len);
parse_hex(comma, new->val, digs, NULL, NULL);
}
else if (is_dec)
{
int i, val = atoi(comma);
/* assume numeric arg is 1 byte except for
options where it is known otherwise.
For vendor class option, we have to hack. */
new->len = 1;
if (lenchar == 'b')
new->len = 1;
else if (lenchar == 's')
new->len = 2;
else if (lenchar == 'i')
new->len = 4;
else if (new->vendor_class)
{
if (val & 0xffff0000)
new->len = 4;
else if (val & 0xff00)
new->len = 2;
}
else
switch (new->opt)
{
case 13: case 22: case 25: case 26:
new->len = 2;
break;
case 2: case 24: case 35: case 38:
new->len = 4;
break;
}
new->val = safe_malloc(new->len);
for (i=0; i<new->len; i++)
new->val[i] = val>>((new->len - i - 1)*8);
}
else if (is_addr)
{
struct in_addr in;
unsigned char *op;
char *slash;
/* max length of address/subnet descriptor is five bytes */
new->val = op = safe_malloc(5 * addrs);
if (!new->vendor_class)
new->flags |= DHOPT_ADDR;
while (addrs--)
{
cp = comma;
if ((comma = strchr(cp, ',')))
*comma++ = 0;
if ((slash = strchr(cp, '/')))
*slash++ = 0;
in.s_addr = inet_addr(cp);
if (!slash)
{
memcpy(op, &in, INADDRSZ);
op += INADDRSZ;
}
else
{
unsigned char *p = (unsigned char *)&in;
int netsize = atoi(slash);
*op++ = netsize;
if (netsize > 0)
*op++ = *p++;
if (netsize > 8)
*op++ = *p++;
if (netsize > 16)
*op++ = *p++;
if (netsize > 24)
*op++ = *p++;
new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
}
}
new->len = op - new->val;
}
else
{
/* text arg */
new->len = strlen(comma);
/* keep terminating zero on string */
new->val = (unsigned char *)safe_string_alloc(comma);
new->flags |= DHOPT_STRING;
}
}
if (new->len > 255)
{
option = '?';
problem = _("dhcp-option too long");
}
if (option == '?')
{
if (new->netid)
free(new->netid);
if (new->val)
free(new->val);
if (new->vendor_class)
free(new->vendor_class);
free(new);
}
else if (new->vendor_class)
{
new->next = daemon->vendor_opts;
daemon->vendor_opts = new;
}
else
{
new->next = daemon->dhcp_opts;
daemon->dhcp_opts = new;
}
break;
}
case 'M':
{
@@ -1509,12 +1602,22 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
}
case 'J':
case LOPT_NO_NAMES:
{
struct dhcp_netid_list *new = safe_malloc(sizeof(struct dhcp_netid_list));
struct dhcp_netid *list = NULL;
if (option == 'J')
{
new->next = daemon->dhcp_ignore;
daemon->dhcp_ignore = new;
do {
}
else
{
new->next = daemon->dhcp_ignore_names;
daemon->dhcp_ignore_names = new;
}
while (arg) {
struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
if ((comma = safe_strchr(arg, ',')))
*comma++ = 0;
@@ -1522,7 +1625,7 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
list = member;
member->net = safe_string_alloc(arg);
arg = comma;
} while (arg);
}
new->list = list;
break;
@@ -1569,6 +1672,30 @@ static char *one_opt(struct daemon *daemon, int option, char *arg, char *problem
break;
}
case LOPT_PTR:
{
struct ptr_record *new;
if ((comma = safe_strchr(arg, ',')))
*(comma) = 0;
if (!canonicalise_opt(arg))
{
option = '?';
problem = _("bad PTR record");
break;
}
new = safe_malloc(sizeof(struct ptr_record));
new->next = daemon->ptr;
daemon->ptr = new;
new->name = safe_string_alloc(arg);
new->ptr = NULL;
if (comma)
new->ptr = safe_string_alloc(comma+1);
break;
}
case 'Y':
{
struct txt_record *new;
@@ -1726,7 +1853,7 @@ static void one_file(struct daemon *daemon, char *file, int nest)
{
int i, option, lineno = 0;
FILE *f;
char *p, *arg, *buff = daemon->namebuff;
char *p, *arg, *start, *buff = daemon->namebuff;
if (nest > 20)
die(_("files nested too deep in %s"), file);
@@ -1743,6 +1870,7 @@ static void one_file(struct daemon *daemon, char *file, int nest)
{
int white;
unsigned int lastquote;
char *errmess = NULL;
lineno++;
@@ -1806,18 +1934,27 @@ static void one_file(struct daemon *daemon, char *file, int nest)
else
arg = NULL;
for (option = 0, i = 0; opts[i].name; i++)
if (strcmp(opts[i].name, buff) == 0)
option = opts[i].val;
/* skip leading space */
for (start = buff; *start && isspace(*start); start++);
if (option)
for (option = 0, i = 0; opts[i].name; i++)
if (strcmp(opts[i].name, start) == 0)
{
char *errmess;
if ((errmess = one_opt(daemon, option, arg, _("error"), nest + 1)))
complain(errmess, lineno, file);
option = opts[i].val;
break;
}
if (!option)
errmess = _("bad option");
else if (opts[i].has_arg == 0 && arg)
errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter");
else
complain(_("bad option"), lineno, file);
errmess = one_opt(daemon, option, arg, _("error"), nest + 1);
if (errmess)
complain(errmess, lineno, file);
}
fclose(f);
@@ -1846,6 +1983,7 @@ struct daemon *read_opts(int argc, char **argv, char *compile_opts)
daemon->groupname = CHGRP;
daemon->runfile = RUNFILE;
daemon->dhcp_max = MAXLEASES;
daemon->tftp_max = TFTP_MAX_CONNECTIONS;
daemon->edns_pktsz = EDNS_PKTSZ;
daemon->log_fac = -1;
add_txt(daemon, "version.bind", "dnsmasq-" VERSION );

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
/* dnsmasq is Copyright (c) 2000 - 2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -341,7 +341,8 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
retransmision and to detect answers to questions we didn't ask, which
might be poisoning attacks. Note that we decode the name rather
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. */
We ignore case in the names for the same reason. Return all-ones
if there is not question section. */
unsigned int questions_crc(HEADER *header, size_t plen, char *name)
{
int q;
@@ -407,16 +408,39 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
return ansp - (unsigned char *)header;
}
unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p)
unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p */
also return length of pseudoheader in *len and pointer to the UDP size in *p
Finally, check to see if a packet is signed. If it is we cannot change a single bit before
forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
int i, arcount = ntohs(header->arcount);
unsigned char *ansp;
unsigned short rdlen, type;
unsigned char *ansp = (unsigned char *)(header+1);
unsigned short rdlen, type, class;
unsigned char *ret = NULL;
if (arcount == 0 || !(ansp = skip_questions(header, plen)))
if (is_sign && header->opcode == QUERY)
{
for (i = 0; i < ntohs(header->qdcount); i++)
{
if (!(ansp = skip_name(ansp, header, plen)))
return NULL;
GETSHORT(type, ansp);
GETSHORT(class, ansp);
if (class == C_IN && type == T_TKEY)
*is_sign = 1;
}
}
else
{
if (!(ansp = skip_questions(header, plen)))
return NULL;
}
if (arcount == 0)
return NULL;
if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
@@ -430,7 +454,8 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
GETSHORT(type, ansp);
save = ansp;
ansp += 6; /* class, TTL */
GETSHORT(class, ansp);
ansp += 4; /* TTL */
GETSHORT(rdlen, ansp);
if ((size_t)(ansp + rdlen - (unsigned char *)header) > plen)
return NULL;
@@ -441,11 +466,16 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
*len = ansp - start;
if (p)
*p = save;
return start;
ret = start;
}
else if (is_sign &&
i == arcount - 1 &&
class == C_ANY &&
(type == T_SIG || type == T_TSIG))
*is_sign = 1;
}
return NULL;
return ret;
}
@@ -744,7 +774,8 @@ void extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, stru
}
/* If the packet holds exactly one query
return 1 and leave the name from the query in name. */
return F_IPV4 or F_IPV6 and leave the name from the query in name.
Abuse F_BIGNAME to indicate an NS query - yuck. */
unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
{
@@ -774,6 +805,8 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
return F_IPV6;
if (qtype == T_ANY)
return F_IPV4 | F_IPV6;
if (qtype == T_NS || qtype == T_SOA)
return F_QUERY | F_BIGNAME;
}
return F_QUERY;
@@ -975,27 +1008,25 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
int qtype, qclass, is_arpa;
int qtype, qclass;
struct all_addr addr;
unsigned int nameoffset;
unsigned short flag;
int qdcount = ntohs(header->qdcount);
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;
int is_sign;
struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0;
struct mx_srv_record *rec;
if (!qdcount || header->opcode != QUERY )
return 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include
security information. */
if (find_pseudoheader(header, qlen, NULL, &pheader))
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, ext_rcode, flags;
unsigned char *psave = pheader;
@@ -1010,12 +1041,15 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
than we allow, trim it so that we don't get an overlarge
response from upstream */
if (udpsz > daemon->edns_pktsz)
if (!is_sign && (udpsz > daemon->edns_pktsz))
PUTSHORT(daemon->edns_pktsz, psave);
dryrun = 1;
}
if (!qdcount || header->opcode != QUERY )
return 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
rec->offset = 0;
@@ -1036,10 +1070,6 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
if (!extract_name(header, qlen, &p, name, 1))
return 0; /* bad packet */
/* see if it's w.z.y.z.in-addr.arpa format */
is_arpa = in_arpa_name_2_addr(name, &addr);
GETSHORT(qtype, p);
GETSHORT(qclass, p);
@@ -1070,7 +1100,16 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
{
if (qtype == T_PTR || qtype == T_ANY)
{
if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
/* see if it's w.z.y.z.in-addr.arpa format */
int is_arpa = in_arpa_name_2_addr(name, &addr);
struct ptr_record *ptr;
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name))
break;
if (!ptr &&
!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
{
if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
{
@@ -1081,6 +1120,21 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen, struct daemon *d
log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
}
}
else if (ptr)
{
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_BIGNAME, name, NULL, 0, NULL, 0);
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name))
{
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_PTR, C_IN, "d", ptr->ptr))
anscount++;
}
}
}
else do
{
/* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */

View File

@@ -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, do_classes = 0;
int clid_len = 0, ignore = 0, do_classes = 0, selecting = 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;
@@ -446,8 +446,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
{
hostname = config->hostname;
hostname_auth = 1;
/* be careful not to send an OFFER with a hostname not
matching the DISCOVER. */
/* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
offer_hostname = hostname;
}
@@ -675,6 +674,8 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
{
/* SELECTING */
selecting = 1;
for (; context; context = context->current)
if (context->local.s_addr == option_addr(opt).s_addr)
break;
@@ -746,7 +747,7 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
/* Check if a new static address has been configured. Be very sure that
when the client does DISCOVER, it will get the static address, otherwise
an endless protocol loop will ensue. */
else if (!tmp &&
else if (!tmp && !selecting &&
have_config(config, CONFIG_ADDR) &&
(!have_config(config, CONFIG_DECLINED) ||
difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
@@ -835,8 +836,6 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
hostname_auth = 1;
}
log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
if (context->netid.net)
{
context->netid.next = netid;
@@ -845,10 +844,23 @@ size_t dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *ifa
time = calc_time(context, config, NULL, option_find(mess, sz, OPTION_LEASE_TIME, 4), now);
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth)
{
for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
hostname = NULL;
}
if (hostname)
lease_set_hostname(lease, hostname, daemon->domain_suffix, hostname_auth);
lease_set_expires(lease, time, now);
log_packet(daemon, "ACK", &mess->yiaddr, mess, iface_name, hostname);
mess->siaddr = context->local;
bootp_option_put(mess, daemon->boot_config, netid);
p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
@@ -1200,14 +1212,12 @@ static unsigned char *do_req_options(struct dhcp_context *context,
if (subnet_addr.s_addr)
p = option_put(p, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
if (in_list(req_options, OPTION_NETMASK) &&
!option_find2(netid, config_opts, OPTION_NETMASK))
if (!option_find2(netid, config_opts, OPTION_NETMASK))
p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr &&
in_list(req_options, OPTION_BROADCAST) &&
!option_find2(netid, config_opts, OPTION_BROADCAST))
p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));

495
src/tftp.c Normal file
View File

@@ -0,0 +1,495 @@
/* dnsmasq is Copyright (c) 2000-2006 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "dnsmasq.h"
#ifdef HAVE_TFTP
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
#define OP_RRQ 1
#define OP_WRQ 2
#define OP_DATA 3
#define OP_ACK 4
#define OP_ERR 5
#define OP_OACK 6
#define ERR_NOTDEF 0
#define ERR_FNF 1
#define ERR_PERM 2
#define ERR_FULL 3
#define ERR_ILL 4
void tftp_request(struct listener *listen, struct daemon *daemon, time_t now)
{
ssize_t len;
char *packet = daemon->packet;
char *filename, *mode, *p, *end, *opt;
struct stat statbuf;
struct sockaddr_in addr, peer;
struct msghdr msg;
struct cmsghdr *cmptr;
struct iovec iov;
struct ifreq ifr;
int is_err = 1, if_index = 0;
struct iname *tmp;
struct tftp_transfer *transfer, *t;
struct tftp_file *file;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_LINUX_NETWORK
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &peer;
msg.msg_namelen = sizeof(peer);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = packet;
iov.iov_len = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
if (daemon->options & OPT_NOWILD)
addr = listen->iface->addr.in;
else
{
addr.sin_addr.s_addr = 0;
#if defined(HAVE_LINUX_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
}
if (!(ifr.ifr_ifindex = if_index) ||
ioctl(listen->tftpfd, SIOCGIFNAME, &ifr) == -1)
return;
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
if (if_index == 0 || !if_indextoname(if_index, ifr.ifr_name))
return;
#endif
if (addr.sin_addr.s_addr == 0)
return;
if (!iface_check(daemon, AF_INET, (struct all_addr *)&addr, &ifr, &if_index))
return;
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
}
/* tell kernel to use ephemeral port */
addr.sin_port = 0;
addr.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin_len = sizeof(addr);
#endif
if (!(transfer = malloc(sizeof(struct tftp_transfer))))
return;
if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
free(transfer);
return;
}
transfer->peer = peer;
transfer->timeout = now + 1;
transfer->backoff = 1;
transfer->block = 1;
transfer->blocksize = 512;
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
!fix_fd(transfer->sockfd))
{
free_transfer(transfer);
return;
}
p = packet + 2;
end = packet + len;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
strcasecmp(mode, "octet") != 0)
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
else
{
while ((opt = next(&p, end)))
{
if (strcasecmp(opt, "blksize") == 0 &&
(opt = next(&p, end)))
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
transfer->opt_blocksize = 1;
transfer->block = 0;
}
if (strcasecmp(opt, "tsize") == 0 && next(&p, end))
{
transfer->opt_transize = 1;
transfer->block = 0;
}
}
if (daemon->tftp_prefix)
{
strncpy(daemon->namebuff, daemon->tftp_prefix, MAXDNAME);
if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/' &&
filename[0] != '/')
strncat(daemon->namebuff, "/", MAXDNAME);
}
else if (filename[0] != '/')
strncpy(daemon->namebuff, "/", MAXDNAME);
else
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, filename, MAXDNAME);
daemon->namebuff[MAXDNAME-1] = 0;
/* If we're doing many tranfers from the same file, only
open it once this saves lots of file descriptors
when mass-booting a big cluster, for instance. */
for (t = daemon->tftp_trans; t; t = t->next)
if (strcmp(t->file->filename, daemon->namebuff) == 0)
break;
if (t)
{
/* file already open */
transfer->file = t->file;
transfer->file->refcount++;
if ((len = get_block(packet, transfer)) == -1)
goto oops;
is_err = 0;
}
else
{
/* check permissions and open file */
/* trick to ban moving out of the subtree */
if (daemon->tftp_prefix && strstr(daemon->namebuff, "/../"))
{
errno = EACCES;
goto perm;
}
if (stat(daemon->namebuff, &statbuf) == -1)
{
if (errno == ENOENT || errno == ENOTDIR)
len = tftp_err(ERR_FNF, packet, _("file %s not found"), daemon->namebuff);
else if (errno == EACCES)
{
perm:
len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), daemon->namebuff);
}
else
{
oops:
len = tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
}
}
else
{
uid_t uid = geteuid();
/* running as root, must be world-readable */
if (uid == 0)
{
if (!(statbuf.st_mode & S_IROTH))
{
errno = EACCES;
goto perm;
}
}
/* in secure mode, must be owned by user running dnsmasq */
else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
{
errno = EACCES;
goto perm;
}
if (!(file = malloc(sizeof(struct tftp_file) + strlen(daemon->namebuff) + 1)))
{
errno = ENOMEM;
goto oops;
}
if ((file->fd = open(daemon->namebuff, O_RDONLY)) == -1)
{
free(file);
if (errno == EACCES || errno == EISDIR)
goto perm;
else
goto oops;
}
else
{
transfer->file = file;
file->refcount = 1;
file->size = statbuf.st_size;
strcpy(file->filename, daemon->namebuff);
if ((len = get_block(packet, transfer)) == -1)
goto oops;
is_err = 0;
}
}
}
}
while (sendto(transfer->sockfd, packet, len, 0,
(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
if (is_err)
free_transfer(transfer);
else
{
syslog(LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
transfer->next = daemon->tftp_trans;
daemon->tftp_trans = transfer;
}
}
void check_tftp_listeners(struct daemon *daemon, fd_set *rset, time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
struct ack {
unsigned short op, block;
} *mess = (struct ack *)daemon->packet;
/* Check for activity on any existing transfers */
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
if (FD_ISSET(transfer->sockfd, rset))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
{
/* Got ack, ensure we take the (re)transmit path */
transfer->timeout = now;
transfer->backoff = 0;
transfer->block++;
}
else if (ntohs(mess->op) == OP_ERR)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
/* Sanitise error message */
if (!err)
err = "";
else
{
char *q, *r;
for (q = r = err; *r; r++)
if (isprint(*r))
*(q++) = *r;
*q = 0;
}
syslog(LOG_ERR, _("TFTP error %d %s received from %s"),
(int)ntohs(mess->block), err,
inet_ntoa(transfer->peer.sin_addr));
/* Got err, ensure we take abort */
transfer->timeout = now;
transfer->backoff = 100;
}
}
}
if (difftime(now, transfer->timeout) >= 0.0)
{
int endcon = 0;
/* timeout, retransmit */
transfer->timeout += 1<<(transfer->backoff);
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = get_block(daemon->packet, transfer)) == -1)
{
len = tftp_err(ERR_NOTDEF, daemon->packet, _("cannot read %s: %s"), transfer->file->filename);
endcon = 1;
}
else if (++transfer->backoff > 5)
{
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (len != 0)
syslog(LOG_ERR, _("TFTP failed sending %s to %s"),
transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
len = 0;
}
if (len != 0)
while(sendto(transfer->sockfd, daemon->packet, len, 0,
(struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
if (endcon || len == 0)
{
/* unlink */
*up = tmp;
free_transfer(transfer);
continue;
}
}
up = &transfer->next;
}
}
static void free_transfer(struct tftp_transfer *transfer)
{
close(transfer->sockfd);
if (transfer->file && (--transfer->file->refcount) == 0)
{
close(transfer->file->fd);
free(transfer->file);
}
free(transfer);
}
static char *next(char **p, char *end)
{
char *ret = *p;
size_t len;
if (*(end-1) != 0 ||
*p == end ||
(len = strlen(ret)) == 0)
return NULL;
*p += len + 1;
return ret;
}
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
{
struct errmess {
unsigned short op, err;
char message[];
} *mess = (struct errmess *)packet;
ssize_t ret = 4;
char *errstr = strerror(errno);
mess->op = htons(OP_ERR);
mess->err = htons(err);
ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
if (err != ERR_FNF)
syslog(LOG_ERR, "TFTP %s", mess->message);
return ret;
}
/* return -1 for error, zero for done. */
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
{
if (transfer->block == 0)
{
/* send OACK */
char *p;
struct oackmess {
unsigned short op;
char data[];
} *mess = (struct oackmess *)packet;
p = mess->data;
mess->op = htons(OP_OACK);
if (transfer->opt_blocksize)
{
p += (sprintf(p, "blksize") + 1);
p += (sprintf(p, "%d", transfer->blocksize) + 1);
}
if (transfer->opt_transize)
{
p += (sprintf(p,"tsize") + 1);
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
}
return p - packet;
}
else
{
/* send data packet */
struct datamess {
unsigned short op, block;
unsigned char data[];
} *mess = (struct datamess *)packet;
off_t offset = transfer->blocksize * (transfer->block - 1);
size_t size = transfer->file->size - offset;
if (offset > transfer->file->size)
return 0; /* finished */
if (size > transfer->blocksize)
size = transfer->blocksize;
lseek(transfer->file->fd, offset, SEEK_SET);
mess->op = htons(OP_DATA);
mess->block = htons((unsigned short)(transfer->block));
if (!read_write(transfer->file->fd, mess->data, size, 1))
return -1;
else
return size + 4;
}
}
#endif

View File

@@ -92,11 +92,12 @@ unsigned short rand16(void)
int legal_char(char c)
{
/* check for legal char a-z A-Z 0-9 -
(also / , used for RFC2317 and _ used in windows queries) */
(also / , used for RFC2317 and _ used in windows queries
and space, for DNS-SD stuff) */
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '/' || c == '_')
c == '-' || c == '/' || c == '_' || c == ' ')
return 1;
return 0;