Compare commits

...

24 Commits

Author SHA1 Message Date
Simon Kelley
8ff556739e SOA serial tweak. 2012-12-09 21:09:01 +00:00
Simon Kelley
496787677e Zone-transfer peer restriction option. 2012-12-09 18:31:10 +00:00
Simon Kelley
e1ff419cf9 Complete AXFR support 2012-12-09 17:08:47 +00:00
Simon Kelley
ee86ce68fc Fix TCP query forwarding to non-default servers. 2012-12-07 11:54:46 +00:00
Simon Kelley
b75e936372 First cut at zone transfer. 2012-12-07 11:50:41 +00:00
Simon Kelley
aa79235194 zero arcount. 2012-12-06 19:41:35 +00:00
Simon Kelley
7c305be1bd Bump Debian version. 2012-12-04 20:59:06 +00:00
Simon Kelley
f7fe362721 Tidy merge. 2012-12-04 20:55:54 +00:00
Simon Kelley
36bec089f7 Merge branch 'auth' 2012-12-04 20:50:38 +00:00
Simon Kelley
45dd1fece4 Correct NS and SOA records in auth mode for PTR queries. 2012-12-04 20:49:24 +00:00
Simon Kelley
29d28dda95 Don't send RAs to the wrong place when DAD in progress. 2012-12-03 14:05:59 +00:00
Simon Kelley
421594f83d Forgot --dhcp-except check in previous commit. 2012-12-02 12:17:35 +00:00
Simon Kelley
d89fb4ed4f Check interface for router advertisements. 2012-12-01 21:21:13 +00:00
Simon Kelley
295a54eed3 SetDomainServers Dbus method. 2012-12-01 21:02:15 +00:00
Simon Kelley
5c0bd5b112 CNAME auth support. 2012-12-01 16:42:47 +00:00
Simon Kelley
86e3b9a026 Post-test fixes. 2012-11-30 13:46:48 +00:00
Simon Kelley
2f38141f43 Don't elide code needed for --bind-dynamic if compiled without IPv6. 2012-11-29 21:16:44 +00:00
Simon Kelley
8273ea5a19 Add MX support. 2012-11-29 21:12:33 +00:00
Simon Kelley
4f7b304f53 Initial code to do authoritative DNS. 2012-11-28 21:27:02 +00:00
Simon Kelley
8e4b87918f Header-file dependency checking in Makefile. 2012-11-14 14:12:56 +00:00
Simon Kelley
83b2198e86 Add warning to man page, -d option 2012-11-12 21:07:44 +00:00
Simon Kelley
d1a5975f9b No lease-time in DHCPINFORM replies. 2012-11-05 16:50:30 +00:00
Simon Kelley
52002051ad Doc update for previous checkin. 2012-10-26 11:39:02 +01:00
Simon Kelley
b191a77901 trivial indent fix. 2012-10-24 14:16:00 +01:00
24 changed files with 1463 additions and 305 deletions

View File

@@ -1,3 +1,8 @@
version 2.65
Fix regression which broke forwarding of queries sent via
TCP which are not for A and AAAA and which were directed to
non-default servers. Thanks to Niax for the bug report.
version 2.64
Handle DHCP FQDN options with all flag bits zero and
--dhcp-client-update set. Thanks to Bernd Krumbroeck for
@@ -19,9 +24,12 @@ version 2.64
Flag DHCP or DHCPv6 in starup logging. Thanks to
Vladislav Grishenko for the patch.
Add SetServersEX method in DBus interface. Thanks to Dan
Add SetServersEx method in DBus interface. Thanks to Dan
Williams for the patch.
Add SetDomainServers method in DBus interface. Thanks to
Roy Marples for the patch.
Fix build with later Lua libraries. Thansk to Cristian
Rodriguez for the patch.
@@ -45,7 +53,27 @@ version 2.64
subnet, --dhcp-range=::,static Useful for Stateless
DHCPv6. Thanks to Vladislav Grishenko for the patch.
Don't include lease-time in DHCPACK replies to DHCPINFORM
queries, since RFC-2131 says we shouldn't. Thanks to
Wouter Ibens for pointing this out.
Makefile tweak to do dependency checking on header files.
Thanks to Johan Peeters for the patch.
Check interface for outgoing unsolicited router
advertisements, rather than relying on interface address
configuration. Thanks to Gene Czarinski for the patch.
Handle better attempts to transmit on interfaces which are
still doing DAD, and specifically do not just transmit
without setting source address and interface, since this
can cause very puzzling effects when a router
advertisement goes astray. Thanks again to Gene Czarinski.
Get RA timers right when there is more than one
dhcp-range on a subnet.
version 2.63
Do duplicate dhcp-host address check in --test mode.

View File

@@ -65,7 +65,7 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o
dhcp-common.o outpacket.o radv.o slaac.o auth.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
@@ -79,7 +79,8 @@ all : $(BUILDDIR)
clean :
rm -f *~ $(BUILDDIR)/*.mo contrib/*/*~ */*~ $(BUILDDIR)/*.pot
rm -f $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq core */core
rm -f $(BUILDDIR)/.configured $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq
rm -rf core */core
install : all install-common
@@ -113,7 +114,11 @@ $(BUILDDIR):
mkdir -p $(BUILDDIR)
# rules below are targets in recusive makes with cwd=$(SRC)
# rules below are targets in recusive makes with cwd=$(BUILDDIR)
.configured: $(hdrs)
@rm -f *.o
@touch $@
$(objs:.o=.c) $(hdrs):
ln -s $(top)/$(SRC)/$@ .
@@ -121,7 +126,7 @@ $(objs:.o=.c) $(hdrs):
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $<
dnsmasq : $(hdrs) $(objs)
dnsmasq : .configured $(hdrs) $(objs)
$(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS)
dnsmasq.pot : $(objs:.o=.c) $(hdrs)

View File

@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c
radv.c slaac.c auth.c
LOCAL_MODULE := dnsmasq

View File

@@ -137,6 +137,23 @@ Each call to SetServersEx completely replaces the set of servers
specified by via the DBus, but it leaves any servers specified via the
command line or /etc/dnsmasq.conf or /etc/resolv.conf alone.
SetDomainServers
----------------
Yes another variation for setting DNS servers, with the capability of
SetServersEx, but without using arrays of arrays, which are not
sendable with dbus-send. The arguments are an array of strings which
are identical to the equivalent arguments --server, so the example
for SetServersEx is represented as
[
"/foobar.com/1.2.3.4"
"/eng.mycorp.com/lab.mycorp.com/1003:1234:abcd::1%eth0"
]
2. SIGNALS
----------

6
debian/changelog vendored
View File

@@ -1,3 +1,9 @@
dnsmasq (2.65-1) unstable; urgency=low
* New upstream.
-- Simon Kelley <simon@thekelleys.org.uk> Tue, 4 Dec 2012 20:58:12 +0000
dnsmasq (2.64-1) unstable; urgency=low
* New upstream.

