diff --git a/Makefile b/Makefile
index d524924..5cbc94b 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/bld/Android.mk b/bld/Android.mk
index 4a54b61..b93afaf 100644
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -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
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 0000000..cbc9c21
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,307 @@
+/* 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 .
+*/
+
+#include "dnsmasq.h"
+
+static int 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 = (1 << (32 - subnet->prefixlen)) - 1;
+
+ if (is_same_net(addr, subnet->addr4, mask))
+ return 1;
+ }
+#ifdef HAVE_IPV6
+ else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr6, subnet->prefixlen))
+ return 1;
+#endif
+
+ }
+ return 0;
+}
+
+
+
+size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t now)
+{
+ char *name = daemon->namebuff;
+ unsigned char *p, *ansp;
+ int qtype, qclass;
+ unsigned int nameoffset;
+ int q, anscount = 0, authcount = 0;
+ struct crec *crecp;
+ int auth = 1, trunc = 0, nxdomain = 1, soa = 0;
+ struct auth_zone *zone = NULL;
+
+ 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--)
+ {
+ size_t domainlen, namelen;
+ unsigned short flag;
+ 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)
+ {
+ struct all_addr addr;
+
+ if (!(flag = in_arpa_name_2_addr(name, &addr)))
+ continue;
+
+ for (zone = daemon->auth_zones; zone; zone = zone->next)
+ if (filter_zone(zone, flag, &addr))
+ break;
+
+ if (!zone)
+ {
+ auth = 0;
+ continue;
+ }
+
+ 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));
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->auth_ttl, NULL,
+ T_PTR, C_IN, "d", name))
+ {
+ anscount++;
+ found = 1;
+ }
+ }
+ else if (crecp->flags & (F_DHCP | F_HOSTS))
+ {
+ domainlen = strlen(zone->domain);
+ namelen = strlen(name);
+
+ if (namelen <= domainlen + 1 ||
+ name[namelen - domainlen - 1] != '.' ||
+ !hostname_isequal(zone->domain, &name[namelen - domainlen]))
+ continue; /* wrong domain */
+
+ log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid));
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->auth_ttl, NULL,
+ T_PTR, C_IN, "d", name))
+ {
+ anscount++;
+ found = 1;
+ }
+ }
+ 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;
+ }
+
+ namelen = strlen(name);
+
+ for (zone = daemon->auth_zones; zone; zone = zone->next)
+ {
+ domainlen = strlen(zone->domain);
+ if (namelen >= domainlen &&
+ hostname_isequal(zone->domain, &name[namelen - domainlen]))
+ break;
+ }
+
+ if (!zone)
+ {
+ auth = 0;
+ continue;
+ }
+
+ if (namelen > domainlen && name[namelen - domainlen - 1] != '.')
+ continue;
+
+ if (qtype == T_A)
+ flag = F_IPV4;
+#ifdef HAVE_IPV6
+ else if (qtype == T_AAAA)
+ flag = F_IPV6;
+#endif
+ else
+ {
+ if (qtype == T_SOA)
+ {
+ soa = 1; /* inhibits auth section */
+ found = 1;
+ log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "");
+ if (add_resource_record(header, limit, &trunc, 0, &ansp,
+ daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
+ zone->domain, daemon->authserver, daemon->hostmaster,
+ daemon->soa_sn, daemon->soa_refresh,
+ daemon->soa_retry, daemon->soa_expiry,
+ daemon->auth_ttl))
+ anscount++;
+ }
+ else if (qtype == T_NS)
+ {
+ soa = 1; /* inhibits auth section */
+ found = 1;
+ log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "");
+ if (add_resource_record(header, limit, &trunc, 0, &ansp,
+ daemon->auth_ttl, NULL, T_NS, C_IN, "d", zone->domain, daemon->authserver))
+ anscount++;
+ }
+
+ continue;
+ }
+
+ if (!option_bool(OPT_DHCP_FQDN) && namelen > domainlen + 1)
+ {
+ name[namelen - domainlen - 1] = 0; /* remove domain part */
+
+ if ((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)))
+ {
+ name[namelen - domainlen - 1] = '.'; /* restore domain part */
+ log_query(crecp->flags, name, &crecp->addr.addr, record_source(crecp->uid));
+ name[namelen - domainlen - 1] = 0; /* remove domain part */
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->auth_ttl, NULL, qtype, C_IN,
+ qtype == T_A ? "4" : "6", &crecp->addr))
+ {
+ anscount++;
+ found = 1;
+ }
+ }
+ } while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4 | F_IPV6)));
+ }
+
+ name[namelen - domainlen - 1] = '.'; /* restore domain part */
+ }
+
+ if (!found && (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));
+ if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
+ daemon->auth_ttl, NULL, qtype, C_IN,
+ qtype == T_A ? "4" : "6", &crecp->addr))
+ {
+ anscount++;
+ found = 1;
+ }
+ }
+ } 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 && !soa)
+ {
+ if (anscount != 0 && add_resource_record(header, limit, &trunc, 0, &ansp,
+ daemon->auth_ttl, NULL, T_NS, C_IN, "d", zone->domain, daemon->authserver))
+ authcount++;
+
+ if (anscount == 0 && add_resource_record(header, limit, &trunc, 0, &ansp,
+ daemon->auth_ttl, NULL, T_SOA, C_IN, "ddlllll",
+ zone->domain, daemon->authserver, daemon->hostmaster,
+ daemon->soa_sn, daemon->soa_refresh,
+ daemon->soa_retry, daemon->soa_expiry,
+ daemon->auth_ttl))
+ authcount++;
+ }
+
+ /* 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);
+ return ansp - (unsigned char *)header;
+}
+
+
+
+
+
diff --git a/src/cache.c b/src/cache.c
index ddbc5c0..8bf0c55 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -1248,14 +1248,14 @@ char *record_source(int index)
return "";
}
-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 +1316,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";
diff --git a/src/config.h b/src/config.h
index 1a08e95..31fb1cc 100644
--- a/src/config.h
+++ b/src/config.h
@@ -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
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index 75076d4..30aad56 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -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)
diff --git a/src/dhcp.c b/src/dhcp.c
index 75a4427..3410020 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -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 */
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 0023a4f..6e3a7c2 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -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);
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index c715e58..91c71c4 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -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,11 +749,13 @@ 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;
int group_set, osport;
char *domain_suffix;
struct cond_domain *cond_domain;
@@ -751,7 +769,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 +796,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 +854,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);
@@ -879,6 +898,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,
+ unsigned int nameoffset, unsigned char **pp, unsigned long ttl,
+ unsigned 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);
/* util.c */
void rand_init(void);
@@ -935,7 +964,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 +982,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
diff --git a/src/forward.c b/src/forward.c
index f672194..603dbd9 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -642,6 +642,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 +665,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 +771,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 +807,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 +819,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);
+ 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,7 +850,7 @@ 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;
@@ -877,7 +892,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,137 +909,142 @@ 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);
+ 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;
diff --git a/src/network.c b/src/network.c
index fda50bd..0235cec 100644
--- a/src/network.c
+++ b/src/network.c
@@ -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;
diff --git a/src/option.c b/src/option.c
index 714f2c2..0cad369 100644
--- a/src/option.c
+++ b/src/option.c
@@ -121,6 +121,10 @@ 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
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -247,6 +251,10 @@ 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 },
{ NULL, 0, 0, 0 }
};
@@ -378,7 +386,11 @@ static struct {
{ LOPT_DUID, ARG_ONE, ",", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, ",", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_RR, ARG_DUP, ",,[]", 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, ",", gettext_noop("Export local names to global DNS"), NULL },
+ { LOPT_AUTHZONE, ARG_DUP, ",[,]", gettext_noop("Domain to export to global DNS"), NULL },
+ { LOPT_AUTHTTL, ARG_ONE, "", gettext_noop("Set TTL for authoritative replies"), NULL },
+ { LOPT_AUTHSOA, ARG_ONE, "[,...]", gettext_noop("Set authoritive zone information"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -1521,6 +1533,95 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
}
+ case LOPT_AUTHSERV: /* --auth-server */
+ comma = split(arg);
+ if (!comma)
+ ret_err(gen_err);
+
+ daemon->authinterface = opt_string_alloc(arg);
+ arg = comma;
+ comma = split(arg);
+ daemon->authserver = opt_string_alloc(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))
+ {
+ 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)
+ {
+ arg = comma;
+ comma = split(arg);
+ daemon->hostmaster = opt_string_alloc(arg);
+ 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);
@@ -1933,6 +2034,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 +2045,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 +3717,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 +3828,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)
{
diff --git a/src/radv.c b/src/radv.c
index d1f5268..6fa4b35 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -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)
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 67325ca..5896a93 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -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, unsigned int nameoffset, unsigned char **pp,
+ unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
unsigned char *sav, *p = *pp;
@@ -1201,8 +1197,19 @@ 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
+ {
+ p = do_rfc1035_name(p, va_arg(ap, char *));
+ *p++ = 0;
+ }
- PUTSHORT(nameoffset | 0xc000, p);
PUTSHORT(type, p);
PUTSHORT(class, p);
PUTLONG(ttl, p); /* TTL */
@@ -1210,8 +1217,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)
{
@@ -1857,7 +1862,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
return ansp - (unsigned char *)header;
}
-
-
-
-
diff --git a/src/tftp.c b/src/tftp.c
index 82bbccc..4ce10b3 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -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