mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8707019237 | ||
|
|
d1fbb77e0f | ||
|
|
1fbe4d2f5f | ||
|
|
0575610fa1 | ||
|
|
e3f1455850 | ||
|
|
bd9b3cf55b | ||
|
|
14db4212ab | ||
|
|
00a5b5d477 | ||
|
|
b8eac19177 | ||
|
|
b47b04c846 | ||
|
|
613ad15d02 | ||
|
|
24187530fb | ||
|
|
a857daa351 | ||
|
|
f01d7be6c6 | ||
|
|
d387380a25 | ||
|
|
f2e4c277c4 | ||
|
|
5107ace14a | ||
|
|
7b1eae4f50 | ||
|
|
c152dc8492 | ||
|
|
7bcca0060f | ||
|
|
d68c2ca2b7 | ||
|
|
de73a497ca | ||
|
|
e3ec15af10 | ||
|
|
dac74312da | ||
|
|
2ecd9bd5c0 | ||
|
|
a0ab18f6eb | ||
|
|
ebe95a831f |
No files matched your search
@@ -62,7 +62,12 @@ version 2.69
|
||||
an invaluable period of full-time work to get it to
|
||||
a workable state.
|
||||
|
||||
Add --rev-server. Thanks to Dave Taht for suggesting this.
|
||||
|
||||
Add --servers-file. Allows dynamic update of upstream servers
|
||||
full access to configuration.
|
||||
|
||||
|
||||
version 2.68
|
||||
Use random addresses for DHCPv6 temporary address
|
||||
allocations, instead of algorithmically determined stable
|
||||
|
||||
Vendored
+4
@@ -19,6 +19,10 @@ LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
|
||||
|
||||
DEB_COPTS = $(COPTS)
|
||||
|
||||
# The nettle library in Debian is too old to include
|
||||
# ECC support.
|
||||
DEB_COPTS += -DNO_NETTLE_ECC
|
||||
|
||||
TARGET = install-i18n
|
||||
|
||||
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
|
||||
|
||||
@@ -415,6 +415,14 @@ source address specified but the port may be specified directly as
|
||||
part of the source address. Forcing queries to an interface is not
|
||||
implemented on all platforms supported by dnsmasq.
|
||||
.TP
|
||||
.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
|
||||
This is functionally the same as
|
||||
.B --server,
|
||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||
.B --rev-server=1.2.3.0/24,192.168.0.1
|
||||
is exactly equivalent to
|
||||
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
|
||||
.TP
|
||||
.B \-A, --address=/<domain>/[domain/]<ipaddr>
|
||||
Specify an IP address to return for any host in the given domains.
|
||||
Queries in the domains are never forwarded and always replied to
|
||||
@@ -600,6 +608,15 @@ key(s) of the root zone,
|
||||
but trust anchors for limited domains are also possible. The current
|
||||
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||
.TP
|
||||
.B --dnssec-check-unsigned
|
||||
As a default, dnsmasq does not check that unsigned DNS replies are
|
||||
legitimate: they are assumed to be valid and passed on (without the
|
||||
"authentic data" bit set, of course). This does not protect against an
|
||||
attacker forging unsigned replies for signed DNS zones, but it is
|
||||
fast. If this flag is set, dnsmasq will check the zones of unsigned
|
||||
replies, to ensure that unsigned replies are allowed in those
|
||||
zones. The cost of this is more upstream queries and slower performance.
|
||||
.TP
|
||||
.B --proxy-dnssec
|
||||
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
|
||||
alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
|
||||
@@ -1670,6 +1687,13 @@ files. If extension(s) are given, any files which end in those
|
||||
extensions are skipped. Any files whose names end in ~ or start with . or start and end
|
||||
with # are always skipped. This flag may be given on the command
|
||||
line or in a configuration file.
|
||||
.TP
|
||||
.B --servers-file=<file>
|
||||
A special case of
|
||||
.B --conf-file
|
||||
which differs in two respects. Firstly, only --server and --rev-server are allowed
|
||||
in the configuration file included. Secondly, the file is re-read and the configuration
|
||||
therein is updated when dnsmasq recieves SIGHUP.
|
||||
.SH CONFIG FILE
|
||||
At startup, dnsmasq reads
|
||||
.I /etc/dnsmasq.conf,
|
||||
|
||||
+24
-24
@@ -183,7 +183,7 @@ static void cache_blockdata_free(struct crec *crecp)
|
||||
else
|
||||
blockdata_free(crecp->addr.key.keydata);
|
||||
}
|
||||
else if (crecp->flags & F_DS)
|
||||
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
|
||||
blockdata_free(crecp->addr.ds.keydata);
|
||||
}
|
||||
#endif
|
||||
@@ -448,12 +448,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
int freed_all = flags & F_REVERSE;
|
||||
int free_avail = 0;
|
||||
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
|
||||
/* Don't log keys here, done elsewhere */
|
||||
/* Don't log DNSSEC records here, done elsewhere */
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
{
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
/* Don;t mess with TTL for DNSSEC records. */
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
}
|
||||
|
||||
/* if previous insertion failed give up now. */
|
||||
if (insert_error)
|
||||
@@ -562,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
*cache_get_name(new) = 0;
|
||||
|
||||
if (addr)
|
||||
new->addr.addr = *addr;
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & (F_DS | F_DNSKEY))
|
||||
new->uid = addr->addr.dnssec.class;
|
||||
else
|
||||
#endif
|
||||
new->addr.addr = *addr;
|
||||
}
|
||||
|
||||
new->ttd = now + (time_t)ttl;
|
||||
new->next = new_chain;
|
||||
@@ -1302,25 +1311,16 @@ void dump_cache(time_t now)
|
||||
else if (cache->flags & F_DS)
|
||||
{
|
||||
if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
/* RRSIG */
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
|
||||
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
|
||||
}
|
||||
else
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
|
||||
cache->addr.ds.algo, cache->addr.ds.digest);
|
||||
}
|
||||
/* RRSIG */
|
||||
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
|
||||
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
|
||||
else if (!(cache->flags & F_NEG))
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
|
||||
cache->addr.ds.algo, cache->addr.ds.digest);
|
||||
}
|
||||
else if (cache->flags & F_DNSKEY)
|
||||
{
|
||||
a = daemon->addrbuff;
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
||||
cache->addr.key.algo, cache->addr.key.flags);
|
||||
}
|
||||
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
|
||||
cache->addr.key.algo, cache->addr.key.flags);
|
||||
#endif
|
||||
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
|
||||
{
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@
|
||||
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
|
||||
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
|
||||
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
|
||||
#define SMALLDNAME 40 /* most domain names are smaller than this */
|
||||
#define SMALLDNAME 50 /* most domain names are smaller than this */
|
||||
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
|
||||
#define HOSTSFILE "/etc/hosts"
|
||||
#define ETHERSFILE "/etc/ethers"
|
||||
#define DEFLEASE 3600 /* default lease time, 1 hour */
|
||||
|
||||
+17
-115
@@ -108,108 +108,6 @@ static void remove_watch(DBusWatch *watch, void *data)
|
||||
w = data; /* no warning */
|
||||
}
|
||||
|
||||
static void add_update_server(union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if ((serv->flags & SERV_FROM_DBUS) &&
|
||||
(serv->flags & SERV_MARK))
|
||||
{
|
||||
if (domain)
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
}
|
||||
|
||||
serv->flags &= ~SERV_MARK;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!serv && (serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
|
||||
if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
serv->flags = SERV_FROM_DBUS;
|
||||
if (domain)
|
||||
{
|
||||
strcpy(serv->domain, domain);
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
if (interface)
|
||||
strcpy(serv->interface, interface);
|
||||
else
|
||||
serv->interface[0] = 0;
|
||||
|
||||
if (source_addr->in.sin_family == AF_INET &&
|
||||
addr->in.sin_addr.s_addr == 0 &&
|
||||
serv->domain)
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
else
|
||||
{
|
||||
serv->flags &= ~SERV_NO_ADDR;
|
||||
serv->addr = *addr;
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mark_dbus(void)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything from DBUS */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_FROM_DBUS)
|
||||
serv->flags |= SERV_MARK;
|
||||
}
|
||||
|
||||
static void cleanup_dbus()
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
if (serv->domain)
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void dbus_read_servers(DBusMessage *message)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
@@ -218,8 +116,8 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
|
||||
dbus_message_iter_init(message, &iter);
|
||||
|
||||
mark_dbus();
|
||||
|
||||
mark_servers(SERV_FROM_DBUS);
|
||||
|
||||
while (1)
|
||||
{
|
||||
int skip = 0;
|
||||
@@ -289,13 +187,13 @@ static void dbus_read_servers(DBusMessage *message)
|
||||
domain = NULL;
|
||||
|
||||
if (!skip)
|
||||
add_update_server(&addr, &source_addr, NULL, domain);
|
||||
add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
|
||||
|
||||
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
cleanup_dbus();
|
||||
cleanup_servers();
|
||||
}
|
||||
|
||||
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
@@ -319,7 +217,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
strings ? "Expected array of string" : "Expected array of string arrays");
|
||||
}
|
||||
|
||||
mark_dbus();
|
||||
mark_servers(SERV_FROM_DBUS);
|
||||
|
||||
/* array_iter points to each "as" element in the outer array */
|
||||
dbus_message_iter_recurse(&iter, &array_iter);
|
||||
@@ -327,6 +225,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
{
|
||||
const char *str = NULL;
|
||||
union mysockaddr addr, source_addr;
|
||||
int flags = 0;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
|
||||
@@ -416,16 +315,19 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
memset(&interface, 0, sizeof(interface));
|
||||
|
||||
/* parse the IP address */
|
||||
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL);
|
||||
|
||||
if (addr_err)
|
||||
{
|
||||
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* 0.0.0.0 for server address == NULL, for Dbus */
|
||||
if (addr.in.sin_family == AF_INET &&
|
||||
addr.in.sin_addr.s_addr == 0)
|
||||
flags |= SERV_NO_ADDR;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
char *p;
|
||||
@@ -439,7 +341,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(&addr, &source_addr, interface, str_domain);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
@@ -454,7 +356,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
add_update_server(&addr, &source_addr, interface, str);
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
@@ -462,7 +364,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
dbus_message_iter_next(&array_iter);
|
||||
}
|
||||
|
||||
cleanup_dbus();
|
||||
cleanup_servers();
|
||||
|
||||
if (dup)
|
||||
free(dup);
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#define T_RRSIG 46
|
||||
#define T_NSEC 47
|
||||
#define T_DNSKEY 48
|
||||
#define T_NSEC3 50
|
||||
#define T_TKEY 249
|
||||
#define T_TSIG 250
|
||||
#define T_AXFR 252
|
||||
|
||||
+22
-5
@@ -98,7 +98,10 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
daemon->keyname = safe_malloc(MAXDNAME);
|
||||
{
|
||||
daemon->keyname = safe_malloc(MAXDNAME);
|
||||
daemon->workspacename = safe_malloc(MAXDNAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
@@ -1114,7 +1117,7 @@ static void async_event(int pipe, time_t now)
|
||||
{
|
||||
pid_t p;
|
||||
struct event_desc ev;
|
||||
int i;
|
||||
int i, check = 0;
|
||||
char *msg;
|
||||
|
||||
/* NOTE: the memory used to return msg is leaked: use msgs in events only
|
||||
@@ -1125,11 +1128,25 @@ static void async_event(int pipe, time_t now)
|
||||
{
|
||||
case EVENT_RELOAD:
|
||||
clear_cache_and_reload(now);
|
||||
if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
|
||||
|
||||
if (daemon->port != 0)
|
||||
{
|
||||
reload_servers(daemon->resolv_files->name);
|
||||
check_servers();
|
||||
if (daemon->resolv_files && option_bool(OPT_NO_POLL))
|
||||
{
|
||||
reload_servers(daemon->resolv_files->name);
|
||||
check = 1;
|
||||
}
|
||||
|
||||
if (daemon->servers_file)
|
||||
{
|
||||
read_servers_file();
|
||||
check = 1;
|
||||
}
|
||||
|
||||
if (check)
|
||||
check_servers();
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
rerun_scripts();
|
||||
#endif
|
||||
|
||||
+26
-3
@@ -232,7 +232,8 @@ struct event_desc {
|
||||
#define OPT_DNSSEC_VALID 45
|
||||
#define OPT_DNSSEC_PERMISS 46
|
||||
#define OPT_DNSSEC_DEBUG 47
|
||||
#define OPT_LAST 48
|
||||
#define OPT_DNSSEC_NO_SIGN 48
|
||||
#define OPT_LAST 49
|
||||
|
||||
/* extra flags for my_syslog, we use a couple of facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
|
||||
@@ -456,6 +457,7 @@ union mysockaddr {
|
||||
#define SERV_COUNTED 512 /* workspace for log code */
|
||||
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
|
||||
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
|
||||
#define SERV_FROM_FILE 4096 /* read from --servers-file */
|
||||
|
||||
struct serverfd {
|
||||
int fd;
|
||||
@@ -533,6 +535,11 @@ struct hostsfile {
|
||||
#define STAT_NEED_DS 4
|
||||
#define STAT_NEED_KEY 5
|
||||
#define STAT_TRUNCATED 6
|
||||
#define STAT_SECURE_WILDCARD 7
|
||||
#define STAT_NO_SIG 8
|
||||
#define STAT_NO_DS 9
|
||||
#define STAT_NEED_DS_NEG 10
|
||||
#define STAT_CHASE_CNAME 11
|
||||
|
||||
#define FREC_NOREBIND 1
|
||||
#define FREC_CHECKING_DISABLED 2
|
||||
@@ -540,6 +547,9 @@ struct hostsfile {
|
||||
#define FREC_DNSKEY_QUERY 8
|
||||
#define FREC_DS_QUERY 16
|
||||
#define FREC_AD_QUESTION 32
|
||||
#define FREC_DO_QUESTION 64
|
||||
#define FREC_ADDED_PHEADER 128
|
||||
#define FREC_CHECK_NOSIGN 256
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
#define HASH_SIZE 20 /* SHA-1 digest size */
|
||||
@@ -864,6 +874,7 @@ extern struct daemon {
|
||||
unsigned int options, options2;
|
||||
struct resolvc default_resolv, *resolv_files;
|
||||
time_t last_resolv;
|
||||
char *servers_file;
|
||||
struct mx_srv_record *mxnames;
|
||||
struct naptr *naptr;
|
||||
struct txt_record *txt, *rr;
|
||||
@@ -939,6 +950,7 @@ extern struct daemon {
|
||||
char *namebuff; /* MAXDNAME size buffer */
|
||||
#ifdef HAVE_DNSSEC
|
||||
char *keyname; /* MAXDNAME size buffer */
|
||||
char *workspacename; /* ditto */
|
||||
#endif
|
||||
unsigned int local_answer, queries_forwarded, auth_answer;
|
||||
struct frec *frec_list;
|
||||
@@ -1033,6 +1045,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
|
||||
char *name, int isExtract, int extrabytes);
|
||||
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
|
||||
unsigned char *skip_questions(struct dns_header *header, size_t plen);
|
||||
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
|
||||
unsigned int extract_request(struct dns_header *header, size_t qlen,
|
||||
char *name, unsigned short *typep);
|
||||
size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
@@ -1043,7 +1056,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
|
||||
int no_cache, int secure, int *doctored);
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int *ad_reqd);
|
||||
time_t now, int *ad_reqd, int *do_bit);
|
||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||
struct bogus_addr *addr, time_t now);
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
@@ -1078,8 +1091,10 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
|
||||
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
|
||||
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
|
||||
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
|
||||
size_t filter_rrsigs(struct dns_header *header, size_t plen);
|
||||
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
|
||||
|
||||
/* util.c */
|
||||
@@ -1130,6 +1145,7 @@ void read_opts (int argc, char **argv, char *compile_opts);
|
||||
char *option_string(int prot, unsigned int opt, unsigned char *val,
|
||||
int opt_len, char *buf, int buf_len);
|
||||
void reread_dhcp(void);
|
||||
void read_servers_file(void);
|
||||
void set_option_bool(unsigned int opt);
|
||||
void reset_option_bool(unsigned int opt);
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list);
|
||||
@@ -1153,6 +1169,13 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
|
||||
int random_sock(int family);
|
||||
void pre_allocate_sfds(void);
|
||||
int reload_servers(char *fname);
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain);
|
||||
void check_servers(void);
|
||||
int enumerate_interfaces(int reset);
|
||||
void create_wildcard_listeners(void);
|
||||
|
||||
+1105
-264
File diff suppressed because it is too large.
Load diff
+300
-37
@@ -24,6 +24,14 @@ static unsigned short get_id(void);
|
||||
static void free_frec(struct frec *f);
|
||||
static struct randfd *allocate_rfd(int family);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
||||
int class, char *name, char *keyname, struct server *server, int *keycount);
|
||||
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
|
||||
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
|
||||
#endif
|
||||
|
||||
|
||||
/* Send a UDP packet with its source address set as "source"
|
||||
unless nowild is true, when we just send it with the kernel default */
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
@@ -235,7 +243,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
|
||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
struct all_addr *dst_addr, unsigned int dst_iface,
|
||||
struct dns_header *header, size_t plen, time_t now,
|
||||
struct frec *forward, int ad_reqd)
|
||||
struct frec *forward, int ad_reqd, int do_bit)
|
||||
{
|
||||
char *domain = NULL;
|
||||
int type = 0, norebind = 0;
|
||||
@@ -250,13 +258,15 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#endif
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
|
||||
(void)do_bit;
|
||||
|
||||
/* may be no servers available. */
|
||||
if (!daemon->servers)
|
||||
forward = NULL;
|
||||
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* If we've already got an answer to this query, but we're awaiting keys for vaildation,
|
||||
/* If we've already got an answer to this query, but we're awaiting keys for validation,
|
||||
there's no point retrying the query, retry the key query instead...... */
|
||||
if (forward->blocking_query)
|
||||
{
|
||||
@@ -336,8 +346,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forward->flags |= FREC_AD_QUESTION;
|
||||
#ifdef HAVE_DNSSEC
|
||||
forward->work_counter = DNSSEC_WORK;
|
||||
if (do_bit)
|
||||
forward->flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
|
||||
|
||||
header->id = htons(forward->new_id);
|
||||
|
||||
/* In strict_order mode, always try servers in the order
|
||||
@@ -393,11 +405,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
|
||||
size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
if (option_bool(OPT_DNSSEC_DEBUG))
|
||||
header->hb4 |= HB4_CD;
|
||||
|
||||
if (new_plen != plen)
|
||||
forward->flags |= FREC_ADDED_PHEADER;
|
||||
|
||||
plen = new_plen;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -506,7 +524,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
|
||||
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
||||
int no_cache, int cache_secure, int ad_reqd, int check_subnet, union mysockaddr *query_source)
|
||||
int no_cache, int cache_secure, int ad_reqd, int do_bit, int added_pheader, int check_subnet, union mysockaddr *query_source)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
char **sets = 0;
|
||||
@@ -514,6 +532,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
size_t plen;
|
||||
|
||||
(void)ad_reqd;
|
||||
(void) do_bit;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
/* Similar algorithm to search_servers. */
|
||||
@@ -553,6 +572,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (added_pheader)
|
||||
{
|
||||
pheader = 0;
|
||||
header->arcount = htons(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC 4035 sect 4.6 para 3 */
|
||||
@@ -624,6 +649,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
|
||||
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
|
||||
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
|
||||
if (!do_bit)
|
||||
n = filter_rrsigs(header, n);
|
||||
#endif
|
||||
|
||||
/* do this after extract_addresses. Ensure NODATA reply and remove
|
||||
@@ -708,7 +737,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
|
||||
{
|
||||
header->hb3 &= ~(HB3_QR | HB3_TC);
|
||||
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
|
||||
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -775,13 +804,27 @@ void reply_query(int fd, int family, time_t now)
|
||||
else if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
{
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
if (status == STAT_NO_DS)
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
else if (forward->flags & FREC_CHECK_NOSIGN)
|
||||
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
{
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
|
||||
if (status == STAT_NO_SIG)
|
||||
{
|
||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
||||
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
|
||||
else
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
/* Can't validate, as we're missing key data. Put this
|
||||
answer aside, whilst we get that. */
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
|
||||
{
|
||||
struct frec *new, *orig;
|
||||
|
||||
@@ -811,7 +854,7 @@ void reply_query(int fd, int family, time_t now)
|
||||
#ifdef HAVE_IPV6
|
||||
new->rfd6 = NULL;
|
||||
#endif
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
|
||||
|
||||
new->dependent = forward; /* to find query awaiting new one. */
|
||||
forward->blocking_query = new; /* for garbage cleaning */
|
||||
@@ -824,7 +867,10 @@ void reply_query(int fd, int family, time_t now)
|
||||
}
|
||||
else
|
||||
{
|
||||
new->flags |= FREC_DS_QUERY;
|
||||
if (status == STAT_NEED_DS_NEG)
|
||||
new->flags |= FREC_CHECK_NOSIGN;
|
||||
else
|
||||
new->flags |= FREC_DS_QUERY;
|
||||
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||
daemon->keyname, forward->class, T_DS, &server->addr);
|
||||
}
|
||||
@@ -888,11 +934,26 @@ void reply_query(int fd, int family, time_t now)
|
||||
if (forward->flags & FREC_DNSKEY_QUERY)
|
||||
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else if (forward->flags & FREC_DS_QUERY)
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
{
|
||||
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
if (status == STAT_NO_DS)
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
else if (forward->flags & FREC_CHECK_NOSIGN)
|
||||
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
|
||||
else
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
|
||||
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||
{
|
||||
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
|
||||
if (status == STAT_NO_SIG)
|
||||
{
|
||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
||||
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
|
||||
else
|
||||
status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
|
||||
goto anotherkey;
|
||||
}
|
||||
}
|
||||
@@ -927,7 +988,8 @@ void reply_query(int fd, int family, time_t now)
|
||||
header->hb4 &= ~HB4_CD;
|
||||
|
||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
||||
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||
{
|
||||
header->id = htons(forward->orig_id);
|
||||
header->hb4 |= HB4_RA; /* recursion if available */
|
||||
@@ -1169,9 +1231,9 @@ void receive_query(struct listener *listen, time_t now)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int ad_reqd;
|
||||
int ad_reqd, do_bit;
|
||||
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
|
||||
dst_addr_4, netmask, now, &ad_reqd);
|
||||
dst_addr_4, netmask, now, &ad_reqd, &do_bit);
|
||||
|
||||
if (m >= 1)
|
||||
{
|
||||
@@ -1180,7 +1242,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
daemon->local_answer++;
|
||||
}
|
||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, (size_t)n, now, NULL, ad_reqd))
|
||||
header, (size_t)n, now, NULL, ad_reqd, do_bit))
|
||||
daemon->queries_forwarded++;
|
||||
else
|
||||
daemon->local_answer++;
|
||||
@@ -1188,6 +1250,164 @@ void receive_query(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
|
||||
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
|
||||
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
|
||||
STAT_NEED_DS_NEG and keyname if we need to do the query. */
|
||||
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
|
||||
{
|
||||
struct crec *crecp;
|
||||
char *name_start = name;
|
||||
int status = dnssec_chase_cname(now, header, plen, name, keyname);
|
||||
|
||||
if (status != STAT_INSECURE)
|
||||
return status;
|
||||
|
||||
while (1)
|
||||
{
|
||||
crecp = cache_find_by_name(NULL, name_start, now, F_DS);
|
||||
|
||||
if (crecp && (crecp->flags & F_DNSSECOK))
|
||||
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
|
||||
|
||||
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
|
||||
{
|
||||
name_start++; /* chop a label off and try again */
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(keyname, name_start);
|
||||
return STAT_NEED_DS_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
|
||||
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||
|
||||
{
|
||||
char *name_start;
|
||||
unsigned char *p;
|
||||
int status = dnssec_validate_ds(now, header, plen, name, keyname, class);
|
||||
|
||||
if (status != STAT_INSECURE)
|
||||
{
|
||||
if (status == STAT_NO_DS)
|
||||
status = STAT_INSECURE;
|
||||
return status;
|
||||
}
|
||||
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
if (extract_name(header, plen, &p, name, 1, 4) &&
|
||||
(name_start = strchr(name, '.')))
|
||||
{
|
||||
name_start++; /* chop a label off and try again */
|
||||
strcpy(keyname, name_start);
|
||||
return STAT_NEED_DS_NEG;
|
||||
}
|
||||
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
|
||||
/* Move toward the root, until we find a signed non-existance of a DS, in which case
|
||||
an unsigned answer is OK, or we find a signed DS, in which case there should be
|
||||
a signature, and the answer is BOGUS */
|
||||
static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
|
||||
char *keyname, struct server *server, int *keycount)
|
||||
{
|
||||
size_t m;
|
||||
unsigned char *packet, *payload;
|
||||
u16 *length;
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int status;
|
||||
char *name_start = name;
|
||||
|
||||
/* Get first insecure entry in CNAME chain */
|
||||
status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
|
||||
if (status == STAT_BOGUS)
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
|
||||
return STAT_BOGUS;
|
||||
|
||||
payload = &packet[2];
|
||||
header = (struct dns_header *)payload;
|
||||
length = (u16 *)packet;
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned char *newhash, hash[HASH_SIZE];
|
||||
unsigned char c1, c2;
|
||||
struct crec *crecp = cache_find_by_name(NULL, name_start, now, F_DS);
|
||||
|
||||
if (--(*keycount) == 0)
|
||||
return STAT_BOGUS;
|
||||
|
||||
if (crecp && (crecp->flags & F_DNSSECOK))
|
||||
{
|
||||
free(packet);
|
||||
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
|
||||
}
|
||||
|
||||
/* If we have cached insecurely that a DS doesn't exist,
|
||||
ise that is a hit for where to start looking for the secure one */
|
||||
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
|
||||
{
|
||||
name_start++; /* chop a label off and try again */
|
||||
continue;
|
||||
}
|
||||
|
||||
m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr);
|
||||
|
||||
/* We rely on the question section coming back unchanged, ensure it is with the hash. */
|
||||
if ((newhash = hash_questions(header, (unsigned int)m, name)))
|
||||
memcpy(hash, newhash, HASH_SIZE);
|
||||
|
||||
*length = htons(m);
|
||||
|
||||
if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
|
||||
read_write(server->tcpfd, &c1, 1, 1) &&
|
||||
read_write(server->tcpfd, &c2, 1, 1) &&
|
||||
read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
||||
{
|
||||
m = (c1 << 8) | c2;
|
||||
|
||||
newhash = hash_questions(header, (unsigned int)m, name);
|
||||
if (newhash && memcmp(hash, newhash, HASH_SIZE) == 0)
|
||||
{
|
||||
/* Note this trashes all three name workspaces */
|
||||
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
|
||||
|
||||
/* We've found a DS which proves the bit of the DNS where the
|
||||
original query is, is unsigned, so the answer is OK,
|
||||
if unvalidated. */
|
||||
if (status == STAT_NO_DS)
|
||||
{
|
||||
free(packet);
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
/* No DS, not got to DNSSEC-land yet, go up. */
|
||||
if (status == STAT_INSECURE)
|
||||
{
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
if (extract_name(header, plen, &p, name, 1, 4) &&
|
||||
(name_start = strchr(name, '.')))
|
||||
{
|
||||
name_start++; /* chop a label off and try again */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(packet);
|
||||
|
||||
return STAT_BOGUS;
|
||||
}
|
||||
}
|
||||
|
||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
||||
int class, char *name, char *keyname, struct server *server, int *keycount)
|
||||
{
|
||||
@@ -1200,11 +1420,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||
else if (status == STAT_NEED_DS)
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||
|
||||
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
|
||||
{
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
|
||||
new_status = STAT_INSECURE;
|
||||
}
|
||||
else if (status == STAT_CHASE_CNAME)
|
||||
new_status = dnssec_chase_cname(now, header, n, name, keyname);
|
||||
else
|
||||
{
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
|
||||
|
||||
if (new_status == STAT_NO_SIG)
|
||||
{
|
||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
||||
else
|
||||
new_status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||
Make query for same, and recurse to validate */
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
@@ -1234,7 +1470,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
{
|
||||
m = (c1 << 8) | c2;
|
||||
|
||||
if (tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount) == STAT_SECURE)
|
||||
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
|
||||
|
||||
if (new_status == STAT_SECURE)
|
||||
{
|
||||
/* Reached a validated record, now try again at this level.
|
||||
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
|
||||
@@ -1242,11 +1480,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
|
||||
if (status == STAT_NEED_KEY)
|
||||
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||
else if (status == STAT_NEED_DS)
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
else
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||
|
||||
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
|
||||
{
|
||||
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
|
||||
new_status = STAT_INSECURE; /* Validated no DS */
|
||||
}
|
||||
else if (status == STAT_CHASE_CNAME)
|
||||
new_status = dnssec_chase_cname(now, header, n, name, keyname);
|
||||
else
|
||||
{
|
||||
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
|
||||
|
||||
if (new_status == STAT_NO_SIG)
|
||||
{
|
||||
if (option_bool(OPT_DNSSEC_NO_SIGN))
|
||||
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
|
||||
else
|
||||
new_status = STAT_INSECURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||
goto another_tcp_key;
|
||||
}
|
||||
@@ -1254,7 +1508,6 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
|
||||
free(packet);
|
||||
}
|
||||
|
||||
return new_status;
|
||||
}
|
||||
#endif
|
||||
@@ -1272,7 +1525,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_AUTH
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, ad_question, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
||||
int checking_disabled, ad_question, do_bit, added_pheader = 0;
|
||||
int check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
||||
size_t m;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname;
|
||||
@@ -1350,7 +1604,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
/* m > 0 if answered from cache */
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now, &ad_question);
|
||||
dst_addr_4, netmask, now, &ad_question, &do_bit);
|
||||
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(NULL);
|
||||
@@ -1430,8 +1684,17 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((char *) header) + 65536);
|
||||
header->hb4 |= HB4_CD;
|
||||
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
if (option_bool(OPT_DNSSEC_DEBUG))
|
||||
header->hb4 |= HB4_CD;
|
||||
|
||||
if (size != new_size)
|
||||
added_pheader = 1;
|
||||
|
||||
size = new_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1530,7 +1793,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
||||
cache_secure, ad_question, check_subnet, &peer_addr);
|
||||
cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
+135
-83
@@ -1254,87 +1254,187 @@ void pre_allocate_sfds(void)
|
||||
}
|
||||
}
|
||||
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & flag)
|
||||
serv->flags |= SERV_MARK;
|
||||
}
|
||||
|
||||
void cleanup_servers(void)
|
||||
{
|
||||
struct server *serv, *tmp, **up;
|
||||
|
||||
/* unlink and free anything still marked. */
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
server_gone(serv);
|
||||
*up = serv->next;
|
||||
if (serv->domain)
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
}
|
||||
|
||||
void add_update_server(int flags,
|
||||
union mysockaddr *addr,
|
||||
union mysockaddr *source_addr,
|
||||
const char *interface,
|
||||
const char *domain)
|
||||
{
|
||||
struct server *serv, *next = NULL;
|
||||
char *domain_str = NULL;
|
||||
|
||||
/* See if there is a suitable candidate, and unmark */
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
if (serv->flags & SERV_MARK)
|
||||
{
|
||||
if (domain)
|
||||
{
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (serv->flags & SERV_HAS_DOMAIN)
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
domain_str = serv->domain;
|
||||
next = serv->next;
|
||||
}
|
||||
else if ((serv = whine_malloc(sizeof (struct server))))
|
||||
{
|
||||
/* Not found, create a new one. */
|
||||
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
|
||||
{
|
||||
free(serv);
|
||||
serv = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct server *s;
|
||||
/* Add to the end of the chain, for order */
|
||||
if (!daemon->servers)
|
||||
daemon->servers = serv;
|
||||
else
|
||||
{
|
||||
for (s = daemon->servers; s->next; s = s->next);
|
||||
s->next = serv;
|
||||
}
|
||||
if (domain)
|
||||
strcpy(domain_str, domain);
|
||||
}
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->flags = flags;
|
||||
serv->domain = domain_str;
|
||||
serv->next = next;
|
||||
serv->queries = serv->failed_queries = 0;
|
||||
|
||||
if (domain)
|
||||
serv->flags |= SERV_HAS_DOMAIN;
|
||||
|
||||
if (interface)
|
||||
strcpy(serv->interface, interface);
|
||||
if (addr)
|
||||
serv->addr = *addr;
|
||||
if (source_addr)
|
||||
serv->source_addr = *source_addr;
|
||||
}
|
||||
}
|
||||
|
||||
void check_servers(void)
|
||||
{
|
||||
struct irec *iface;
|
||||
struct server *new, *tmp, *ret = NULL;
|
||||
struct server *serv;
|
||||
int port = 0;
|
||||
|
||||
/* interface may be new since startup */
|
||||
if (!option_bool(OPT_NOWILD))
|
||||
enumerate_interfaces(0);
|
||||
|
||||
for (new = daemon->servers; new; new = tmp)
|
||||
for (serv = daemon->servers; serv; serv = serv->next)
|
||||
{
|
||||
tmp = new->next;
|
||||
|
||||
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
|
||||
{
|
||||
port = prettyprint_addr(&new->addr, daemon->namebuff);
|
||||
port = prettyprint_addr(&serv->addr, daemon->namebuff);
|
||||
|
||||
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
|
||||
if (new->addr.sa.sa_family == AF_INET &&
|
||||
new->addr.in.sin_addr.s_addr == 0)
|
||||
if (serv->addr.sa.sa_family == AF_INET &&
|
||||
serv->addr.in.sin_addr.s_addr == 0)
|
||||
{
|
||||
free(new);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (sockaddr_isequal(&new->addr, &iface->addr))
|
||||
if (sockaddr_isequal(&serv->addr, &iface->addr))
|
||||
break;
|
||||
if (iface)
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
|
||||
free(new);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we need a socket set? */
|
||||
if (!new->sfd &&
|
||||
!(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
|
||||
if (!serv->sfd &&
|
||||
!(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
|
||||
errno != 0)
|
||||
{
|
||||
my_syslog(LOG_WARNING,
|
||||
_("ignoring nameserver %s - cannot make/bind socket: %s"),
|
||||
daemon->namebuff, strerror(errno));
|
||||
free(new);
|
||||
serv->flags |= SERV_MARK;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* reverse order - gets it right. */
|
||||
new->next = ret;
|
||||
ret = new;
|
||||
|
||||
if (!(new->flags & SERV_NO_REBIND))
|
||||
if (!(serv->flags & SERV_NO_REBIND))
|
||||
{
|
||||
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
|
||||
if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
|
||||
{
|
||||
char *s1, *s2;
|
||||
if (!(new->flags & SERV_HAS_DOMAIN))
|
||||
if (!(serv->flags & SERV_HAS_DOMAIN))
|
||||
s1 = _("unqualified"), s2 = _("names");
|
||||
else if (strlen(new->domain) == 0)
|
||||
else if (strlen(serv->domain) == 0)
|
||||
s1 = _("default"), s2 = "";
|
||||
else
|
||||
s1 = _("domain"), s2 = new->domain;
|
||||
s1 = _("domain"), s2 = serv->domain;
|
||||
|
||||
if (new->flags & SERV_NO_ADDR)
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
|
||||
else if (new->flags & SERV_USE_RESOLV)
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
|
||||
else if (!(new->flags & SERV_LITERAL_ADDRESS))
|
||||
else if (!(serv->flags & SERV_LITERAL_ADDRESS))
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
|
||||
}
|
||||
else if (new->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
|
||||
else if (serv->interface[0] != 0)
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
|
||||
}
|
||||
}
|
||||
|
||||
daemon->servers = ret;
|
||||
|
||||
cleanup_servers();
|
||||
}
|
||||
|
||||
/* Return zero if no servers found, in that case we keep polling.
|
||||
@@ -1343,9 +1443,6 @@ int reload_servers(char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
char *line;
|
||||
struct server *old_servers = NULL;
|
||||
struct server *new_servers = NULL;
|
||||
struct server *serv;
|
||||
int gotone = 0;
|
||||
|
||||
/* buff happens to be MAXDNAME long... */
|
||||
@@ -1354,28 +1451,9 @@ int reload_servers(char *fname)
|
||||
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* move old servers to free list - we can reuse the memory
|
||||
and not risk malloc if there are the same or fewer new servers.
|
||||
Servers which were specced on the command line go to the new list. */
|
||||
for (serv = daemon->servers; serv;)
|
||||
{
|
||||
struct server *tmp = serv->next;
|
||||
if (serv->flags & SERV_FROM_RESOLV)
|
||||
{
|
||||
serv->next = old_servers;
|
||||
old_servers = serv;
|
||||
/* forward table rules reference servers, so have to blow them away */
|
||||
server_gone(serv);
|
||||
}
|
||||
else
|
||||
{
|
||||
serv->next = new_servers;
|
||||
new_servers = serv;
|
||||
}
|
||||
serv = tmp;
|
||||
}
|
||||
|
||||
|
||||
mark_servers(SERV_FROM_RESOLV);
|
||||
|
||||
while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
|
||||
{
|
||||
union mysockaddr addr, source_addr;
|
||||
@@ -1434,38 +1512,12 @@ int reload_servers(char *fname)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (old_servers)
|
||||
{
|
||||
serv = old_servers;
|
||||
old_servers = old_servers->next;
|
||||
}
|
||||
else if (!(serv = whine_malloc(sizeof (struct server))))
|
||||
continue;
|
||||
|
||||
/* this list is reverse ordered:
|
||||
it gets reversed again in check_servers */
|
||||
serv->next = new_servers;
|
||||
new_servers = serv;
|
||||
serv->addr = addr;
|
||||
serv->source_addr = source_addr;
|
||||
serv->domain = NULL;
|
||||
serv->interface[0] = 0;
|
||||
serv->sfd = NULL;
|
||||
serv->flags = SERV_FROM_RESOLV;
|
||||
serv->queries = serv->failed_queries = 0;
|
||||
add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
|
||||
gotone = 1;
|
||||
}
|
||||
|
||||
/* Free any memory not used. */
|
||||
while (old_servers)
|
||||
{
|
||||
struct server *tmp = old_servers->next;
|
||||
free(old_servers);
|
||||
old_servers = tmp;
|
||||
}
|
||||
|
||||
daemon->servers = new_servers;
|
||||
fclose(f);
|
||||
cleanup_servers();
|
||||
|
||||
return gotone;
|
||||
}
|
||||
|
||||
+139
-57
@@ -141,6 +141,9 @@ struct myoption {
|
||||
#define LOPT_SEC_VALID 329
|
||||
#define LOPT_TRUST_ANCHOR 330
|
||||
#define LOPT_DNSSEC_DEBUG 331
|
||||
#define LOPT_REV_SERV 332
|
||||
#define LOPT_SERVERS_FILE 333
|
||||
#define LOPT_DNSSEC_CHECK 334
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -157,6 +160,7 @@ static const struct myoption opts[] =
|
||||
{ "user", 2, 0, 'u' },
|
||||
{ "group", 2, 0, 'g' },
|
||||
{ "resolv-file", 2, 0, 'r' },
|
||||
{ "servers-file", 1, 0, LOPT_SERVERS_FILE },
|
||||
{ "mx-host", 1, 0, 'm' },
|
||||
{ "mx-target", 1, 0, 't' },
|
||||
{ "cache-size", 2, 0, 'c' },
|
||||
@@ -178,6 +182,7 @@ static const struct myoption opts[] =
|
||||
{ "pid-file", 2, 0, 'x' },
|
||||
{ "strict-order", 0, 0, 'o' },
|
||||
{ "server", 1, 0, 'S' },
|
||||
{ "rev-server", 1, 0, LOPT_REV_SERV },
|
||||
{ "local", 1, 0, LOPT_LOCAL },
|
||||
{ "address", 1, 0, 'A' },
|
||||
{ "conf-file", 2, 0, 'C' },
|
||||
@@ -279,6 +284,7 @@ static const struct myoption opts[] =
|
||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
||||
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
|
||||
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
||||
#endif
|
||||
@@ -347,7 +353,9 @@ static struct {
|
||||
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
|
||||
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
|
||||
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
|
||||
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
|
||||
{ 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
|
||||
{ LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
|
||||
{ LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
|
||||
{ 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
|
||||
{ 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
|
||||
@@ -432,6 +440,7 @@ static struct {
|
||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
|
||||
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
|
||||
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
||||
#endif
|
||||
@@ -591,6 +600,7 @@ static int atoi_check16(char *a, int *res)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
static int atoi_check8(char *a, int *res)
|
||||
{
|
||||
if (!(atoi_check(a, res)) ||
|
||||
@@ -600,6 +610,7 @@ static int atoi_check8(char *a, int *res)
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void add_txt(char *name, char *txt)
|
||||
{
|
||||
@@ -684,6 +695,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
|
||||
char *scope_id;
|
||||
#endif
|
||||
|
||||
if (!arg || strlen(arg) == 0)
|
||||
{
|
||||
*flags |= SERV_NO_ADDR;
|
||||
*interface = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(source, '#')) &&
|
||||
!atoi_check16(portno, &source_port))
|
||||
@@ -762,6 +780,54 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct server *add_rev4(struct in_addr addr, int msize)
|
||||
{
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
in_addr_t a = ntohl(addr.s_addr) >> 8;
|
||||
char *p;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
|
||||
|
||||
if (msize == 24)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
if (msize != 8)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
return serv;
|
||||
|
||||
}
|
||||
|
||||
static struct server *add_rev6(struct in6_addr *addr, int msize)
|
||||
{
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
|
||||
|
||||
for (i = msize-1; i >= 0; i -= 4)
|
||||
{
|
||||
int dig = ((unsigned char *)addr)[i>>3];
|
||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
}
|
||||
p += sprintf(p, "ip6.arpa");
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
return serv;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
|
||||
static int is_tag_prefix(char *arg)
|
||||
@@ -1325,7 +1391,7 @@ void reset_option_bool(unsigned int opt)
|
||||
daemon->options2 &= ~(1u << (opt - 32));
|
||||
}
|
||||
|
||||
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line)
|
||||
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
|
||||
{
|
||||
int i;
|
||||
char *comma;
|
||||
@@ -1528,6 +1594,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->resolv_files = list;
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_SERVERS_FILE:
|
||||
daemon->servers_file = opt_string_alloc(arg);
|
||||
break;
|
||||
|
||||
case 'm': /* --mx-host */
|
||||
{
|
||||
@@ -1824,34 +1894,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
ret_err(gen_err);
|
||||
else
|
||||
{
|
||||
/* generate the equivalent of
|
||||
local=/<domain>/
|
||||
local=/xxx.yyy.zzz.in-addr.arpa/ */
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
in_addr_t a = ntohl(new->start.s_addr) >> 8;
|
||||
char *p;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->domain = d;
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
serv = opt_malloc(sizeof(struct server));
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
|
||||
|
||||
if (msize == 24)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
if (msize != 8)
|
||||
p += sprintf(p, "%d.", a & 0xff);
|
||||
a = a >> 8;
|
||||
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
/* generate the equivalent of
|
||||
local=/<domain>/
|
||||
local=/xxx.yyy.zzz.in-addr.arpa/ */
|
||||
struct server *serv = add_rev4(new->start, msize);
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1887,29 +1934,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
/* generate the equivalent of
|
||||
local=/<domain>/
|
||||
local=/xxx.yyy.zzz.ip6.arpa/ */
|
||||
struct server *serv = opt_malloc(sizeof(struct server));
|
||||
char *p;
|
||||
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
serv->domain = d;
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
|
||||
serv = opt_malloc(sizeof(struct server));
|
||||
memset(serv, 0, sizeof(struct server));
|
||||
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
|
||||
|
||||
for (i = msize-1; i >= 0; i -= 4)
|
||||
{
|
||||
int dig = ((unsigned char *)&new->start6)[i>>3];
|
||||
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
|
||||
}
|
||||
p += sprintf(p, "ip6.arpa");
|
||||
|
||||
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
|
||||
serv->next = daemon->servers;
|
||||
daemon->servers = serv;
|
||||
struct server *serv = add_rev6(&new->start6, msize);
|
||||
serv->flags |= SERV_NO_ADDR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2136,6 +2162,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
memset(newlist, 0, sizeof(struct server));
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
newlist->flags |= SERV_FROM_FILE;
|
||||
|
||||
if (option == 'A')
|
||||
{
|
||||
newlist->flags |= SERV_LITERAL_ADDRESS;
|
||||
@@ -2180,6 +2209,40 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_REV_SERV: /* --rev-server */
|
||||
{
|
||||
char *string;
|
||||
int size;
|
||||
struct server *serv;
|
||||
struct in_addr addr4;
|
||||
#ifdef HAVE_IPV6
|
||||
struct in6_addr addr6;
|
||||
#endif
|
||||
|
||||
unhide_metas(arg);
|
||||
if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (inet_pton(AF_INET, arg, &addr4))
|
||||
serv = add_rev4(addr4, size);
|
||||
#ifdef HAVE_IPV6
|
||||
else if (inet_pton(AF_INET6, arg, &addr6))
|
||||
serv = add_rev6(&addr6, size);
|
||||
#endif
|
||||
else
|
||||
ret_err(gen_err);
|
||||
|
||||
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
|
||||
|
||||
if (string)
|
||||
ret_err(string);
|
||||
|
||||
if (servers_only)
|
||||
serv->flags |= SERV_FROM_FILE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_IPSET: /* --ipset */
|
||||
#ifndef HAVE_IPSET
|
||||
ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
|
||||
@@ -3754,12 +3817,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
|
||||
|
||||
while (fgets(buff, MAXDNAME, f))
|
||||
{
|
||||
int white, i, option = hard_opt;
|
||||
int white, i;
|
||||
volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
|
||||
char *errmess, *p, *arg = NULL, *start;
|
||||
size_t len;
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (option != 0)
|
||||
if (option != 0 || hard_opt == LOPT_REV_SERV)
|
||||
{
|
||||
if (setjmp(mem_jmp))
|
||||
continue;
|
||||
@@ -3860,13 +3924,15 @@ static void read_file(char *file, FILE *f, int hard_opt)
|
||||
errmess = _("extraneous parameter");
|
||||
else if (opts[i].has_arg == 1 && !arg)
|
||||
errmess = _("missing parameter");
|
||||
else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
|
||||
errmess = _("illegal option");
|
||||
}
|
||||
|
||||
oops:
|
||||
if (errmess)
|
||||
strcpy(daemon->namebuff, errmess);
|
||||
|
||||
if (errmess || !one_opt(option, arg, buff, _("error"), 0))
|
||||
if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
|
||||
{
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
|
||||
if (hard_opt != 0)
|
||||
@@ -4048,6 +4114,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
|
||||
return list;
|
||||
}
|
||||
|
||||
void read_servers_file(void)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if (!(f = fopen(daemon->servers_file, "r")))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
mark_servers(SERV_FROM_FILE);
|
||||
cleanup_servers();
|
||||
|
||||
read_file(daemon->servers_file, f, LOPT_REV_SERV);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
void reread_dhcp(void)
|
||||
@@ -4241,9 +4323,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1))
|
||||
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
|
||||
#else
|
||||
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1))
|
||||
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
|
||||
#endif
|
||||
die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
|
||||
}
|
||||
|
||||
+36
-25
@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
|
||||
return ansp;
|
||||
}
|
||||
|
||||
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
|
||||
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
|
||||
{
|
||||
int i, rdlen;
|
||||
|
||||
@@ -601,11 +601,11 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
|
||||
|
||||
if (family == parm->l3->sa.sa_family)
|
||||
{
|
||||
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
|
||||
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
|
||||
match = 1;
|
||||
#ifdef HAVE_IPV6
|
||||
else
|
||||
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
|
||||
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
|
||||
match = 1;
|
||||
#endif
|
||||
}
|
||||
@@ -917,8 +917,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, name, doctored);
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (*doctored)
|
||||
secure = 0;
|
||||
if (*doctored && secure)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -927,7 +927,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
for (i = ntohs(header->qdcount); i != 0; i--)
|
||||
{
|
||||
int found = 0, cname_count = 5;
|
||||
int found = 0, cname_count = CNAME_CHAIN;
|
||||
struct crec *cpp = NULL;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
int secflag = secure ? F_DNSSECOK : 0;
|
||||
@@ -988,9 +988,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
secflag = 0; /* no longer DNSSEC */
|
||||
if (!cname_count-- || secure)
|
||||
return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
@@ -1453,7 +1452,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
|
||||
/* return zero if we can't answer from cache, or packet size if we can */
|
||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
struct in_addr local_addr, struct in_addr local_netmask,
|
||||
time_t now, int *ad_reqd)
|
||||
time_t now, int *ad_reqd, int *do_bit)
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp, *pheader;
|
||||
@@ -1475,7 +1474,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
/* RFC 6840 5.7 */
|
||||
*ad_reqd = header->hb4 & HB4_AD;
|
||||
|
||||
*do_bit = 0;
|
||||
|
||||
/* If there is an RFC2671 pseudoheader then it will be overwritten by
|
||||
partial replies, so we have to do a dry run to see if we can answer
|
||||
the query. We check to see if the do bit is set, if so we always
|
||||
@@ -1493,7 +1493,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
pheader += 2; /* ext_rcode */
|
||||
GETSHORT(flags, pheader);
|
||||
|
||||
sec_reqd = flags & 0x8000; /* do bit */
|
||||
if ((sec_reqd = flags & 0x8000))
|
||||
*do_bit = 1;/* do bit */
|
||||
*ad_reqd = 1;
|
||||
|
||||
/* If our client is advertising a larger UDP packet size
|
||||
@@ -1578,19 +1579,29 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
||||
if (crecp->uid == qclass)
|
||||
{
|
||||
gotone = 1;
|
||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
||||
{
|
||||
struct all_addr a;
|
||||
a.addr.keytag = crecp->addr.ds.keytag;
|
||||
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_DS, qclass, "sbbt",
|
||||
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
||||
anscount++;
|
||||
|
||||
}
|
||||
gotone = 1;
|
||||
if (!dryrun)
|
||||
{
|
||||
if (crecp->flags & F_NEG)
|
||||
{
|
||||
if (crecp->flags & F_NXDOMAIN)
|
||||
nxdomain = 1;
|
||||
log_query(F_UPSTREAM, name, NULL, "secure no DS");
|
||||
}
|
||||
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
||||
{
|
||||
struct all_addr a;
|
||||
a.addr.keytag = crecp->addr.ds.keytag;
|
||||
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
crec_ttl(crecp, now), &nameoffset,
|
||||
T_DS, qclass, "sbbt",
|
||||
crecp->addr.ds.keytag, crecp->addr.ds.algo,
|
||||
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
||||
anscount++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* DNSKEY */
|
||||
|
||||
Reference in new issue
Block a user