View File

@@ -83,7 +83,9 @@ or launchd.
Debug mode: don't fork to the background, don't write a pid file,
don't change user id, generate a complete cache dump on receipt on
SIGUSR1, log to stderr as well as syslog, don't fork new processes
to handle TCP queries.
to handle TCP queries. Note that this option is for use in debugging
only, to stop dnsmasq daemonising in production, use
.B -k.
.TP
.B \-q, --log-queries
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1.
@@ -584,7 +586,11 @@ which tells dnsmasq to enable DHCP for the network specified, but not
to dynamically allocate IP addresses: only hosts which have static
addresses given via
.B dhcp-host
or from /etc/ethers will be served.
or from /etc/ethers will be served. A static-only subnet with address
all zeros may be used as a "catch-all" address to enable replies to all
Information-request packets on a subnet which is provided with
stateless DHCPv6, ie
.B --dhcp=range=::,static
For IPv4, the <mode> may be
.B proxy

738
src/auth.c Normal file
View File

@@ -0,0 +1,738 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
static struct subnet *filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{
struct subnet *subnet;
for (subnet = zone->subnet; subnet; subnet = subnet->next)
{
if (subnet->is6 && (flag & F_IPV4))
continue;
if (!subnet->is6)
{
struct in_addr addr = addr_u->addr.addr4;
struct in_addr mask;
mask.s_addr = htonl(~((1 << (32 - subnet->prefixlen)) - 1));
if (is_same_net(addr, subnet->addr4, mask))
return subnet;
}
#ifdef HAVE_IPV6
else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr6, subnet->prefixlen))
return subnet;
#endif
}
return NULL;
}
static int in_zone(struct auth_zone *zone, char *name, char **cut)
{
size_t namelen = strlen(name);
size_t domainlen = strlen(zone->domain);
if (cut)
*cut = NULL;
if (namelen >= domainlen &&
hostname_isequal(zone->domain, &name[namelen - domainlen]))
{
if (namelen == domainlen)
return 1;
if (name[namelen - domainlen - 1] == '.')
{
if (cut)
*cut = &name[namelen - domainlen - 1];
return 1;
}
}
return 0;
}
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp;
int qtype, qclass;
int nameoffset, axfroffset = 0;
int q, anscount = 0, authcount = 0;
struct crec *crecp;
int auth = 1, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0;
struct auth_zone *zone = NULL;
struct subnet *subnet = NULL;
char *cut;
struct mx_srv_record *rec, *move, **up;
struct txt_record *txt;
struct interface_name *intr;
struct naptr *na;
struct all_addr addr;
struct cname *a;
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
/* determine end of question section (we put answers there) */
if (!(ansp = skip_questions(header, qlen)))
return 0; /* bad packet */
/* now process each question, answers go in RRs after the question */
p = (unsigned char *)(header+1);
for (q = ntohs(header->qdcount); q != 0; q--)
{
unsigned short flag = 0;
int found = 0;
/* save pointer to name for copying into answers */
nameoffset = p - (unsigned char *)header;
/* now extract name as .-concatenated string into name */
if (!extract_name(header, qlen, &p, name, 1, 4))
return 0; /* bad packet */
GETSHORT(qtype, p);
GETSHORT(qclass, p);
if (qclass != C_IN)
continue;
if (qtype == T_PTR)
{
if (!(flag = in_arpa_name_2_addr(name, &addr)))
continue;
for (zone = daemon->auth_zones; zone; zone = zone->next)
if ((subnet = filter_zone(zone, flag, &addr)))
break;
if (!zone)
{
auth = 0;
continue;
}
if (flag == F_IPV4)
{
for (intr = daemon->int_names; intr; intr = intr->next)
{
if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
break;
else
while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
intr = intr->next;
}
if (intr)
{
if (in_zone(zone, intr->name, NULL))
{
found = 1;
log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", intr->name))
anscount++;
}
}
}
if ((crecp = cache_find_by_addr(NULL, &addr, now, flag)))
do {
strcpy(name, cache_get_name(crecp));
if (crecp->flags & F_DHCP && !option_bool(OPT_DHCP_FQDN))
{
char *p = strchr(name, '.');
if (p)
*p = 0; /* must be bare name */
/* add external domain */
strcat(name, ".");
strcat(name, zone->domain);
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", name))
anscount++;
}
else if (crecp->flags & (F_DHCP | F_HOSTS) && in_zone(zone, name, NULL))
{
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_PTR, C_IN, "d", name))
anscount++;
}
else
continue;
} while ((crecp = cache_find_by_addr(crecp, &addr, now, flag)));
if (!found)
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | F_AUTH, NULL, &addr, NULL);
continue;
}
cname_restart:
for (zone = daemon->auth_zones; zone; zone = zone->next)
if (in_zone(zone, name, &cut))
break;
if (!zone)
{
auth = 0;
continue;
}
for (rec = daemon->mxnames; rec; rec = rec->next)
if (!rec->issrv && hostname_isequal(name, rec->name))
{
nxdomain = 0;
if (qtype == T_MX)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
anscount++;
}
}
for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name))
{
nxdomain = 0;
if (qtype == T_SRV)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
anscount++;
}
/* unlink first SRV record found */
if (!move)
{
move = rec;
*up = rec->next;
}
else
up = &rec->next;
}
else
up = &rec->next;
/* put first SRV record back at the end. */
if (move)
{
*up = move;
move->next = NULL;
}
for (txt = daemon->rr; txt; txt = txt->next)
if (hostname_isequal(name, txt->name))
{
nxdomain = 0;
if (txt->class == qtype)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
anscount++;
}
}
for (txt = daemon->txt; txt; txt = txt->next)
if (txt->class == C_IN && hostname_isequal(name, txt->name))
{
nxdomain = 0;
if (qtype == T_TXT)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
anscount++;
}
}
for (na = daemon->naptr; na; na = na->next)
if (hostname_isequal(name, na->name))
{
nxdomain = 0;
if (qtype == T_NAPTR)
{
found = 1;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
NULL, T_NAPTR, C_IN, "sszzzd",
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
anscount++;
}
}
for (intr = daemon->int_names; intr; intr = intr->next)
if (hostname_isequal(name, intr->name))
{
nxdomain = 0;
if (qtype == T_A && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
{
found = 1;
log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, T_A, C_IN, "4", &addr))
anscount++;
}
}
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(name, a->alias) )
{
log_query(F_CONFIG | F_CNAME, name, NULL, NULL);
strcpy(name, a->target);
if (!strchr(name, '.'))
{
strcat(name, ".");
strcat(name, zone->domain);
}
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL,
T_CNAME, C_IN, "d", name))
anscount++;
goto cname_restart;
}
if (qtype == T_A)
flag = F_IPV4;
#ifdef HAVE_IPV6
if (qtype == T_AAAA)
flag = F_IPV6;
#endif
if (!cut)
{
nxdomain = 0;
if (qtype == T_SOA)
{
soa = 1; /* inhibits auth section */
found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>");
}
else if (qtype == T_AXFR)
{
if (daemon->auth_peers)
{
struct iname *peers;
if (peer_addr->sa.sa_family == AF_INET)
peer_addr->in.sin_port = 0;
#ifdef HAVE_IPV6
else
peer_addr->in6.sin6_port = 0;
#endif
for (peers = daemon->auth_peers; peers; peers = peers->next)
if (sockaddr_isequal(peer_addr, &peers->addr))
break;
if (!peers)
{
if (peer_addr->sa.sa_family == AF_INET)
inet_ntop(AF_INET, &peer_addr->in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_IPV6
else
inet_ntop(AF_INET6, &peer_addr->in6.sin6_addr, daemon->addrbuff, ADDRSTRLEN);
#endif
my_syslog(LOG_WARNING, _("ignoring zone transfer request from %s"), daemon->addrbuff);
return 0;
}
}
soa = 1; /* inhibits auth section */
ns = 1; /* ensure we include NS records! */
axfr = 1;
found = 1;
axfroffset = nameoffset;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>");
}
else if (qtype == T_NS)
{
ns = 1; /* inhibits auth section */
found = 1;
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>");
}
}
if (!option_bool(OPT_DHCP_FQDN) && cut)
{
*cut = 0; /* remove domain part */
if (!strchr(name, '.') && (crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
{
if (crecp->flags & F_DHCP)
do
{
nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr)))
{
*cut = '.'; /* restore domain part */
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
*cut = 0; /* remove domain part */
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
} while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
}
*cut = '.'; /* restore domain part */
}
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)))
{
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
do
{
nxdomain = 0;
if ((crecp->flags & flag) && filter_zone(zone, flag, &(crecp->addr.addr)))
{
log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
found = 1;
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
qtype == T_A ? "4" : "6", &crecp->addr))
anscount++;
}
} while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
}
if (!found)
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
}
/* Add auth section */
if (auth)
{
char *authname;
int newoffset, offset = 0;
if (!subnet)
authname = zone->domain;
else
{
/* handle NS and SOA for PTR records */
authname = name;
if (!subnet->is6)
{
in_addr_t a = ntohl(subnet->addr4.s_addr) >> 8;
char *p = name;
if (subnet->prefixlen == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (subnet->prefixlen != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
}
#ifdef HAVE_IPV6
else
{
char *p = name;
int i;
for (i = subnet->prefixlen-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)&subnet->addr6)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
}
#endif
}
/* handle NS and SOA in auth section or for explicit queries */
newoffset = ansp - (unsigned char *)header;
if (((anscount == 0 && !ns) || soa) &&
add_resource_record(header, limit, &trunc, 0, &ansp,
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
authname, daemon->authserver, daemon->hostmaster,
daemon->soa_sn, daemon->soa_refresh,
daemon->soa_retry, daemon->soa_expiry,
daemon->auth_ttl))
{
offset = newoffset;
if (soa)
anscount++;
else
authcount++;
}
if (anscount != 0 || ns)
{
struct name_list *secondary;
newoffset = ansp - (unsigned char *)header;
if (add_resource_record(header, limit, &trunc, -offset, &ansp,
daemon->auth_ttl, NULL, T_NS, C_IN, "d", offset == 0 ? authname : NULL, daemon->authserver))
{
if (offset == 0)
offset = newoffset;
if (ns)
anscount++;
else
authcount++;
}
if (!subnet)
for (secondary = daemon->secondary_forward_server; secondary; secondary = secondary->next)
if (add_resource_record(header, limit, &trunc, offset, &ansp,
daemon->auth_ttl, NULL, T_NS, C_IN, "d", secondary->name))
{
if (ns)
anscount++;
else
authcount++;
}
}
if (axfr)
{
for (rec = daemon->mxnames; rec; rec = rec->next)
if (in_zone(zone, rec->name, &cut))
{
if (cut)
*cut = 0;
if (rec->issrv)
{
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
NULL, T_SRV, C_IN, "sssd", cut ? rec->name : NULL,
rec->priority, rec->weight, rec->srvport, rec->target))
anscount++;
}
else
{
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
NULL, T_MX, C_IN, "sd", cut ? rec->name : NULL, rec->weight, rec->target))
anscount++;
}
/* restore config data */
if (cut)
*cut = '.';
}
for (txt = daemon->rr; txt; txt = txt->next)
if (in_zone(zone, txt->name, &cut))
{
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
NULL, txt->class, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
anscount++;
/* restore config data */
if (cut)
*cut = '.';
}
for (txt = daemon->txt; txt; txt = txt->next)
if (txt->class == C_IN && in_zone(zone, txt->name, &cut))
{
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
NULL, T_TXT, C_IN, "t", cut ? txt->name : NULL, txt->len, txt->txt))
anscount++;
/* restore config data */
if (cut)
*cut = '.';
}
for (na = daemon->naptr; na; na = na->next)
if (in_zone(zone, na->name, &cut))
{
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp, daemon->auth_ttl,
NULL, T_NAPTR, C_IN, "sszzzd", cut ? na->name : NULL,
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
anscount++;
/* restore config data */
if (cut)
*cut = '.';
}
for (intr = daemon->int_names; intr; intr = intr->next)
if (in_zone(zone, intr->name, &cut) && (addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr != (in_addr_t) -1)
{
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, T_A, C_IN, "4", cut ? intr->name : NULL, &addr))
anscount++;
/* restore config data */
if (cut)
*cut = '.';
}
for (a = daemon->cnames; a; a = a->next)
if (in_zone(zone, a->alias, &cut))
{
strcpy(name, a->target);
if (!strchr(name, '.'))
{
strcat(name, ".");
strcat(name, zone->domain);
}
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL,
T_CNAME, C_IN, "d", cut ? a->alias : NULL, name))
anscount++;
}
cache_enumerate(1);
while ((crecp = cache_enumerate(0)))
{
if ((crecp->flags & (F_IPV4 | F_IPV6)) &&
!(crecp->flags & (F_NEG | F_NXDOMAIN)) &&
(crecp->flags & F_FORWARD))
{
if ((crecp->flags & F_DHCP) && !option_bool(OPT_DHCP_FQDN))
{
char *cache_name = cache_get_name(crecp);
if (!strchr(cache_name, '.') && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
if (crecp->flags & F_IPV6)
qtype = T_AAAA;
#endif
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
(crecp->flags & F_IPV4) ? "4" : "6", cache_name, &crecp->addr))
anscount++;
}
}
if ((crecp->flags & F_HOSTS) || (((crecp->flags & F_DHCP) && option_bool(OPT_DHCP_FQDN))))
{
strcpy(name, cache_get_name(crecp));
if (in_zone(zone, name, &cut) && filter_zone(zone, (crecp->flags & (F_IPV6 | F_IPV4)), &(crecp->addr.addr)))
{
qtype = T_A;
#ifdef HAVE_IPV6
if (crecp->flags & F_IPV6)
qtype = T_AAAA;
#endif
if (cut)
*cut = 0;
if (add_resource_record(header, limit, &trunc, -axfroffset, &ansp,
daemon->auth_ttl, NULL, qtype, C_IN,
(crecp->flags & F_IPV4) ? "4" : "6", cut ? name : NULL, &crecp->addr))
anscount++;
}
}
}
}
/* repeat SOA as last record */
if (add_resource_record(header, limit, &trunc, axfroffset, &ansp,
daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
daemon->authserver, daemon->hostmaster,
daemon->soa_sn, daemon->soa_refresh,
daemon->soa_retry, daemon->soa_expiry,
daemon->auth_ttl))
anscount++;
}
}
/* done all questions, set up header and return length of result */
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
/* clear RA flag */
header->hb4 &= ~HB4_RA;
/* authoritive */
if (auth)
header->hb3 |= HB3_AA;
/* truncation */
if (trunc)
header->hb3 |= HB3_TC;
if (anscount == 0 && auth && nxdomain)
SET_RCODE(header, NXDOMAIN);
else
SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount);
header->nscount = htons(authcount);
header->arcount = htons(0);
return ansp - (unsigned char *)header;
}

View File

@@ -235,6 +235,29 @@ char *cache_get_name(struct crec *crecp)
return crecp->name.sname;
}
struct crec *cache_enumerate(int init)
{
static int bucket;
static struct crec *cache;
if (init)
{
bucket = 0;
cache = NULL;
}
else if (cache && cache->hash_next)
cache = cache->hash_next;
else
{
cache = NULL;
while (bucket < hash_size)
if ((cache = hash_table[bucket++]))
break;
}
return cache;
}
static int is_outdated_cname_pointer(struct crec *crecp)
{
if (!(crecp->flags & F_CNAME))
@@ -1248,14 +1271,14 @@ char *record_source(int index)
return "<unknown>";
}
void querystr(char *str, unsigned short type)
void querystr(char *desc, char *str, unsigned short type)
{
unsigned int i;
sprintf(str, "query[type=%d]", type);
sprintf(str, "%s[type=%d]", desc, type);
for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
if (typestr[i].type == type)
sprintf(str,"query[%s]", typestr[i].name);
sprintf(str,"%s[%s]", desc, typestr[i].name);
}
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
@@ -1316,6 +1339,8 @@ void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_SERVER)
{
source = "forwarded";

View File

@@ -42,7 +42,11 @@
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
#define AUTH_TTL 600 /* default TTL for auth DNS */
#define SOA_REFRESH 1200 /* SOA refresh default */
#define SOA_RETRY 180 /* SOA retry default */
#define SOA_EXPIRY 1209600 /* SOA expiry default */
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC

View File

@@ -38,6 +38,9 @@ const char* introspection_xml_template =
" <method name=\"SetServers\">\n"
" <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
" </method>\n"
" <method name=\"SetDomainServers\">\n"
" <arg name=\"servers\" direction=\"in\" type=\"as\"/>\n"
" </method>\n"
" <method name=\"SetServersEx\">\n"
" <arg name=\"servers\" direction=\"in\" type=\"aas\"/>\n"
" </method>\n"
@@ -292,12 +295,15 @@ static void dbus_read_servers(DBusMessage *message)
cleanup_dbus();
}
static DBusMessage* dbus_read_servers_ex(DBusMessage *message)
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{
DBusMessageIter iter, array_iter, string_iter;
DBusMessage *error = NULL;
const char *addr_err;
char *dup = NULL;
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
if (!dbus_message_iter_init(message, &iter))
{
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
@@ -306,10 +312,10 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message)
/* check that the message contains an array of arrays */
if ((dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY))
(dbus_message_iter_get_element_type(&iter) != (strings ? DBUS_TYPE_STRING : DBUS_TYPE_ARRAY)))
{
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected array of string arrays");
strings ? "Expected array of string" : "Expected array of string arrays");
}
mark_dbus();
@@ -321,51 +327,91 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message)
const char *str = NULL;
union mysockaddr addr, source_addr;
char interface[IF_NAMESIZE];
char *str_addr;
char *str_addr, *str_domain = NULL;
/* check the types of the struct and its elements */
if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING))
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected inner array of strings");
break;
}
if (strings)
{
dbus_message_iter_get_basic(&array_iter, &str);
if (!str || !strlen (str))
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Empty string");
break;
}
/* dup the string because it gets modified during parsing */
if (!(dup = str_domain = whine_malloc(strlen(str)+1)))
break;
/* string_iter points to each "s" element in the inner array */
dbus_message_iter_recurse(&array_iter, &string_iter);
if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING)
{
/* no IP address given */
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected IP address");
break;
}
strcpy(str_domain, str);
dbus_message_iter_get_basic(&string_iter, &str);
if (!str || !strlen (str))
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Empty IP address");
break;
}
/* point to address part of old string for error message */
if ((str_addr = strrchr(str, '/')))
str = str_addr+1;
if ((str_addr = strrchr(str_domain, '/')))
{
if (*str_domain != '/' || str_addr == str_domain)
{
error = dbus_message_new_error_printf(message,
DBUS_ERROR_INVALID_ARGS,
"No domain terminator '%s'",
str);
break;
}
*str_addr++ = 0;
str_domain++;
}
else
{
str_addr = str_domain;
str_domain = NULL;
}
}
else
{
/* check the types of the struct and its elements */
if ((dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY) ||
(dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_STRING))
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected inner array of strings");
break;
}
/* string_iter points to each "s" element in the inner array */
dbus_message_iter_recurse(&array_iter, &string_iter);
if (dbus_message_iter_get_arg_type(&string_iter) != DBUS_TYPE_STRING)
{
/* no IP address given */
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Expected IP address");
break;
}
dbus_message_iter_get_basic(&string_iter, &str);
if (!str || !strlen (str))
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Empty IP address");
break;
}
/* dup the string because it gets modified during parsing */
if (!(dup = str_addr = whine_malloc(strlen(str)+1)))
break;
strcpy(str_addr, str);
}
memset(&addr, 0, sizeof(addr));
memset(&source_addr, 0, sizeof(source_addr));
memset(&interface, 0, sizeof(interface));
/* dup the string because it gets modified during parsing */
str_addr = strdup(str);
if (!str_addr)
{
error = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Out of memory parsing IP address");
break;
}
/* parse the IP address */
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL);
free(str_addr);
if (addr_err)
{
@@ -375,25 +421,47 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message)
break;
}
/* jump past the address to the domain list (if any) */
dbus_message_iter_next (&string_iter);
/* parse domains and add each server/domain pair to the list */
do {
str = NULL;
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(&addr, &source_addr, interface, str);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
if (strings)
{
char *p;
do {
if (str_domain)
{
if ((p = strchr(str_domain, '/')))
*p++ = 0;
}
else
p = NULL;
add_update_server(&addr, &source_addr, interface, str_domain);
} while ((str_domain = p));
}
else
{
/* jump past the address to the domain list (if any) */
dbus_message_iter_next (&string_iter);
/* parse domains and add each server/domain pair to the list */
do {
str = NULL;
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter);
add_update_server(&addr, &source_addr, interface, str);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
}
/* jump to next element in outer array */
dbus_message_iter_next(&array_iter);
}
cleanup_dbus();
if (dup)
free(dup);
return error;
}
@@ -403,7 +471,7 @@ DBusHandlerResult message_handler(DBusConnection *connection,
{
char *method = (char *)dbus_message_get_member(message);
DBusMessage *reply = NULL;
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
/* string length: "%s" provides space for termination zero */
@@ -432,8 +500,12 @@ DBusHandlerResult message_handler(DBusConnection *connection,
}
else if (strcmp(method, "SetServersEx") == 0)
{
my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
reply = dbus_read_servers_ex(message);
reply = dbus_read_servers_ex(message, 0);
check_servers();
}
else if (strcmp(method, "SetDomainServers") == 0)
{
reply = dbus_read_servers_ex(message, 1);
check_servers();
}
else if (strcmp(method, "ClearCache") == 0)

View File

@@ -366,7 +366,7 @@ static int join_multicast_worker(struct in6_addr *local, int prefix,
close(fd);
/* Are we doing DHCP on this interface? */
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
@@ -420,7 +420,7 @@ void bindtodevice(int fd)
SO_BINDTODEVICE is only available Linux. */
struct irec *iface, *found;
for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dhcp_ok)
{
@@ -435,14 +435,14 @@ void bindtodevice(int fd)
}
if (found)
{
struct ifreq ifr;
strcpy(ifr.ifr_name, found->name);
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
}
{
struct ifreq ifr;
strcpy(ifr.ifr_name, found->name);
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
}
}
#endif

View File

@@ -262,7 +262,7 @@ void dhcp_packet(time_t now, int pxe_fd)
parm.current = NULL;
parm.ind = iface_index;
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
{
/* If we failed to match the primary address of the interface, see if we've got a --listen-address
for a secondary */

View File

@@ -52,6 +52,7 @@
#define T_OPT 41
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
#define T_MAILB 253
#define T_ANY 255

View File

@@ -84,6 +84,7 @@ int main (int argc, char **argv)
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
#ifdef HAVE_DHCP
if (!daemon->lease_file)
{
@@ -150,6 +151,14 @@ int main (int argc, char **argv)
rand_init();
now = dnsmasq_time();
/* Create a serial at startup is not configured. */
if (daemon->authinterface && daemon->soa_sn == 0)
#ifdef HAVE_BROKEN_RTC
die(_("zone serial must be configured in --auth-soa"));
#else
daemon->soa_sn = now;
#endif
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->dhcp6)
@@ -1440,11 +1449,18 @@ static void check_dns_listeners(fd_set *set, time_t now)
struct server *s;
int flags;
struct in_addr netmask;
int auth_dns;
if (iface)
netmask = iface->netmask;
{
netmask = iface->netmask;
auth_dns = iface->dns_auth;
}
else
netmask.s_addr = 0;
{
netmask.s_addr = 0;
auth_dns = 0;
}
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
@@ -1463,7 +1479,7 @@ static void check_dns_listeners(fd_set *set, time_t now)
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
buff = tcp_request(confd, now, &tcp_addr, netmask);
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
shutdown(confd, SHUT_RDWR);
close(confd);

View File

@@ -278,6 +278,20 @@ struct cname {
struct cname *next;
};
struct auth_zone {
char *domain;
struct subnet {
int is6, prefixlen;
struct in_addr addr4;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
struct subnet *next;
} *subnet;
struct auth_zone *next;
};
struct host_record {
struct name_list {
char *name;
@@ -357,6 +371,8 @@ struct crec {
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
#define F_NOERR (1u<<20)
#define F_AUTH (1u<<21)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
@@ -412,7 +428,7 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, dhcp_ok, mtu, done, dad;
int tftp_ok, dhcp_ok, mtu, done, dad, dns_auth;
char *name;
struct irec *next;
};
@@ -733,17 +749,20 @@ extern struct daemon {
struct ptr_record *ptr;
struct host_record *host_records, *host_records_tail;
struct cname *cnames;
struct auth_zone *auth_zones;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
char *authserver, *authinterface, *hostmaster;
struct name_list *secondary_forward_server;
int group_set, osport;
char *domain_suffix;
struct cond_domain *cond_domain;
char *runfile;
char *lease_change_command;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers;
struct bogus_addr *bogus_addr;
struct server *servers;
int log_fac; /* log facility */
@@ -751,7 +770,7 @@ extern struct daemon {
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl;
unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6, *ra_contexts;
struct dhcp_config *dhcp_conf;
@@ -778,6 +797,7 @@ extern struct daemon {
unsigned int duid_enterprise, duid_config_len;
unsigned char *duid_config;
char *dbus_name;
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
@@ -835,7 +855,7 @@ extern struct daemon {
void cache_init(void);
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
char *record_source(int index);
void querystr(char *str, unsigned short type);
void querystr(char *desc, char *str, unsigned short type);
struct crec *cache_find_by_addr(struct crec *crecp,
struct all_addr *addr, time_t now,
unsigned short prot);
@@ -851,6 +871,7 @@ struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
struct crec *cache_enumerate(int init);
char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
@@ -879,6 +900,16 @@ unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
int add_resource_record(struct dns_header *header, char *limit, int *truncp,
int nameoffset, unsigned char **pp, unsigned long ttl,
int *offset, unsigned short type, unsigned short class, char *format, ...);
unsigned char *skip_questions(struct dns_header *header, size_t plen);
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes);
int in_arpa_name_2_addr(char *namein, struct all_addr *addrp);
/* auth.c */
size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now, union mysockaddr *peer_addr);
/* util.c */
void rand_init(void);
@@ -935,7 +966,7 @@ char *parse_server(char *arg, union mysockaddr *addr,
void reply_query(int fd, int family, time_t now);
void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask);
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
int send_from(int fd, int nowild, char *packet, size_t len,
@@ -953,7 +984,7 @@ int enumerate_interfaces();
void create_wildcard_listeners(void);
void create_bound_listeners(int die);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int fix_fd(int fd);
struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6

View File

@@ -95,26 +95,19 @@ int send_from(int fd, int nowild, char *packet, size_t len,
#endif
}
retry:
if (sendmsg(fd, &msg, 0) == -1)
while (sendmsg(fd, &msg, 0) == -1)
{
/* certain Linux kernels seem to object to setting the source address in the IPv6 stack
by returning EINVAL from sendmsg. In that case, try again without setting the
source address, since it will nearly alway be correct anyway. IPv6 stinks. */
if (errno == EINVAL && msg.msg_controllen)
{
msg.msg_controllen = 0;
goto retry;
}
if (retry_send())
goto retry;
continue;
/* If interface is still in DAD, EINVAL results - ignore that. */
if (errno == EINVAL)
break;
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
return 0;
}
return 1;
}
@@ -642,6 +635,7 @@ void receive_query(struct listener *listen, time_t now)
size_t m;
ssize_t n;
int if_index = 0;
int auth_dns = 0;
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmptr;
@@ -664,17 +658,20 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD))
dst_addr_4.s_addr = 0;
netmask.s_addr = 0;
if (listen->iface && option_bool(OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
auth_dns = listen->iface->dns_auth;
if (listen->family == AF_INET)
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
}
}
else
{
dst_addr_4.s_addr = 0;
netmask.s_addr = 0;
}
iov[0].iov_base = daemon->packet;
iov[0].iov_len = daemon->edns_pktsz;
@@ -767,7 +764,7 @@ void receive_query(struct listener *listen, time_t now)
/* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name))
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
return;
if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
@@ -803,7 +800,7 @@ void receive_query(struct listener *listen, time_t now)
{
char types[20];
querystr(types, type);
querystr(auth_dns ? "auth" : "query", types, type);
if (listen->family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
@@ -815,19 +812,30 @@ void receive_query(struct listener *listen, time_t now)
#endif
}
m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
dst_addr_4, netmask, now);
if (m >= 1)
if (auth_dns)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr);
if (m >= 1)
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL))
daemon->queries_forwarded++;
else
daemon->local_answer++;
{
m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
dst_addr_4, netmask, now);
if (m >= 1)
{
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL))
daemon->queries_forwarded++;
else
daemon->local_answer++;
}
}
/* The daemon forks before calling this: it should deal with one connection,
@@ -835,13 +843,14 @@ void receive_query(struct listener *listen, time_t now)
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
unsigned char *tcp_request(int confd, time_t now,
union mysockaddr *local_addr, struct in_addr netmask)
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
{
size_t size = 0;
int norebind = 0;
int checking_disabled;
size_t m;
unsigned short qtype, gotname;
unsigned short qtype;
unsigned int gotname;
unsigned char c1, c2;
/* Max TCP packet + slop */
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
@@ -877,7 +886,7 @@ unsigned char *tcp_request(int confd, time_t now,
{
char types[20];
querystr(types, qtype);
querystr(auth_dns ? "auth" : "query", types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
@@ -894,142 +903,148 @@ unsigned char *tcp_request(int confd, time_t now,
else
dst_addr_4.s_addr = 0;
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0)
if (auth_dns)
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);
else
{
unsigned int flags = 0;
struct all_addr *addrp = NULL;
int type = 0;
char *domain = NULL;
if (option_bool(OPT_ADD_MAC))
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now);
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
else
last_server = daemon->last_server;
if (!flags && last_server)
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0)
{
struct server *firstsendto = NULL;
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
while (1)
{
if (!firstsendto)
firstsendto = last_server;
else
{
if (!(last_server = last_server->next))
last_server = daemon->servers;
if (last_server == firstsendto)
break;
}
unsigned int flags = 0;
struct all_addr *addrp = NULL;
int type = 0;
char *domain = NULL;
/* server for wrong domain */
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
if (last_server->tcpfd == -1)
if (option_bool(OPT_ADD_MAC))
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
else
last_server = daemon->last_server;
if (!flags && last_server)
{
struct server *firstsendto = NULL;
unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
which can go to the same server, do so. */
while (1)
{
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
if (!firstsendto)
firstsendto = last_server;
else
{
if (!(last_server = last_server->next))
last_server = daemon->servers;
if (last_server == firstsendto)
break;
}
/* server for wrong domain */
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
if (last_server->tcpfd == -1)
{
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue;
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
c1 = size >> 8;
c2 = size;
if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
!read_write(last_server->tcpfd, &c2, 1, 0) ||
!read_write(last_server->tcpfd, packet, size, 0) ||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
!read_write(last_server->tcpfd, &c2, 1, 1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
c1 = size >> 8;
c2 = size;
if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
!read_write(last_server->tcpfd, &c2, 1, 0) ||
!read_write(last_server->tcpfd, packet, size, 0) ||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
!read_write(last_server->tcpfd, &c2, 1, 1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
m = (c1 << 8) | c2;
if (!read_write(last_server->tcpfd, packet, m, 1))
return packet;
if (!gotname)
strcpy(daemon->namebuff, "query");
if (last_server->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in.sin_addr, NULL);
}
m = (c1 << 8) | c2;
if (!read_write(last_server->tcpfd, packet, m, 1))
return packet;
if (!gotname)
strcpy(daemon->namebuff, "query");
if (last_server->addr.sa.sa_family == AF_INET)
log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in.sin_addr, NULL);
#ifdef HAVE_IPV6
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
else
log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
#endif
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
break;
/* There's no point in updating the cache, since this process will exit and
lose the information after a few queries. We make this call for the alias and
bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
break;
}
}
/* In case of local answer or no connections made. */
if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
}
/* In case of local answer or no connections made. */
if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
}
check_log_writer(NULL);
c1 = m>>8;
c2 = m;
if (!read_write(confd, &c1, 1, 0) ||
if (m == 0 ||
!read_write(confd, &c1, 1, 0) ||
!read_write(confd, &c2, 1, 0) ||
!read_write(confd, packet, m, 0))
return packet;

View File

@@ -420,6 +420,11 @@ void lease_update_dns(int force)
if (daemon->port != 0 && (dns_dirty || force))
{
#ifndef HAVE_BROKEN_RTC
/* force transfer to authoritative secondaries */
daemon->soa_sn++;
#endif
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)

View File

@@ -371,9 +371,9 @@ static int nl_async(struct nlmsghdr *h)
}
return 0;
}
#ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWADDR)
{
#ifdef HAVE_DHCP6
/* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts)
{
@@ -383,9 +383,9 @@ static int nl_async(struct nlmsghdr *h)
iface_enumerate and can't re-enter it now */
send_alarm(0, 0);
}
return 1; /* clever bind mode - rescan */
}
#endif
return 1; /* clever bind mode - rescan */
}
return 0;
}

View File

@@ -107,13 +107,24 @@ int indextoname(int fd, int index, char *name)
#endif
int iface_check(int family, struct all_addr *addr, char *name)
int iface_check(int family, struct all_addr *addr, char *name, int *auth)
{
struct iname *tmp;
int ret = 1;
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (auth)
{
if (daemon->authinterface && strcmp(daemon->authinterface, name) == 0)
{
*auth = 1;
return 1;
}
else
*auth = 0;
}
if (daemon->if_names || daemon->if_addrs)
{
@@ -153,6 +164,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
struct ifreq ifr;
int tftp_ok = !!option_bool(OPT_TFTP);
int dhcp_ok = 1;
int auth_dns = 0;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
@@ -210,25 +222,31 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns))
return 1;
#ifdef HAVE_DHCP
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
{
tftp_ok = 0;
dhcp_ok = 0;
}
#endif
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns))
return 1;
#endif
#ifdef HAVE_DHCP
/* No DHCP where we're doing auth DNS. */
if (auth_dns)
{
tftp_ok = 0;
dhcp_ok = 0;
}
else
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
{
tftp_ok = 0;
dhcp_ok = 0;
}
#endif
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
{
@@ -236,6 +254,7 @@ static int iface_allowed(struct irec **irecp, int if_index,
iface->netmask = netmask;
iface->tftp_ok = tftp_ok;
iface->dhcp_ok = dhcp_ok;
iface->dns_auth = auth_dns;
iface->mtu = mtu;
iface->dad = dad;
iface->done = 0;

View File

@@ -121,6 +121,12 @@ struct myoption {
#define LOPT_RR 310
#define LOPT_CLVERBIND 311
#define LOPT_MAXCTTL 312
#define LOPT_AUTHZONE 313
#define LOPT_AUTHSERV 314
#define LOPT_AUTHTTL 315
#define LOPT_AUTHSOA 316
#define LOPT_AUTHSFS 317
#define LOPT_AUTHPEER 318
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -247,6 +253,12 @@ static const struct myoption opts[] =
{ "dhcp-duid", 1, 0, LOPT_DUID },
{ "host-record", 1, 0, LOPT_HOST_REC },
{ "bind-dynamic", 0, 0, LOPT_CLVERBIND },
{ "auth-zone", 1, 0, LOPT_AUTHZONE },
{ "auth-server", 1, 0, LOPT_AUTHSERV },
{ "auth-ttl", 1, 0, LOPT_AUTHTTL },
{ "auth-soa", 1, 0, LOPT_AUTHSOA },
{ "auth-sec-servers", 1, 0, LOPT_AUTHSFS },
{ "auth-peer", 1, 0, LOPT_AUTHPEER },
{ NULL, 0, 0, 0 }
};
@@ -378,7 +390,13 @@ static struct {
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL},
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
{ LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
{ LOPT_AUTHZONE, ARG_DUP, "<domain>,<subnet>[,<subnet>]", gettext_noop("Domain to export to global DNS"), NULL },
{ LOPT_AUTHTTL, ARG_ONE, "<integer>", gettext_noop("Set TTL for authoritative replies"), NULL },
{ LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL },
{ LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL },
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -1513,7 +1531,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->next = daemon->dhcp_hosts_file;
daemon->dhcp_hosts_file = new;
}
else if (option == LOPT_DHCP_OPTS)
else if (option == LOPT_DHCP_OPTS)
{
new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new;
@@ -1521,6 +1539,114 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
case LOPT_AUTHSERV: /* --auth-server */
if (!(comma = split(arg)))
ret_err(gen_err);
daemon->authserver = opt_string_alloc(arg);
daemon->authinterface = opt_string_alloc(comma);
break;
case LOPT_AUTHSFS: /* --auth-sec-servers */
{
struct name_list *new;
do {
comma = split(arg);
new = safe_malloc(sizeof(struct name_list));
new->name = opt_string_alloc(arg);
new->next = daemon->secondary_forward_server;
daemon->secondary_forward_server = new;
arg = comma;
} while (arg);
break;
}
case LOPT_AUTHZONE: /* --auth-zone */
{
struct auth_zone *new;
comma = split(arg);
if (!comma)
ret_err(gen_err);
new = safe_malloc(sizeof(struct auth_zone));
new->domain = opt_string_alloc(arg);
new->subnet = NULL;
new->next = daemon->auth_zones;
daemon->auth_zones = new;
while ((arg = comma))
{
int prefixlen = 0;
char *prefix;
struct subnet *subnet = safe_malloc(sizeof(struct subnet));
subnet->next = new->subnet;
new->subnet = subnet;
comma = split(arg);
prefix = split_chr(arg, '/');
if (prefix && !atoi_check(prefix, &prefixlen))
ret_err(gen_err);
if (inet_pton(AF_INET, arg, &subnet->addr4))
{
if ((prefixlen & 0x07) != 0 || prefixlen > 24)
ret_err(_("bad prefix"));
subnet->prefixlen = (prefixlen == 0) ? 24 : prefixlen;
subnet->is6 = 0;
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &subnet->addr6))
{
subnet->prefixlen = (prefixlen == 0) ? 64 : prefixlen;
subnet->is6 = 1;
}
#endif
else
ret_err(gen_err);
}
break;
}
case LOPT_AUTHSOA: /* --auth-soa */
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_sn);
if (comma)
{
char *cp;
arg = comma;
comma = split(arg);
daemon->hostmaster = opt_string_alloc(arg);
for (cp = daemon->hostmaster; *cp; cp++)
if (*cp == '@')
*cp = '.';
if (comma)
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_refresh);
if (comma)
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_retry);
if (comma)
{
arg = comma;
comma = split(arg);
atoi_check(arg, (int *)&daemon->soa_expiry);
}
}
}
}
break;
case 's': /* --domain */
if (strcmp (arg, "#") == 0)
set_option_bool(OPT_RESOLV_DOMAIN);
@@ -1748,14 +1874,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
case 'a': /* --listen-address */
case LOPT_AUTHPEER: /* --auth-peer */
do {
struct iname *new = opt_malloc(sizeof(struct iname));
comma = split(arg);
unhide_metas(arg);
new->next = daemon->if_addrs;
if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
{
new->addr.sa.sa_family = AF_INET;
new->addr.in.sin_port = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
new->addr.in.sin_len = sizeof(new->addr.in);
#endif
@@ -1766,6 +1893,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new->addr.sa.sa_family = AF_INET6;
new->addr.in6.sin6_flowinfo = 0;
new->addr.in6.sin6_scope_id = 0;
new->addr.in6.sin6_port = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
new->addr.in6.sin6_len = sizeof(new->addr.in6);
#endif
@@ -1775,7 +1903,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
ret_err(gen_err);
new->used = 0;
daemon->if_addrs = new;
if (option == 'a')
{
new->next = daemon->if_addrs;
daemon->if_addrs = new;
}
else
{
new->next = daemon->auth_peers;
daemon->auth_peers = new;
}
arg = comma;
} while (arg);
break;
@@ -1933,6 +2070,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_NEGTTL: /* --neg-ttl */
case LOPT_MAXTTL: /* --max-ttl */
case LOPT_MAXCTTL: /* --max-cache-ttl */
case LOPT_AUTHTTL: /* --auth-ttl */
{
int ttl;
if (!atoi_check(arg, &ttl))
@@ -1943,6 +2081,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->max_ttl = (unsigned long)ttl;
else if (option == LOPT_MAXCTTL)
daemon->max_cache_ttl = (unsigned long)ttl;
else if (option == LOPT_AUTHTTL)
daemon->auth_ttl = (unsigned long)ttl;
else
daemon->local_ttl = (unsigned long)ttl;
break;
@@ -3613,6 +3753,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->tftp_max = TFTP_MAX_CONNECTIONS;
daemon->edns_pktsz = EDNS_PKTSZ;
daemon->log_fac = -1;
daemon->auth_ttl = AUTH_TTL;
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
add_txt("version.bind", "dnsmasq-" VERSION );
add_txt("authors.bind", "Simon Kelley");
add_txt("copyright.bind", COPYRIGHT);
@@ -3720,7 +3864,15 @@ void read_opts(int argc, char **argv, char *compile_opts)
tmp->addr.in6.sin6_port = htons(daemon->port);
#endif /* IPv6 */
}
/* create default, if not specified */
if (daemon->authserver && !daemon->hostmaster)
{
strcpy(buff, "hostmaster.");
strcat(buff, daemon->authserver);
daemon->hostmaster = opt_string_alloc(buff);
}
/* only one of these need be specified: the other defaults to the host-name */
if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
{

View File

@@ -149,7 +149,7 @@ void icmp6_packet(void)
if (!indextoname(daemon->icmp6fd, if_index, interface))
return;
if (!iface_check(AF_LOCAL, NULL, interface))
if (!iface_check(AF_LOCAL, NULL, interface, NULL))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
@@ -457,6 +457,7 @@ time_t periodic_ra(time_t now)
char interface[IF_NAMESIZE+1];
param.now = now;
param.iface = 0;
while (1)
{
@@ -482,13 +483,21 @@ time_t periodic_ra(time_t now)
ever be able to send ra's and satistfy it. */
if (iface_enumerate(AF_INET6, &param, iface_search))
context->ra_time = 0;
else if (indextoname(daemon->icmp6fd, param.iface, interface))
send_ra(param.iface, interface, NULL);
}
else if (param.iface != 0 &&
indextoname(daemon->icmp6fd, param.iface, interface) &&
iface_check(AF_LOCAL, NULL, interface, NULL))
{
struct iname *tmp;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, interface) == 0))
break;
if (!tmp)
send_ra(param.iface, interface, NULL);
}
}
return next_event;
}
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
@@ -505,9 +514,11 @@ static int iface_search(struct in6_addr *local, int prefix,
if (context->ra_time != 0 && difftime(context->ra_time, param->now) <= 0.0)
{
/* found an interface that's overdue for RA determine new
timeout value and zap other contexts on the same interface
so they don't timeout independently .*/
param->iface = if_index;
timeout value and arrange for RA to be sent unless interface is
still doing DAD.*/
if (!dad)
param->iface = if_index;
if (difftime(param->now, ra_short_period_start) < 60.0)
/* range 5 - 20 */
@@ -516,6 +527,14 @@ static int iface_search(struct in6_addr *local, int prefix,
/* range 450 - 600 */
context->ra_time = param->now + 450 + (rand16()/440);
/* zero timers for other contexts on the same subnet, so they don't timeout
independently */
for (context = context->next; context; context = context->next)
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
context->ra_time = 0;
return 0; /* found, abort */
}

View File

@@ -16,10 +16,6 @@
#include "dnsmasq.h"
static int add_resource_record(struct dns_header *header, char *limit, int *truncp,
unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type,
unsigned short class, char *format, ...);
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
@@ -27,8 +23,8 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
unsigned int j, l, hops = 0;
@@ -173,7 +169,7 @@ static int extract_name(struct dns_header *header, size_t plen, unsigned char **
/* Max size of input string (for IPv6) is 75 chars.) */
#define MAXARPANAME 75
static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
{
int j;
char name[MAXARPANAME+1], *cp1;
@@ -333,7 +329,7 @@ static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header,
return ansp;
}
static unsigned char *skip_questions(struct dns_header *header, size_t plen)
unsigned char *skip_questions(struct dns_header *header, size_t plen)
{
int q;
unsigned char *ansp = (unsigned char *)(header+1);
@@ -1189,8 +1185,8 @@ int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
return 0;
}
static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
int add_resource_record(struct dns_header *header, char *limit, int *truncp, int nameoffset, unsigned char **pp,
unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
unsigned char *sav, *p = *pp;
@@ -1201,8 +1197,26 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
if (truncp && *truncp)
return 0;
va_start(ap, format); /* make ap point to 1st unamed argument */
if (nameoffset > 0)
{
PUTSHORT(nameoffset | 0xc000, p);
}
else
{
char *name = va_arg(ap, char *);
if (name)
p = do_rfc1035_name(p, name);
if (nameoffset < 0)
{
PUTSHORT(-nameoffset | 0xc000, p);
}
else
*p++ = 0;
}
PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(type, p);
PUTSHORT(class, p);
PUTLONG(ttl, p); /* TTL */
@@ -1210,8 +1224,6 @@ static int add_resource_record(struct dns_header *header, char *limit, int *trun
sav = p; /* Save pointer to RDLength field */
PUTSHORT(0, p); /* Placeholder RDLength */
va_start(ap, format); /* make ap point to 1st unamed argument */
for (; *format; format++)
switch (*format)
{
@@ -1307,7 +1319,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
unsigned char *p, *ansp, *pheader;
int qtype, qclass;
struct all_addr addr;
unsigned int nameoffset;
int nameoffset;
unsigned short flag;
int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0;
@@ -1689,7 +1701,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
ans = found = 1;
if (!dryrun)
{
unsigned int offset;
int offset;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
@@ -1727,7 +1739,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
found = ans = 1;
if (!dryrun)
{
unsigned int offset;
int offset;
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_SRV, C_IN, "sssd",
@@ -1857,7 +1869,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
return ansp - (unsigned char *)header;
}

View File

@@ -1387,6 +1387,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (lease)
{
lease_set_interface(lease, int_index, now);
if (override.s_addr != 0)
lease->override = override;
else
@@ -1397,16 +1398,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
if (lease)
{
if (lease->expires == 0)
time = 0xffffffff;
else
time = (unsigned int)difftime(lease->expires, now);
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
lease_set_interface(lease, int_index, now);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);

View File

@@ -193,12 +193,12 @@ void tftp_request(struct listener *listen, time_t now)
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name))
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, NULL))
return;
}
else
#endif
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name))
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, NULL))
return;
#ifdef HAVE_DHCP