Commit f6b7dc47 authored by Simon Kelley's avatar Simon Kelley

import of dnsmasq-2.20.tar.gz

parent bb01cb96
...@@ -1267,7 +1267,7 @@ version 2.17 ...@@ -1267,7 +1267,7 @@ version 2.17
already a fatal error when bind-interfaces is set.) already a fatal error when bind-interfaces is set.)
Allow the --interface and --except-interface options to Allow the --interface and --except-interface options to
take a comma-seperated list of interfaces. take a comma-separated list of interfaces.
Tweak --dhcp-userclass matching code to work with the Tweak --dhcp-userclass matching code to work with the
ISC dhclient which violates RFC3004 unless its ISC dhclient which violates RFC3004 unless its
...@@ -1324,7 +1324,50 @@ version 2.19 ...@@ -1324,7 +1324,50 @@ version 2.19
option, send the /etc/hosts name as the hostname in option, send the /etc/hosts name as the hostname in
the DHCP lease. Thanks to Will Murname for the suggestion. the DHCP lease. Thanks to Will Murname for the suggestion.
version 2.20
Allow more than one instance of dnsmasq to run on a
machine, each providing DHCP service on a different
interface, provided that --bind-interfaces is set. This
configuration used to work, but regressed in version 2.14
Fix compilation on Mac OS X. Thanks to Kevin Bullock.
Protect against overlong names and overlong
labels in configuration and from DHCP.
Fix interesting corner case in CNAME handling. This occurs
when a CNAME has a target which "shadowed" by a name in
/etc/hosts or from DHCP. Resolving the CNAME would sneak
the upstream value of the CNAME's target into the cache,
alongside the local value. Now that doesn't happen, though
resolving the CNAME still gives the unshadowed value. This
is arguably wrong but rather difficult to fix. The main
thing is to avoid getting strange results for the target
due to the cache pollution when resolving the
CNAME. Thanks to Pierre Habouzit for exploring the corner
and submitting a very clear bug report.
Fix subtle bug in the DNS packet parsing code. It's almost
impossible to describe this succinctly, but the one known
manifestation is the inability to cache the A record for
www.apple.com. Thanks to Bob Alexander for spotting that.
Support SRV records. Thanks to Robert Kean for the patches
for this.
Fixed sign confusion in the vendor-id matching code which
could cause crashes sometimes. (Credit to Mark Wiater for
help finding this.)
Added the ability to match the netid tag in a
dhcp-range. Combined with the ability to have multiple
ranges in a single subnet, this provides a means to
segregate hosts on different address ranges based on
vendorclass or userclass. Thanks to Mark Wiater for
prompting this enhancement.
Added preference values for MX records.
Added the --localise-queries option.
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
############################################################################### ###############################################################################
Name: dnsmasq Name: dnsmasq
Version: 2.19 Version: 2.20
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: System Environment/Daemons Group: System Environment/Daemons
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
############################################################################### ###############################################################################
Name: dnsmasq Name: dnsmasq
Version: 2.19 Version: 2.20
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Productivity/Networking/DNS/Servers Group: Productivity/Networking/DNS/Servers
......
...@@ -143,11 +143,21 @@ requests that it shouldn't reply to. This has the advantage of ...@@ -143,11 +143,21 @@ requests that it shouldn't reply to. This has the advantage of
working even when interfaces come and go and change address. This working even when interfaces come and go and change address. This
option forces dnsmasq to really bind only the interfaces it is option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when listening on. About the only time when this is useful is when
running another nameserver on the same machine or using IP running another nameserver (or another instance of dnsmasq) on the
same machine or when using IP
alias. Specifying interfaces with IP alias automatically turns this alias. Specifying interfaces with IP alias automatically turns this
option on. Note that this only applies to the DNS part of dnsmasq, the option on. Setting this option also enables multiple instances of
DHCP server always binds the wildcard address in order to receive dnsmasq which provide DHCP service to run in the same machine.
broadcast packets. .TP
.B \-y, --localise-queries
Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
recieved. If a name in /etc/hosts has more than one address associated with
it, and at least one of those addresses is on the same subnet as the
interface to which the query was sent, then return only the
address(es) on that subnet. This allows for a server to have multiple
addresses in /etc/hosts corresponding to each of its interfaces, and
hosts will get the correct address based on which network they are
attached to. Currently this facility is limited to IPv4.
.TP .TP
.B \-b, --bogus-priv .B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc) Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
...@@ -214,7 +224,7 @@ and they are queried only using the specified server. This is ...@@ -214,7 +224,7 @@ and they are queried only using the specified server. This is
intended for private nameservers: if you have a nameserver on your intended for private nameservers: if you have a nameserver on your
network which deals with names of the form network which deals with names of the form
xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving the flag
.B -S /internal.thekelleys.org.uk/192.168.1.1 .B -S /.internal.thekelleys.org.uk/192.168.1.1
will send all queries for will send all queries for
internal machines to that nameserver, everything else will go to the internal machines to that nameserver, everything else will go to the
servers in /etc/resolv.conf. An empty domain specification, servers in /etc/resolv.conf. An empty domain specification,
...@@ -258,18 +268,20 @@ additional facility that /#/ matches any domain. Thus ...@@ -258,18 +268,20 @@ additional facility that /#/ matches any domain. Thus
answered from /etc/hosts or DHCP and not sent to an upstream answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive. nameserver by a more specific --server directive.
.TP .TP
.B \-m, --mx-host=<mx name>[,<hostname>] .B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
Return an MX record named <mx name> pointing to the given hostname (if Return an MX record named <mx name> pointing to the given hostname (if
given), or given), or
the host specified in the --mx-target switch the host specified in the --mx-target switch
or, if that switch is not given, the host on which dnsmasq or, if that switch is not given, the host on which dnsmasq
is running. This is useful for directing mail from systems on a LAN is running. The default is useful for directing mail from systems on a LAN
to a central server. to a central server. The preference value is optional, and defaults to
1 if not given. More than one MX record may be given for a host.
.TP .TP
.B \-t, --mx-target=<hostname> .B \-t, --mx-target=<hostname>
Specify target for the MX record returned by dnsmasq. See --mx-host. Note that to turn on the MX function, Specify the default target for the MX record returned by dnsmasq. See
at least one of --mx-host and --mx-target must be set. If only one of --mx-host and --mx-target --mx-host. If --mx-target is given, but not --mx-host, then dnsmasq
is set, the other defaults to the hostname of the machine on which dnsmasq is running. returns a MX record containing the MX target for MX queries on the
hostname of the machine on which dnsmasq is running.
.TP .TP
.B \-e, --selfmx .B \-e, --selfmx
Return an MX record pointing to itself for each local Return an MX record pointing to itself for each local
...@@ -281,6 +293,23 @@ machine on which dnsmasq is running) for each ...@@ -281,6 +293,23 @@ machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP local machine. Local machines are those in /etc/hosts or with DHCP
leases. leases.
.TP .TP
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
Return a SRV DNS record. See RFC2782 for details. If not supplied, the
domain defaults to that given by
.B --domain.
The default for the target domain is empty, and the default for port
is one and the defaults for
weight and priority are zero. Be careful if transposing data from BIND
zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned. Specifying at least one
.B --srv-host
option also turns on replies to SOA queries for the
domain given by the
.B --domain
option. The data in these is stereotyped, but is enough for resolvers
to deduce that the domain is a valid one for resolving SRV records.
.TP
.B \-c, --cache-size=<cachesize> .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. Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
.TP .TP
...@@ -307,7 +336,8 @@ always optional. On some broken systems, dnsmasq can listen on only ...@@ -307,7 +336,8 @@ always optional. On some broken systems, dnsmasq can listen on only
one interface when using DHCP, and the name of that interface must be one interface when using DHCP, and the name of that interface must be
given using the given using the
.B interface .B interface
option. This limitation currently affects OpenBSD. The optional option. This limitation currently affects OpenBSD. 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 network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis. The end address dhcp options may be specified on a per-network basis. The end address
may be replaced by the keyword may be replaced by the keyword
...@@ -380,8 +410,8 @@ specfied in RFC2132. For example, to set the default route option to ...@@ -380,8 +410,8 @@ specfied in RFC2132. For example, to set the default route option to
and to set the time-server address to 192.168.0.4, do and to set the time-server address to 192.168.0.4, do
.B --dhcp-option=42,192.168.0.4 .B --dhcp-option=42,192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma seperated machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-seperated hex digits dotted-quad IP addresses, a decimal number, colon-separated hex digits
and a text string. If the optional network-ids are given then and a text string. If the optional network-ids are given then
this option is only sent when all the network-ids are matched. this option is only sent when all the network-ids are matched.
Be careful: no checking is done that the correct type of data for the Be careful: no checking is done that the correct type of data for the
...@@ -544,6 +574,17 @@ and run dnsmasq with the ...@@ -544,6 +574,17 @@ and run dnsmasq with the
.B \-r /etc/resolv.dnsmasq .B \-r /etc/resolv.dnsmasq
option. This second technique allows for dynamic update of the server option. This second technique allows for dynamic update of the server
addresses by PPP or DHCP. addresses by PPP or DHCP.
.PP
Addresses in /etc/hosts will "shadow" different addresses for the same
names in the upstream DNS, so "mycompany.com 1.2.3.4" in /etc/hosts will ensure that
queries for "mycompany.com" always return 1.2.3.4 even if queries in
the upstream DNS would otherwise return a different address. There is
one exception to this: if the upstream DNS contains a CNAME which
points to a shadowed name, then looking up the CNAME through dnsmasq
will result in the unshadowed address associated with the target of
the CNAME. To work around this, add the CNAME to /etc/hosts so that
the CNAME is shadowed too.
.PP .PP
The network-id system works as follows: For each DHCP request, dnsmasq The network-id system works as follows: For each DHCP request, dnsmasq
collects a set of valid network-id tags, one from the collects a set of valid network-id tags, one from the
...@@ -560,6 +601,14 @@ set collected as described above. The prefix '#' on a tag means 'not' ...@@ -560,6 +601,14 @@ set collected as described above. The prefix '#' on a tag means 'not'
so --dhcp=option=#purple,3,1.2.3.4 sends the option when the so --dhcp=option=#purple,3,1.2.3.4 sends the option when the
network-id tag purple is not in the set of valid tags. network-id tag purple is not in the set of valid tags.
.PP .PP
If the network-id in a
.B dhcp-range
is prefixed with 'net:' then its meaning changes from setting a
tag to matching it. Thus if there is more than dhcp-range on a subnet,
and one is tagged with a network-id which is set (for instance
from a vendorclass option) then hosts which set the netid tag will be
allocated addresses in the tagged range.
.PP
The DHCP server in dnsmasq will function as a BOOTP server also, The DHCP server in dnsmasq will function as a BOOTP server also,
provided that the MAC address and IP address for clients are given, provided that the MAC address and IP address for clients are given,
either using either using
......
...@@ -4,14 +4,6 @@ ...@@ -4,14 +4,6 @@
# as the long options legal on the command line. See # as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details. # "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Change these lines if you want dnsmasq to serve MX records.
# Only one of mx-host and mx-target need be set, the other defaults
# to the name of the host running dnsmasq.
#mx-host=
#mx-target=
#selfmx
#localmx
# The following two options make you a better netizen, since they # The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot # tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers) # answer, and which load the servers (especially the root servers)
...@@ -28,6 +20,8 @@ bogus-priv ...@@ -28,6 +20,8 @@ bogus-priv
# which can trigger dial-on-demand links needlessly. # which can trigger dial-on-demand links needlessly.
# Note that (amongst other things) this blocks all SRV requests, # Note that (amongst other things) this blocks all SRV requests,
# so don't use it if you use eg Kerberos. # so don't use it if you use eg Kerberos.
# This option only affects forwarding, SRV records originating for
# dnsmasq (via srv-host= lines) are not suppressed by it.
#filterwin2k #filterwin2k
# Change this line if you want dns to get its upstream servers from # Change this line if you want dns to get its upstream servers from
...@@ -63,9 +57,8 @@ bogus-priv ...@@ -63,9 +57,8 @@ bogus-priv
# webserver. # webserver.
#address=/doubleclick.net/127.0.0.1 #address=/doubleclick.net/127.0.0.1
# You no longer (as of version 1.7) need to set these to enable # If you want dnsmasq to change uid and gid to something other
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the # than the default, edit the following lines.
# "dip" group to achieve this.
#user= #user=
#group= #group=
...@@ -292,6 +285,50 @@ bogus-priv ...@@ -292,6 +285,50 @@ bogus-priv
# and this maps 1.2.3.x to 5.6.7.x # and this maps 1.2.3.x to 5.6.7.x
#alias=1.2.3.0,5.6.7.0,255.255.255.0 #alias=1.2.3.0,5.6.7.0,255.255.255.0
# Change these lines if you want dnsmasq to serve MX records.
# Return an MX record named "maildomain.com" with target
# servermachine.com and preference 50
#mx-host=maildomain.com,servermachine.com,50
# Set the default target for MX records created using the localmx option.
#mx-target=servermachine.com
# Return an MX record pointing to the mx-target for all local
# machines.
#localmx
# Return an MX record pointing to itself for all local machines.
#selfmx
# Change the following lines if you want dnsmasq to serve SRV
# records. These are useful if you want to serve ldap requests for
# Active Directory and other windows-originated DNS requests.
# See RFC 2782.
# You may add multiple srv-host lines.
# The fields are <name>,<target>,<port>,<priority>,<weight>
# If the domain part if missing from the name (so that is just has the
# service and protocol sections) then the domain given by the domain=
# config option is used.
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
# A SRV record sending LDAP for the example.com domain to
# ldapserver.example.com port 289 (using domain=)
#domain=example.com
#srv-host=_ldap._tcp,ldapserver.example.com,389
# Two SRV records for LDAP, each with different priorities
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
# A SRV record indicating that there is no LDAP server for the domain
# example.com
#srv-host=_ldap._tcp.example.com
# For debugging purposes, log each DNS query as it passes through # For debugging purposes, log each DNS query as it passes through
# dnsmasq. # dnsmasq.
#log-queries #log-queries
......
...@@ -9,17 +9,6 @@ ...@@ -9,17 +9,6 @@
/etc/ppp/resolv.conf which is not normally world readable. /etc/ppp/resolv.conf which is not normally world readable.
.TP .TP
.B \-v, --version .B \-v, --version
--- dnsmasq.conf.example 2004-08-08 21:18:26.000000000 +0200
+++ dnsmasq.conf.example 2004-08-12 00:40:01.000000000 +0200
@@ -65,7 +65,7 @@
# You no longer (as of version 1.7) need to set these to enable
# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
-# "dip" group to achieve this.
+# "dialout" group to achieve this.
#user=
#group=
--- src/config.h 2004-08-11 11:39:18.000000000 +0200 --- src/config.h 2004-08-11 11:39:18.000000000 +0200
+++ src/config.h 2004-08-12 00:40:01.000000000 +0200 +++ src/config.h 2004-08-12 00:40:01.000000000 +0200
@@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
......
/* dnsmasq is Copyright (c) 2000 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -156,32 +156,52 @@ static int is_outdated_cname_pointer(struct crec *crecp) ...@@ -156,32 +156,52 @@ static int is_outdated_cname_pointer(struct crec *crecp)
return 1; return 1;
} }
static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags) static int is_expired(time_t now, struct crec *crecp)
{
if (crecp->flags & F_IMMORTAL)
return 0;
if (difftime(now, crecp->ttd) < 0)
return 0;
return 1;
}
static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
{ {
/* Scan and remove old entries. /* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired If (flags & F_FORWARD) then remove any forward entries for name and any expired
entries but only in the same hash bucket as name. entries but only in the same hash bucket as name.
If (flags & F_REVERSE) then remove any reverse entries for addr and any expired If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
entries in the whole cache. entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache. */ If (flags == 0) remove any expired entries in the whole cache.
In the flags & F_FORWARD case, the return code is valid, and returns zero if the
name exists in the cache as a HOSTS or DHCP entry (these are never deleted) */
#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6 | F_CNAME)
struct crec *crecp, **up; struct crec *crecp, **up;
flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4 | F_CNAME);
if (flags & F_FORWARD) if (flags & F_FORWARD)
{ {
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next) for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) || if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
is_outdated_cname_pointer(crecp) || {
((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
{
*up = crecp->hash_next; *up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
{ {
cache_unlink(crecp); cache_unlink(crecp);
cache_free(crecp); cache_free(crecp);
} }
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || (crecp->flags & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
return 0;
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
} }
else else
up = &crecp->hash_next; up = &crecp->hash_next;
...@@ -196,8 +216,7 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig ...@@ -196,8 +216,7 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
#endif #endif
for (i = 0; i < hash_size; i++) for (i = 0; i < hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next) for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) || if (is_expired(now, crecp))
((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
{ {
*up = crecp->hash_next; *up = crecp->hash_next;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
...@@ -206,9 +225,20 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig ...@@ -206,9 +225,20 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
cache_free(crecp); cache_free(crecp);
} }
} }
else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
{
*up = crecp->hash_next;
cache_unlink(crecp);
cache_free(crecp);
}
else else
up = &crecp->hash_next; up = &crecp->hash_next;
} }
return 1;
} }
/* Note: The normal calling sequence is /* Note: The normal calling sequence is
...@@ -260,8 +290,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr, ...@@ -260,8 +290,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
return NULL; return NULL;
/* First remove any expired entries and entries for the name/address we /* First remove any expired entries and entries for the name/address we
are currently inserting. */ are currently inserting. Fail is we attempt to delete a name from
cache_scan_free(name, addr, now, flags); /etc/hosts or DHCP. */
if (!cache_scan_free(name, addr, now, flags))
{
insert_error = 1;
return NULL;
}
/* Now get a cache entry from the end of the LRU list */ /* Now get a cache entry from the end of the LRU list */
while (1) { while (1) {
...@@ -376,8 +411,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi ...@@ -376,8 +411,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
{ {
next = crecp->hash_next; next = crecp->hash_next;
if (!is_outdated_cname_pointer(crecp) && if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
{ {
if ((crecp->flags & F_FORWARD) && if ((crecp->flags & F_FORWARD) &&
(crecp->flags & prot) && (crecp->flags & prot) &&
...@@ -458,7 +492,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, ...@@ -458,7 +492,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
for(i=0; i<hash_size; i++) for(i=0; i<hash_size; i++)
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next) for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0) if (!is_expired(now, crecp))
{ {
if ((crecp->flags & F_REVERSE) && if ((crecp->flags & F_REVERSE) &&
(crecp->flags & prot) && (crecp->flags & prot) &&
...@@ -835,7 +869,15 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, ...@@ -835,7 +869,15 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
strcat(addrbuff, "-IPv6"); strcat(addrbuff, "-IPv6");
} }
else if (flags & F_CNAME) else if (flags & F_CNAME)
strcpy(addrbuff, "<CNAME>"); {
/* nasty abuse of IPV4 and IPV6 flags */
if (flags & F_IPV4)
strcpy(addrbuff, "<MX>");
else if (flags & F_IPV6)
strcpy(addrbuff, "<SRV>");
else
strcpy(addrbuff, "<CNAME>");
}
else else
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
......
/* dnsmasq is Copyright (c) 2000-2004 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */ /* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.19" #define VERSION "2.20"
#define FTABSIZ 150 /* max number of outstanding requests */ #define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */ #define MAX_PROCS 20 /* max no children for TCP requests */
...@@ -278,7 +278,6 @@ typedef unsigned long in_addr_t; ...@@ -278,7 +278,6 @@ typedef unsigned long in_addr_t;
#define HAVE_SOCKADDR_SA_LEN #define HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT #undef HAVE_PSELECT
#define HAVE_BPF #define HAVE_BPF
#define BIND_8_COMPAT
/* Define before sys/socket.h is included so we get socklen_t */ /* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_ #define _BSD_SOCKLEN_T_
/* This is not defined in Mac OS X arpa/nameserv.h */ /* This is not defined in Mac OS X arpa/nameserv.h */
......
...@@ -34,6 +34,13 @@ void dhcp_init(struct daemon *daemon) ...@@ -34,6 +34,13 @@ void dhcp_init(struct daemon *daemon)
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1) setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)
die("failed to set options on DHCP socket: %s", NULL); 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);
saddr.sin_family = AF_INET; saddr.sin_family = AF_INET;
saddr.sin_port = htons(DHCP_SERVER_PORT); saddr.sin_port = htons(DHCP_SERVER_PORT);
saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_addr.s_addr = INADDR_ANY;
...@@ -429,17 +436,52 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i ...@@ -429,17 +436,52 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
return NULL; return NULL;
} }
/* Is every member of check matched by a member of pool? */
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
{
struct dhcp_netid *tmp1;
if (!check)
return 0;
for (; check; check = check->next)
{
if (check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
int address_allocate(struct dhcp_context *context, struct daemon *daemon, int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr) struct in_addr *addrp, unsigned char *hwaddr, struct dhcp_netid *netids)
{ {
/* Find a free address: exclude anything in use and anything allocated to /* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration */ a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which mathc netis first. */
struct in_addr start, addr ; struct in_addr start, addr ;
unsigned int i, j; unsigned int i, j;
for (; context; context = context->current) for (; context; context = context->current)
if (!context->static_only) if (context->static_only)
continue;
else if (netids && !context->filter_netid)
continue;
else if (!netids && context->filter_netid)
continue;
else if (netids && context->filter_netid && !match_netid(&context->netid, netids))
continue;
else
{ {
/* pick a seed based on hwaddr then iterate until we find a free address. */ /* pick a seed based on hwaddr then iterate until we find a free address. */
for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++) for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
...@@ -471,6 +513,10 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon, ...@@ -471,6 +513,10 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
} while (addr.s_addr != start.s_addr); } while (addr.s_addr != start.s_addr);
} }
if (netids)
return address_allocate(context, daemon, addrp, hwaddr, NULL);
return 0; return 0;
} }
......
/* dnsmasq is Copyright (c) 2000-2004 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -82,8 +82,9 @@ int main (int argc, char **argv) ...@@ -82,8 +82,9 @@ int main (int argc, char **argv)
die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL); die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
#endif #endif
interfaces = enumerate_interfaces(daemon); if (!enumerate_interfaces(daemon, &interfaces, NULL, NULL))
die("failed to find list of interfaces: %s", NULL);
if (!(daemon->options & OPT_NOWILD) && if (!(daemon->options & OPT_NOWILD) &&
!(daemon->listeners = create_wildcard_listeners(daemon->port))) !(daemon->listeners = create_wildcard_listeners(daemon->port)))
{ {
...@@ -509,45 +510,25 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now) ...@@ -509,45 +510,25 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if (FD_ISSET(listener->tcpfd, set)) if (FD_ISSET(listener->tcpfd, set))
{ {
int confd; int confd;
struct in_addr netmask, dst_addr_4;
while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR); while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
if (confd != -1) if (confd != -1)
{ {
int match = 1; union mysockaddr tcp_addr;
if (!(daemon->options & OPT_NOWILD)) socklen_t tcp_len = sizeof(union mysockaddr);
{
/* Check for allowed interfaces when binding the wildcard address */ /* Check for allowed interfaces when binding the wildcard address:
/* Don't know how to get interface of a connection, so we have to we do this by looking for an interface with the same address as
check by address. This will break when interfaces change address */ the local address of the TCP connection, then looking to see if that's
union mysockaddr tcp_addr; an allowed interface. As a side effect, we get the netmask of the
socklen_t tcp_len = sizeof(union mysockaddr); interface too, for localisation. */
struct iname *tmp;
if ((num_kids >= MAX_PROCS) ||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1) (!(daemon->options & OPT_NOWILD) &&
{ (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1 ||
#ifdef HAVE_IPV6 !enumerate_interfaces(daemon, NULL, &tcp_addr, &netmask))))
if (tcp_addr.sa.sa_family == AF_INET6)
tcp_addr.in6.sin6_flowinfo = htonl(0);
#endif
for (match = 1, tmp = daemon->if_except; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 0;
if (match && (daemon->if_names || daemon->if_addrs))
{
match = 0;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, &tcp_addr))
match = 1;
}
}
}
if (!match || (num_kids >= MAX_PROCS))
close(confd); close(confd);
#ifndef NO_FORK #ifndef NO_FORK
else if (!(daemon->options & OPT_DEBUG) && fork()) else if (!(daemon->options & OPT_DEBUG) && fork())
...@@ -584,7 +565,21 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now) ...@@ -584,7 +565,21 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1) if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK); fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
buff = tcp_request(daemon, confd, now); if (listener->family == AF_INET)
{
if (daemon->options & OPT_NOWILD)
{
netmask = listener->iface->netmask;
dst_addr_4 = listener->iface->addr.in.sin_addr;
}
else
/* netmask already set by enumerate_interfaces */
dst_addr_4 = tcp_addr.in.sin_addr;
}
else
dst_addr_4.s_addr = 0;
buff = tcp_request(daemon, confd, now, dst_addr_4, netmask);
if (!(daemon->options & OPT_DEBUG)) if (!(daemon->options & OPT_DEBUG))
exit(0); exit(0);
......
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -12,11 +12,11 @@ ...@@ -12,11 +12,11 @@
/* Author's email: simon@thekelleys.org.uk */ /* Author's email: simon@thekelleys.org.uk */
#define COPYRIGHT "Copyright (C) 2000-2004 Simon Kelley" #define COPYRIGHT "Copyright (C) 2000-2005 Simon Kelley"
#ifdef __linux__ #ifdef __linux__
/* for pselect.... */ /* for pselect.... */
#define _XOPEN_SOURCE 600 # define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */ /* but then DNS headers don't compile without.... */
#define _BSD_SOURCE #define _BSD_SOURCE
#endif #endif
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
/* get this before config.h too. */ /* get this before config.h too. */
#include <syslog.h> #include <syslog.h>
#ifdef __APPLE__
/* need this before arpa/nameser.h */
# define BIND_8_COMPAT
#endif
#include <arpa/nameser.h> #include <arpa/nameser.h>
/* and this. */ /* and this. */
...@@ -57,6 +61,7 @@ ...@@ -57,6 +61,7 @@
#include <errno.h> #include <errno.h>
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__) #if defined(__OpenBSD__) || defined(__NetBSD__)
# include <netinet/if_ether.h> # include <netinet/if_ether.h>
#else #else
...@@ -96,6 +101,7 @@ ...@@ -96,6 +101,7 @@
#define OPT_RESOLV_DOMAIN 32768 #define OPT_RESOLV_DOMAIN 32768
#define OPT_NO_FORK 65536 #define OPT_NO_FORK 65536
#define OPT_AUTHORITATIVE 131072 #define OPT_AUTHORITATIVE 131072
#define OPT_LOCALISE 262144
struct all_addr { struct all_addr {
union { union {
...@@ -119,9 +125,16 @@ struct doctor { ...@@ -119,9 +125,16 @@ struct doctor {
struct mx_record { struct mx_record {
char *mxname, *mxtarget; char *mxname, *mxtarget;
int preference;
struct mx_record *next; struct mx_record *next;
}; };
struct srv_record {
char *srvname, *srvtarget;
int srvport, priority, weight;
struct srv_record *next;
};
union bigname { union bigname {
char name[MAXDNAME]; char name[MAXDNAME];
union bigname *next; /* freelist */ union bigname *next; /* freelist */
...@@ -211,11 +224,13 @@ struct server { ...@@ -211,11 +224,13 @@ struct server {
struct irec { struct irec {
union mysockaddr addr; union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
struct irec *next; struct irec *next;
}; };
struct listener { struct listener {
int fd, tcpfd, family; int fd, tcpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct listener *next; struct listener *next;
}; };
...@@ -319,7 +334,7 @@ struct dhcp_context { ...@@ -319,7 +334,7 @@ struct dhcp_context {
unsigned int lease_time, addr_epoch; unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast, router; struct in_addr netmask, broadcast, router;
struct in_addr start, end; /* range of available addresses */ struct in_addr start, end; /* range of available addresses */
int static_only; int static_only, filter_netid;
struct dhcp_netid netid; struct dhcp_netid netid;
struct dhcp_context *next, *current; struct dhcp_context *next, *current;
}; };
...@@ -360,6 +375,7 @@ struct daemon { ...@@ -360,6 +375,7 @@ struct daemon {
char *lease_file; char *lease_file;
char *username, *groupname; char *username, *groupname;
char *domain_suffix; char *domain_suffix;
struct srv_record *srvnames;
char *runfile; char *runfile;
struct iname *if_names, *if_addrs, *if_except; struct iname *if_names, *if_addrs, *if_except;
struct bogus_addr *bogus_addr; struct bogus_addr *bogus_addr;
...@@ -420,12 +436,13 @@ int setup_reply(HEADER *header, unsigned int qlen, ...@@ -420,12 +436,13 @@ int setup_reply(HEADER *header, unsigned int qlen,
unsigned long local_ttl); unsigned long local_ttl);
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
time_t now, struct daemon *daemon); time_t now, struct daemon *daemon);
int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now); int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
struct bogus_addr *addr, time_t now); struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, unsigned char *find_pseudoheader(HEADER *header, unsigned int plen,
unsigned int *len, unsigned char **p); unsigned int *len, unsigned char **p);
int check_for_local_domain(char *name, time_t now, struct mx_record *mx); int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
unsigned int questions_crc(HEADER *header, unsigned int plen); unsigned int questions_crc(HEADER *header, unsigned int plen);
int resize_packet(HEADER *header, unsigned int plen, int resize_packet(HEADER *header, unsigned int plen,
unsigned char *pheader, unsigned int hlen); unsigned char *pheader, unsigned int hlen);
...@@ -453,13 +470,15 @@ struct daemon *read_opts (int argc, char **argv); ...@@ -453,13 +470,15 @@ struct daemon *read_opts (int argc, char **argv);
void forward_init(int first); void forward_init(int first);
void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now); void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now);
void receive_query(struct listener *listen, struct daemon *daemon, time_t now); void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
char *tcp_request(struct daemon *daemon, int confd, time_t now); char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
/* network.c */ /* network.c */
struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds); struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
void reload_servers(char *fname, struct daemon *daemon); void reload_servers(char *fname, struct daemon *daemon);
void check_servers(struct daemon *daemon, struct irec *interfaces); void check_servers(struct daemon *daemon, struct irec *interfaces);
struct irec *enumerate_interfaces(struct daemon *daemon); int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
union mysockaddr *test_addrp, struct in_addr *netmaskp);
struct listener *create_wildcard_listeners(int port); struct listener *create_wildcard_listeners(int port);
struct listener *create_bound_listeners(struct irec *interfaces, int port); struct listener *create_bound_listeners(struct irec *interfaces, int port);
...@@ -469,8 +488,10 @@ void dhcp_packet(struct daemon *daemon, time_t now); ...@@ -469,8 +488,10 @@ void dhcp_packet(struct daemon *daemon, time_t now);
struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr); struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr); struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
int address_allocate(struct dhcp_context *context, struct daemon *daemon, int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr *addrp, unsigned char *hwaddr); struct in_addr *addrp, unsigned char *hwaddr,
struct dhcp_netid *netids);
struct dhcp_config *find_config(struct dhcp_config *configs, struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context, struct dhcp_context *context,
unsigned char *clid, int clid_len, unsigned char *clid, int clid_len,
......
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley /* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -197,7 +197,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a ...@@ -197,7 +197,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.')) else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
flags = F_NXDOMAIN; flags = F_NXDOMAIN;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames)) if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
flags = F_NOERR; flags = F_NOERR;
if (flags == F_NXDOMAIN || flags == F_NOERR) if (flags == F_NXDOMAIN || flags == F_NOERR)
...@@ -390,7 +390,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now, ...@@ -390,7 +390,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
{ {
if (header->rcode == NXDOMAIN && if (header->rcode == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) && extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now, daemon->mxnames)) check_for_local_domain(daemon->namebuff, now, daemon))
{ {
/* if we forwarded a query for a locally known name (because it was for /* if we forwarded a query for a locally known name (because it was for
an unknown type) and the answer is NXDOMAIN, convert that to NODATA, an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
...@@ -474,6 +474,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -474,6 +474,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
unsigned short type; unsigned short type;
struct iname *tmp; struct iname *tmp;
struct all_addr dst_addr; struct all_addr dst_addr;
struct in_addr netmask, dst_addr_4;
int m, n, if_index = 0; int m, n, if_index = 0;
struct iovec iov[1]; struct iovec iov[1];
struct msghdr msg; struct msghdr msg;
...@@ -491,6 +492,14 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -491,6 +492,14 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif #endif
} control_u; } control_u;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
}
else
dst_addr_4.s_addr = 0;
iov[0].iov_base = daemon->packet; iov[0].iov_base = daemon->packet;
iov[0].iov_len = daemon->edns_pktsz; iov[0].iov_len = daemon->edns_pktsz;
...@@ -526,7 +535,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -526,7 +535,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{ {
dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst; dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
} }
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
...@@ -534,7 +543,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -534,7 +543,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
{ {
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr)); dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
} }
...@@ -557,7 +566,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -557,7 +566,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (if_index == 0) if (if_index == 0)
return; return;
if (daemon->if_except || daemon->if_names) if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
{ {
#ifdef SIOCGIFNAME #ifdef SIOCGIFNAME
ifr.ifr_ifindex = if_index; ifr.ifr_ifindex = if_index;
...@@ -567,6 +576,13 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -567,6 +576,13 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
if (!if_indextoname(if_index, ifr.ifr_name)) if (!if_indextoname(if_index, ifr.ifr_name))
return; return;
#endif #endif
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
} }
for (tmp = daemon->if_except; tmp; tmp = tmp->next) for (tmp = daemon->if_except; tmp; tmp = tmp->next)
...@@ -610,7 +626,8 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now) ...@@ -610,7 +626,8 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
#endif #endif
} }
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, now); m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon,
dst_addr_4, netmask, now);
if (m >= 1) if (m >= 1)
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index); send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
else else
...@@ -647,7 +664,8 @@ static int read_write(int fd, char *packet, int size, int rw) ...@@ -647,7 +664,8 @@ static int read_write(int fd, char *packet, int size, int rw)
blocking as neccessary, and then return. Note, need to be a bit careful 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 about resources for debug mode, when the fork is suppressed: that's
done by the caller. */ done by the caller. */
char *tcp_request(struct daemon *daemon, int confd, time_t now) char *tcp_request(struct daemon *daemon, int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
{ {
int size = 0, m; int size = 0, m;
unsigned short qtype, gotname; unsigned short qtype, gotname;
...@@ -689,7 +707,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now) ...@@ -689,7 +707,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
} }
/* m > 0 if answered from cache */ /* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, now); m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon,
local_addr, netmask, now);
if (m == 0) if (m == 0)
{ {
......
/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley /* dnsmasq is Copyright (c) 2000 - 2005 by Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
......
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
......
/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley /* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -14,10 +14,9 @@ ...@@ -14,10 +14,9 @@
#include "dnsmasq.h" #include "dnsmasq.h"
static struct irec *add_iface(struct daemon *daemon, struct irec *list, static int iface_allowed(struct daemon *daemon, struct irec *iface,
char *name, int is_loopback, union mysockaddr *addr) char *name, int is_loopback, union mysockaddr *addr)
{ {
struct irec *iface;
struct iname *tmp; struct iname *tmp;
/* If we are restricting the set of interfaces to use, make /* If we are restricting the set of interfaces to use, make
...@@ -45,12 +44,8 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list, ...@@ -45,12 +44,8 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
if (daemon->if_except) if (daemon->if_except)
for (tmp = daemon->if_except; tmp; tmp = tmp->next) for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && strcmp(tmp->name, name) == 0) if (tmp->name && strcmp(tmp->name, name) == 0)
{ return 0;
/* record address of named interfaces, for TCP access control */
tmp->addr = *addr;
return list;
}
/* we may need to check the whitelist */ /* we may need to check the whitelist */
if (daemon->if_names || daemon->if_addrs) if (daemon->if_names || daemon->if_addrs)
{ {
...@@ -58,36 +53,39 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list, ...@@ -58,36 +53,39 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
for (tmp = daemon->if_names; tmp; tmp = tmp->next) for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0)) if (tmp->name && (strcmp(tmp->name, name) == 0))
{ found = tmp->used = 1;
tmp->addr = *addr;
found = tmp->used = 1;
}
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (sockaddr_isequal(&tmp->addr, addr)) if (sockaddr_isequal(&tmp->addr, addr))
found = tmp->used = 1; found = tmp->used = 1;
if (!found) if (!found)
return list; return 0;
} }
/* check whether the interface IP has been added already /* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address */ it is possible to have multiple interfaces with the same address */
for (iface = list; iface; iface = iface->next) for (; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr)) if (sockaddr_isequal(&iface->addr, addr))
break; break;
if (iface) if (iface)
return list; return 0;
/* If OK, add it to the head of the list */ return 1;
iface = safe_malloc(sizeof(struct irec));
iface->addr = *addr;
iface->next = list;
return iface;
} }
/* This does two different jobs: if chainp is non-NULL, it puts
a list of all the interfaces allowed by config into *chainp.
If chainp is NULL, it returns 1 if addr is an address of an interface
allowed by config and if that address is IPv4, it fills in the
netmask of the interface.
If chainp is non-NULL, a zero return indicates a fatal error.
struct irec *enumerate_interfaces(struct daemon *daemon) If chainp is NULL, errors result in a match failure and zero return.
*/
int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
union mysockaddr *test_addrp, struct in_addr *netmaskp)
{ {
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
FILE *f; FILE *f;
...@@ -100,9 +98,16 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -100,9 +98,16 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
int lastlen = 0; int lastlen = 0;
int len = 20 * sizeof(struct ifreq); int len = 20 * sizeof(struct ifreq);
int fd = socket(PF_INET, SOCK_DGRAM, 0); int fd = socket(PF_INET, SOCK_DGRAM, 0);
struct in_addr netmask;
int ret = 0;
if (fd == -1) if (fd == -1)
die ("cannot create socket to enumerate interfaces: %s", NULL); return 0;
#ifdef HAVE_IPV6
if (test_addrp && test_addrp->sa.sa_family == AF_INET6)
test_addrp->in6.sin6_flowinfo = htonl(0);
#endif
while (1) while (1)
{ {
...@@ -113,7 +118,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -113,7 +118,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
{ {
if (errno != EINVAL || lastlen != 0) if (errno != EINVAL || lastlen != 0)
die ("ioctl error while enumerating interfaces: %s", NULL); goto exit;
} }
else else
{ {
...@@ -133,7 +138,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -133,7 +138,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
unaligned accesses. */ unaligned accesses. */
int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE; int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
if (!(ifr = realloc(ifr, ifr_len))) if (!(ifr = realloc(ifr, ifr_len)))
die("cannot allocate buffer", NULL); goto exit;
memcpy(ifr, ptr, ifr_len); memcpy(ifr, ptr, ifr_len);
ptr += ifr_len; ptr += ifr_len;
...@@ -147,6 +152,9 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -147,6 +152,9 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
{ {
addr.in = *((struct sockaddr_in *) &ifr->ifr_addr); addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
addr.in.sin_port = htons(daemon->port); addr.in.sin_port = htons(daemon->port);
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
goto exit;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6) else if (ifr->ifr_addr.sa_family == AF_INET6)
...@@ -164,9 +172,25 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -164,9 +172,25 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
continue; /* unknown address family */ continue; /* unknown address family */
if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0) if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
die("ioctl error getting interface flags: %m", NULL); goto exit;
iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr); if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
{
if (chainp)
{
struct irec *new = safe_malloc(sizeof(struct irec));
new->addr = addr;
new->netmask = netmask;
new->next = iface;
iface = new;
}
else if (sockaddr_isequal(&addr, test_addrp))
{
*netmaskp = netmask;
ret = 1;
goto exit;
}
}
} }
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6) #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
...@@ -198,13 +222,35 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -198,13 +222,35 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
strncpy(sifr.ifr_name, devname, IF_NAMESIZE); strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0) if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
die("ioctl error getting interface flags: %m", NULL); goto exit;
iface = add_iface(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr);
if (iface_allowed(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr))
{
if (chainp)
{
struct irec *new = safe_malloc(sizeof(struct irec));
new->addr = addr;
new->next = iface;
iface = new;
}
else if (sockaddr_isequal(&addr, test_addrp))
{
ret = 1;
goto exit;
}
}
} }
fclose(f); fclose(f);
} }
#endif /* LINUX */ #endif /* LINUX */
if (chainp)
{
*chainp = iface;
ret = 1;
}
exit:
if (buf) if (buf)
free(buf); free(buf);
#ifdef HAVE_SOCKADDR_SA_LEN #ifdef HAVE_SOCKADDR_SA_LEN
...@@ -213,7 +259,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon) ...@@ -213,7 +259,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
#endif #endif
close(fd); close(fd);
return iface; return ret;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
...@@ -354,6 +400,7 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port) ...@@ -354,6 +400,7 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
{ {
struct listener *new = safe_malloc(sizeof(struct listener)); struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family; new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = listeners; new->next = listeners;
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -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 || (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
......
/* dnsmasq is Copyright (c) 2000 - 2004 Simon Kelley /* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -21,7 +21,7 @@ struct myoption { ...@@ -21,7 +21,7 @@ struct myoption {
int val; int val;
}; };
#define OPTSTRING "ZDNLERKzowefnbvhdkqr: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:" #define OPTSTRING "yZDNLERKzowefnbvhdkqr: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:"
static struct myoption opts[] = { static struct myoption opts[] = {
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
...@@ -76,6 +76,8 @@ static struct myoption opts[] = { ...@@ -76,6 +76,8 @@ static struct myoption opts[] = {
{"edns-packet-max", 1, 0, 'P'}, {"edns-packet-max", 1, 0, 'P'},
{"keep-in-foreground", 0, 0, 'k'}, {"keep-in-foreground", 0, 0, 'k'},
{"dhcp-authoritative", 0, 0, 'K'}, {"dhcp-authoritative", 0, 0, 'K'},
{"srv-host", 1, 0, 'W'},
{"localise-queries", 0, 0, 'y'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
...@@ -102,6 +104,7 @@ static struct optflags optmap[] = { ...@@ -102,6 +104,7 @@ static struct optflags optmap[] = {
{ 'D', OPT_NODOTS_LOCAL }, { 'D', OPT_NODOTS_LOCAL },
{ 'z', OPT_NOWILD }, { 'z', OPT_NOWILD },
{ 'Z', OPT_ETHERS }, { 'Z', OPT_ETHERS },
{ 'y', OPT_LOCALISE },
{ 'v', 0}, { 'v', 0},
{ 'w', 0}, { 'w', 0},
{ 0, 0 } { 0, 0 }
...@@ -137,7 +140,7 @@ static char *usage = ...@@ -137,7 +140,7 @@ static char *usage =
"-K, --dhcp-authoritative Assume we are the only DHCP server on the local network.\n" "-K, --dhcp-authoritative Assume we are the only DHCP server on the local network.\n"
"-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n" "-l, --dhcp-leasefile=path Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
"-L, --localmx Return MX records for local hosts.\n" "-L, --localmx Return MX records for local hosts.\n"
"-m, --mx-host=host_name Specify the MX name to reply to.\n" "-m, --mx-host=host_name,target,pref Specify an MX record.\n"
"-M, --dhcp-boot=<bootp opts> Specify BOOTP options to DHCP server.\n" "-M, --dhcp-boot=<bootp opts> Specify BOOTP options to DHCP server.\n"
"-n, --no-poll Do NOT poll " RESOLVFILE " file, reload only on SIGHUP.\n" "-n, --no-poll Do NOT poll " RESOLVFILE " file, reload only on SIGHUP.\n"
"-N, --no-negcache Do NOT cache failed search results.\n" "-N, --no-negcache Do NOT cache failed search results.\n"
...@@ -152,15 +155,17 @@ static char *usage = ...@@ -152,15 +155,17 @@ static char *usage =
"-S, --server=/domain/ipaddr Specify address(es) of upstream servers with optional domains.\n" "-S, --server=/domain/ipaddr Specify address(es) of upstream servers with optional domains.\n"
" --local=/domain/ Never forward queries to specified domains.\n" " --local=/domain/ Never forward queries to specified domains.\n"
"-s, --domain=domain Specify the domain to be assigned in DHCP leases.\n" "-s, --domain=domain Specify the domain to be assigned in DHCP leases.\n"
"-t, --mx-target=host_name Specify the host in an MX reply.\n" "-t, --mx-target=host_name Specify default target in an MX record.\n"
"-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n" "-T, --local-ttl=time Specify time-to-live in seconds for replies from /etc/hosts.\n"
"-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n" "-u, --user=username Change to this user after startup. (defaults to " CHUSER ").\n"
"-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n" "-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n"
"-v, --version Display dnsmasq version and copyright information.\n" "-v, --version Display dnsmasq version and copyright information.\n"
"-V, --alias=addr,addr,mask Translate IPv4 addresses from upstream servers.\n" "-V, --alias=addr,addr,mask Translate IPv4 addresses from upstream servers.\n"
"-W, --srv-host=name,port,pri,weight Specify a SRV record.\n"
"-w, --help Display this message.\n" "-w, --help Display this message.\n"
"-x, --pid-file=path Specify path of PID file. (defaults to " RUNFILE ").\n" "-x, --pid-file=path Specify path of PID file. (defaults to " RUNFILE ").\n"
"-X, --dhcp-lease-max=number Specify maximum number of DHCP leases (defaults to %d).\n" "-X, --dhcp-lease-max=number Specify maximum number of DHCP leases (defaults to %d).\n"
"-y, --localise-queries Answer DNS queries based on the interface a query was sent to."
"-z, --bind-interfaces Bind only to interfaces in use.\n" "-z, --bind-interfaces Bind only to interfaces in use.\n"
"-Z, --read-ethers Read DHCP static host information from " ETHERSFILE ".\n" "-Z, --read-ethers Read DHCP static host information from " ETHERSFILE ".\n"
"\n"; "\n";
...@@ -372,24 +377,41 @@ struct daemon *read_opts (int argc, char **argv) ...@@ -372,24 +377,41 @@ struct daemon *read_opts (int argc, char **argv)
case 'm': case 'm':
{ {
int pref = 1;
struct mx_record *new;
if ((comma = strchr(optarg, ','))) if ((comma = strchr(optarg, ',')))
*(comma++) = 0; {
char *prefstr;
*(comma++) = 0;
if ((prefstr=strchr(comma, ',')))
{
*(prefstr++) = 0;
if (!atoi_check(prefstr, &pref))
{
option = '?';
problem = "bad MX preference";
break;
}
}
}
if (!canonicalise(optarg) || (comma && !canonicalise(comma))) if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
{ {
option = '?'; option = '?';
problem = "bad MX name"; problem = "bad MX name";
break;
} }
else
{ new = safe_malloc(sizeof(struct mx_record));
struct mx_record *new = safe_malloc(sizeof(struct mx_record)); new->next = daemon->mxnames;
new->next = daemon->mxnames; daemon->mxnames = new;
daemon->mxnames = new; new->mxname = safe_string_alloc(optarg);
new->mxname = safe_string_alloc(optarg); new->mxtarget = safe_string_alloc(comma); /* may be NULL */
new->mxtarget = safe_string_alloc(comma); /* may be NULL */ new->preference = pref;
}
break; break;
} }
case 't': case 't':
if (!canonicalise(optarg)) if (!canonicalise(optarg))
{ {
...@@ -747,7 +769,7 @@ struct daemon *read_opts (int argc, char **argv) ...@@ -747,7 +769,7 @@ struct daemon *read_opts (int argc, char **argv)
new->broadcast.s_addr = 0; new->broadcast.s_addr = 0;
new->router.s_addr = 0; new->router.s_addr = 0;
new->netid.net = NULL; new->netid.net = NULL;
new->static_only = 0; new->static_only = new->filter_netid = 0;
problem = "bad dhcp-range"; problem = "bad dhcp-range";
...@@ -758,7 +780,14 @@ struct daemon *read_opts (int argc, char **argv) ...@@ -758,7 +780,14 @@ struct daemon *read_opts (int argc, char **argv)
if (*cp != ',' && (comma = strchr(optarg, ','))) if (*cp != ',' && (comma = strchr(optarg, ',')))
{ {
*comma = 0; *comma = 0;
new->netid.net = safe_string_alloc(optarg); if (strstr(optarg, "net:") == optarg)
{
new->netid.net = safe_string_alloc(optarg+4);
new->netid.next = NULL;
new->filter_netid = 1;
}
else
new->netid.net = safe_string_alloc(optarg);
a[0] = comma + 1; a[0] = comma + 1;
} }
else else
...@@ -1363,6 +1392,84 @@ struct daemon *read_opts (int argc, char **argv) ...@@ -1363,6 +1392,84 @@ struct daemon *read_opts (int argc, char **argv)
break; break;
} }
case 'W':
{
int port = 1, priority = 0, weight = 0;
char *name, *target = NULL;
struct srv_record *new;
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!canonicalise(optarg))
{
option = '?';
problem = "bad SRV record";
break;
}
name = safe_string_alloc(optarg);
if (comma)
{
optarg = comma;
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!canonicalise(optarg))
{
option = '?';
problem = "bad SRV target";
break;
}
target = safe_string_alloc(optarg);
if (comma)
{
optarg = comma;
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!atoi_check(optarg, &port))
{
option = '?';
problem = "invalid port number";
break;
}
if (comma)
{
optarg = comma;
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!atoi_check(optarg, &priority))
{
option = '?';
problem = "invalid priority";
break;
}
if (comma)
{
optarg = comma;
if ((comma = strchr(optarg, ',')))
*(comma++) = 0;
if (!atoi_check(optarg, &weight))
{
option = '?';
problem = "invalid weight";
break;
}
}
}
}
}
new = safe_malloc(sizeof(struct srv_record));
new->next = daemon->srvnames;
daemon->srvnames = new;
new->srvname = name;
new->srvtarget = target;
new->srvport = port;
new->priority = priority;
new->weight = weight;
break;
}
} }
} }
...@@ -1411,24 +1518,39 @@ struct daemon *read_opts (int argc, char **argv) ...@@ -1411,24 +1518,39 @@ struct daemon *read_opts (int argc, char **argv)
#endif /* IPv6 */ #endif /* IPv6 */
} }
/* only one of these need be specified: the other defaults to the /* only one of these need be specified: the other defaults to the host-name */
host-name */
if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget) if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
{ {
if (gethostname(buff, MAXDNAME) == -1) if (gethostname(buff, MAXDNAME) == -1)
die("cannot get host-name: %s", NULL); die("cannot get host-name: %s", NULL);
if (!daemon->mxnames) if (!daemon->mxnames)
{ {
daemon->mxnames = safe_malloc(sizeof(struct mx_record)); daemon->mxnames = safe_malloc(sizeof(struct mx_record));
daemon->mxnames->next = NULL; daemon->mxnames->next = NULL;
daemon->mxnames->mxtarget = NULL; daemon->mxnames->mxtarget = NULL;
daemon->mxnames->mxname = safe_string_alloc(buff); daemon->mxnames->mxname = safe_string_alloc(buff);
} }
if (!daemon->mxtarget) if (!daemon->mxtarget)
daemon->mxtarget = safe_string_alloc(buff); daemon->mxtarget = safe_string_alloc(buff);
} }
if (daemon->domain_suffix)
{
/* add domain for any srv record without one. */
struct srv_record *srv;
for (srv = daemon->srvnames; srv; srv = srv->next)
if (strchr(srv->srvname, '.') && strchr(srv->srvname, '.') == strrchr(srv->srvname, '.'))
{
strcpy(buff, srv->srvname);
strcat(buff, ".");
strcat(buff, daemon->domain_suffix);
free(srv->srvname);
srv->srvname = safe_string_alloc(buff);
}
}
if (daemon->options & OPT_NO_RESOLV) if (daemon->options & OPT_NO_RESOLV)
daemon->resolv_files = 0; daemon->resolv_files = 0;
......
This diff is collapsed.
/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley /* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -59,13 +59,12 @@ static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dh ...@@ -59,13 +59,12 @@ static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dh
static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string); static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
static void bootp_option_put(struct dhcp_packet *mess, static void bootp_option_put(struct dhcp_packet *mess,
struct dhcp_boot *boot_opts, struct dhcp_netid *netids); struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
static unsigned int option_len(unsigned char *opt); static int option_len(unsigned char *opt);
static void *option_ptr(unsigned char *opt); static void *option_ptr(unsigned char *opt);
static struct in_addr option_addr(unsigned char *opt); static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int size); static unsigned int option_uint(unsigned char *opt, int size);
static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string); static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool); static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize);
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize);
static unsigned char *do_req_options(struct dhcp_context *context, static unsigned char *do_req_options(struct dhcp_context *context,
unsigned char *p, unsigned char *end, unsigned char *p, unsigned char *end,
unsigned char *req_options, unsigned char *req_options,
...@@ -211,7 +210,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -211,7 +210,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if (have_config(config, CONFIG_NAME)) if (have_config(config, CONFIG_NAME))
hostname = config->hostname; hostname = config->hostname;
if (context->netid.net) if (context->netid.net && !context->filter_netid)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; netid = &context->netid;
...@@ -255,7 +254,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -255,7 +254,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
hostname = config->hostname; hostname = config->hostname;
else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1))) else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
{ {
unsigned int len = option_len(opt); int len = option_len(opt);
hostname = daemon->dhcp_buff; hostname = daemon->dhcp_buff;
memcpy(hostname, option_ptr(opt), len); memcpy(hostname, option_ptr(opt), len);
/* May not be zero terminated */ /* May not be zero terminated */
...@@ -291,7 +290,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -291,7 +290,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1))) if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{ {
unsigned char *ucp = option_ptr(opt); unsigned char *ucp = option_ptr(opt);
unsigned int tmp, j; int tmp, j;
for (j = 0; j < option_len(opt); j += ucp[j] + 1); for (j = 0; j < option_len(opt); j += ucp[j] + 1);
if (j == option_len(opt)) if (j == option_len(opt))
for (j = 0; j < option_len(opt); j = tmp) for (j = 0; j < option_len(opt); j = tmp)
...@@ -304,7 +303,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -304,7 +303,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1))) if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
{ {
unsigned int i; int i;
for (i = 0; i <= (option_len(opt) - vendor->len); i++) for (i = 0; i <= (option_len(opt) - vendor->len); i++)
if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0) if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
{ {
...@@ -407,7 +406,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -407,7 +406,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) && else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr)) !config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr; mess->yiaddr = addr;
else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr)) else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, netid))
message = "no address available"; message = "no address available";
log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message); log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);
...@@ -415,7 +414,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -415,7 +414,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
return 0; return 0;
context = narrow_context(context, mess->yiaddr); context = narrow_context(context, mess->yiaddr);
if (context->netid.net) if (context->netid.net && !context->filter_netid)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; netid = &context->netid;
...@@ -542,7 +541,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam ...@@ -542,7 +541,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname); log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
context = narrow_context(context, mess->yiaddr); context = narrow_context(context, mess->yiaddr);
if (context->netid.net) if (context->netid.net && !context->filter_netid)
{ {
context->netid.next = netid; context->netid.next = netid;
netid = &context->netid; netid = &context->netid;
...@@ -628,7 +627,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, ...@@ -628,7 +627,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr,
string ? string : ""); string ? string : "");
} }
static unsigned int option_len(unsigned char *opt) static int option_len(unsigned char *opt)
{ {
return opt[1]; return opt[1];
} }
...@@ -769,7 +768,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt ...@@ -769,7 +768,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
return NULL; return NULL;
} }
static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize) static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize)
{ {
int overload = 0; int overload = 0;
unsigned char *ret; unsigned char *ret;
...@@ -805,32 +804,6 @@ static int in_list(unsigned char *list, int opt) ...@@ -805,32 +804,6 @@ static int in_list(unsigned char *list, int opt)
return 0; return 0;
} }
/* Is every member of check matched by a member of pool? */
static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
{
struct dhcp_netid *tmp1;
if (!check)
return 0;
for (; check; check = check->next)
{
if (check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt) static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
{ {
struct dhcp_opt *tmp; struct dhcp_opt *tmp;
......
...@@ -113,20 +113,22 @@ int legal_char(char c) ...@@ -113,20 +113,22 @@ int legal_char(char c)
int canonicalise(char *s) int canonicalise(char *s)
{ {
/* check for legal chars and remove trailing . /* check for legal chars and remove trailing .
also fail empty string. */ also fail empty string and label > 63 chars */
int l = strlen(s); int dotgap = 0, l = strlen(s);
char c; char c;
if (l == 0) return 0; if (l == 0 || l > MAXDNAME) return 0;
if (s[l-1] == '.') if (s[l-1] == '.')
{ {
if (l == 1) return 0; if (l == 1) return 0;
s[l-1] = 0; s[l-1] = 0;
} }
while ((c = *s++)) while ((c = *s++))
if (c != '.' && !legal_char(c)) if (c == '.')
dotgap = 0;
else if (!legal_char(c) || (++dotgap > MAXLABEL))
return 0; return 0;
return 1; return 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment