mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-12 16:50:54 +00:00
Compare commits
6
Commits
v2.69test7
...
v2.69test8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee4158678a | ||
|
|
83349b8aa4 | ||
|
|
7fa836e105 | ||
|
|
1633e30834 | ||
|
|
c8ca33f810 | ||
|
|
e243c072b5 |
No files matched your search
+15
-6
@@ -13,7 +13,10 @@ Dnsmasq accepts DNS queries and either answers them from a small, local,
|
|||||||
cache or forwards them to a real, recursive, DNS server. It loads the
|
cache or forwards them to a real, recursive, DNS server. It loads the
|
||||||
contents of /etc/hosts so that local hostnames
|
contents of /etc/hosts so that local hostnames
|
||||||
which do not appear in the global DNS can be resolved and also answers
|
which do not appear in the global DNS can be resolved and also answers
|
||||||
DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
|
DNS queries for DHCP configured hosts. It can also act as the
|
||||||
|
authoritative DNS server for one or more domains, allowing local names
|
||||||
|
to appear in the global DNS. It can be configured to do DNSSEC
|
||||||
|
validation.
|
||||||
.PP
|
.PP
|
||||||
The dnsmasq DHCP server supports static address assignments and multiple
|
The dnsmasq DHCP server supports static address assignments and multiple
|
||||||
networks. It automatically
|
networks. It automatically
|
||||||
@@ -587,12 +590,15 @@ validation by clients more efficient. Note that validation by clients is the mos
|
|||||||
clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between
|
clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between
|
||||||
the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
|
the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
|
||||||
trust anchors provided, see
|
trust anchors provided, see
|
||||||
.B --dnsskey.
|
.B --trust-anchor.
|
||||||
Because the DNSSEC validation process uses the cache, it is not permitted to reduce the cache size below the default when DNSSEC is enabled.
|
Because the DNSSEC validation process uses the cache, it is not permitted to reduce the cache size below the default when DNSSEC is enabled.
|
||||||
.TP
|
.TP
|
||||||
.B --dnskey=[<class>],<domain>,<flags>,<algorithm>,<base64-key>
|
.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
|
||||||
Provide DNSKEY records to act a trust anchors for DNSSEC validation. Typically these will be the keys for root zone,
|
Provide DS records to act a trust anchors for DNSSEC
|
||||||
but trust anchors for limited domains are also possible.
|
validation. Typically these will be the DS record(s) for Zone Signing
|
||||||
|
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
|
.TP
|
||||||
.B --proxy-dnssec
|
.B --proxy-dnssec
|
||||||
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
|
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
|
||||||
@@ -601,7 +607,10 @@ dnsmasq and the upstream servers, and the trustworthiness of the upstream server
|
|||||||
.TP
|
.TP
|
||||||
.B --dnssec-debug
|
.B --dnssec-debug
|
||||||
Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
|
Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries,
|
||||||
and don't convert BOGUS replies to SERVFAIL responses.
|
and don't convert replies which do not validate to responses with
|
||||||
|
a return code of SERVFAIL. Note that
|
||||||
|
setting this may affect DNS behaviour in bad ways, it is not an
|
||||||
|
extra-logging flag and should not be set in production.
|
||||||
.TP
|
.TP
|
||||||
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
|
.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
|
||||||
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
|
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
|
||||||
|
|||||||
+11
-11
@@ -460,7 +460,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* First remove any expired entries and entries for the name/address we
|
/* First remove any expired entries and entries for the name/address we
|
||||||
are currently inserting. Fail is we attempt to delete a name from
|
are currently inserting. Fail if we attempt to delete a name from
|
||||||
/etc/hosts or DHCP. */
|
/etc/hosts or DHCP. */
|
||||||
if (!cache_scan_free(name, addr, now, flags))
|
if (!cache_scan_free(name, addr, now, flags))
|
||||||
{
|
{
|
||||||
@@ -985,7 +985,7 @@ void cache_reload(void)
|
|||||||
struct cname *a;
|
struct cname *a;
|
||||||
struct interface_name *intr;
|
struct interface_name *intr;
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
struct dnskey *key;
|
struct ds_config *ds;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cache_inserted = cache_live_freed = 0;
|
cache_inserted = cache_live_freed = 0;
|
||||||
@@ -1031,17 +1031,17 @@ void cache_reload(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
for (key = daemon->dnskeys; key; key = key->next)
|
for (ds = daemon->ds; ds; ds = ds->next)
|
||||||
if ((cache = whine_malloc(sizeof(struct crec))) &&
|
if ((cache = whine_malloc(sizeof(struct crec))) &&
|
||||||
(cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
|
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
|
||||||
{
|
{
|
||||||
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
|
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
|
||||||
cache->name.namep = key->name;
|
cache->name.namep = ds->name;
|
||||||
cache->addr.key.keylen = key->keylen;
|
cache->addr.ds.keylen = ds->digestlen;
|
||||||
cache->addr.key.algo = key->algo;
|
cache->addr.ds.algo = ds->algo;
|
||||||
cache->addr.key.flags = key->flags;
|
cache->addr.ds.keytag = ds->keytag;
|
||||||
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
|
cache->addr.ds.digest = ds->digest_type;
|
||||||
cache->uid = key->class;
|
cache->uid = ds->class;
|
||||||
cache_hash(cache);
|
cache_hash(cache);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
|
||||||
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
|
||||||
#define KEYBLOCK_LEN 35 /* choose to mininise fragmentation when storing DNSSEC keys */
|
#define KEYBLOCK_LEN 35 /* choose to mininise fragmentation when storing DNSSEC keys */
|
||||||
|
#define DNSSEC_WORK 50 /* Max number of queries to validate one question */
|
||||||
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
|
||||||
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
#define FORWARD_TEST 50 /* try all servers every 50 queries */
|
||||||
#define FORWARD_TIME 20 /* or 20 seconds */
|
#define FORWARD_TIME 20 /* or 20 seconds */
|
||||||
|
|||||||
+1
-1
@@ -144,7 +144,7 @@ int main (int argc, char **argv)
|
|||||||
if (option_bool(OPT_DNSSEC_VALID))
|
if (option_bool(OPT_DNSSEC_VALID))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (!daemon->dnskeys)
|
if (!daemon->ds)
|
||||||
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
|
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
|
||||||
|
|
||||||
if (daemon->cachesize < CACHESIZ)
|
if (daemon->cachesize < CACHESIZ)
|
||||||
|
|||||||
+9
-10
@@ -295,10 +295,10 @@ struct cname {
|
|||||||
struct cname *next;
|
struct cname *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dnskey {
|
struct ds_config {
|
||||||
char *name, *key;
|
char *name, *digest;
|
||||||
int keylen, class, algo, flags;
|
int digestlen, class, algo, keytag, digest_type;
|
||||||
struct dnskey *next;
|
struct ds_config *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ADDRLIST_LITERAL 1
|
#define ADDRLIST_LITERAL 1
|
||||||
@@ -539,6 +539,7 @@ struct hostsfile {
|
|||||||
#define FREC_HAS_SUBNET 4
|
#define FREC_HAS_SUBNET 4
|
||||||
#define FREC_DNSKEY_QUERY 8
|
#define FREC_DNSKEY_QUERY 8
|
||||||
#define FREC_DS_QUERY 16
|
#define FREC_DS_QUERY 16
|
||||||
|
#define FREC_AD_QUESTION 32
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
#define HASH_SIZE 20 /* SHA-1 digest size */
|
#define HASH_SIZE 20 /* SHA-1 digest size */
|
||||||
@@ -560,7 +561,7 @@ struct frec {
|
|||||||
time_t time;
|
time_t time;
|
||||||
unsigned char *hash[HASH_SIZE];
|
unsigned char *hash[HASH_SIZE];
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
int class;
|
int class, work_counter;
|
||||||
struct blockdata *stash; /* Saved reply, whilst we validate */
|
struct blockdata *stash; /* Saved reply, whilst we validate */
|
||||||
size_t stash_len;
|
size_t stash_len;
|
||||||
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
struct frec *dependent; /* Query awaiting internally-generated DNSKEY or DS query */
|
||||||
@@ -929,7 +930,7 @@ extern struct daemon {
|
|||||||
struct prefix_class *prefix_classes;
|
struct prefix_class *prefix_classes;
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
struct dnskey *dnskeys;
|
struct ds_config *ds;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* globally used stuff for DNS */
|
/* globally used stuff for DNS */
|
||||||
@@ -1041,7 +1042,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
|
|||||||
time_t now, char **ipsets, int is_sign, int checkrebind,
|
time_t now, char **ipsets, int is_sign, int checkrebind,
|
||||||
int no_cache, int secure, int *doctored);
|
int no_cache, int secure, int *doctored);
|
||||||
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
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);
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
|
time_t now, int *ad_reqd);
|
||||||
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
|
||||||
struct bogus_addr *addr, time_t now);
|
struct bogus_addr *addr, time_t now);
|
||||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||||
@@ -1105,9 +1107,6 @@ void prettyprint_time(char *buf, unsigned int t);
|
|||||||
int prettyprint_addr(union mysockaddr *addr, char *buf);
|
int prettyprint_addr(union mysockaddr *addr, char *buf);
|
||||||
int parse_hex(char *in, unsigned char *out, int maxlen,
|
int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||||
unsigned int *wildcard_mask, int *mac_type);
|
unsigned int *wildcard_mask, int *mac_type);
|
||||||
#ifdef HAVE_DNSSEC
|
|
||||||
int parse_base64(char *in, char *out);
|
|
||||||
#endif
|
|
||||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
||||||
unsigned int mask);
|
unsigned int mask);
|
||||||
int expand_buf(struct iovec *iov, size_t size);
|
int expand_buf(struct iovec *iov, size_t size);
|
||||||
|
|||||||
+3
-2
@@ -1168,6 +1168,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
|||||||
type_covered == T_DNSKEY || type_covered == T_PTR))
|
type_covered == T_DNSKEY || type_covered == T_PTR))
|
||||||
{
|
{
|
||||||
a.addr.dnssec.type = type_covered;
|
a.addr.dnssec.type = type_covered;
|
||||||
|
a.addr.dnssec.class = class1;
|
||||||
|
|
||||||
algo = *p2++;
|
algo = *p2++;
|
||||||
p2 += 13; /* labels, orig_ttl, expiration, inception */
|
p2 += 13; /* labels, orig_ttl, expiration, inception */
|
||||||
@@ -1373,12 +1374,12 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned long ac;
|
unsigned long ac = flags + 0x300 + alg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ac = ((htons(flags) >> 8) | ((htons(flags) << 8) & 0xff00)) + 0x300 + alg;
|
|
||||||
for (i = 0; i < keylen; ++i)
|
for (i = 0; i < keylen; ++i)
|
||||||
ac += (i & 1) ? key[i] : key[i] << 8;
|
ac += (i & 1) ? key[i] : key[i] << 8;
|
||||||
|
|
||||||
ac += (ac >> 16) & 0xffff;
|
ac += (ac >> 16) & 0xffff;
|
||||||
return ac & 0xffff;
|
return ac & 0xffff;
|
||||||
}
|
}
|
||||||
|
|||||||
+165
-145
@@ -234,7 +234,8 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
|
|||||||
|
|
||||||
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||||
struct all_addr *dst_addr, unsigned int dst_iface,
|
struct all_addr *dst_addr, unsigned int dst_iface,
|
||||||
struct dns_header *header, size_t plen, time_t now, struct frec *forward)
|
struct dns_header *header, size_t plen, time_t now,
|
||||||
|
struct frec *forward, int ad_reqd)
|
||||||
{
|
{
|
||||||
char *domain = NULL;
|
char *domain = NULL;
|
||||||
int type = 0, norebind = 0;
|
int type = 0, norebind = 0;
|
||||||
@@ -249,9 +250,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
#endif
|
#endif
|
||||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||||
|
|
||||||
/* RFC 4035: sect 4.6 para 2 */
|
|
||||||
header->hb4 &= ~HB4_AD;
|
|
||||||
|
|
||||||
/* may be no servers available. */
|
/* may be no servers available. */
|
||||||
if (!daemon->servers)
|
if (!daemon->servers)
|
||||||
forward = NULL;
|
forward = NULL;
|
||||||
@@ -334,6 +332,11 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
|||||||
forward->flags |= FREC_NOREBIND;
|
forward->flags |= FREC_NOREBIND;
|
||||||
if (header->hb4 & HB4_CD)
|
if (header->hb4 & HB4_CD)
|
||||||
forward->flags |= FREC_CHECKING_DISABLED;
|
forward->flags |= FREC_CHECKING_DISABLED;
|
||||||
|
if (ad_reqd)
|
||||||
|
forward->flags |= FREC_AD_QUESTION;
|
||||||
|
#ifdef HAVE_DNSSEC
|
||||||
|
forward->work_counter = DNSSEC_WORK;
|
||||||
|
#endif
|
||||||
|
|
||||||
header->id = htons(forward->new_id);
|
header->id = htons(forward->new_id);
|
||||||
|
|
||||||
@@ -503,13 +506,15 @@ 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,
|
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 check_subnet, union mysockaddr *query_source)
|
int no_cache, int cache_secure, int ad_reqd, int check_subnet, union mysockaddr *query_source)
|
||||||
{
|
{
|
||||||
unsigned char *pheader, *sizep;
|
unsigned char *pheader, *sizep;
|
||||||
char **sets = 0;
|
char **sets = 0;
|
||||||
int munged = 0, is_sign;
|
int munged = 0, is_sign;
|
||||||
size_t plen;
|
size_t plen;
|
||||||
|
|
||||||
|
(void)ad_reqd;
|
||||||
|
|
||||||
#ifdef HAVE_IPSET
|
#ifdef HAVE_IPSET
|
||||||
/* Similar algorithm to search_servers. */
|
/* Similar algorithm to search_servers. */
|
||||||
struct ipsets *ipset_pos;
|
struct ipsets *ipset_pos;
|
||||||
@@ -535,15 +540,13 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
|
|
||||||
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
|
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
|
||||||
{
|
{
|
||||||
if (!is_sign)
|
unsigned short udpsz;
|
||||||
{
|
unsigned char *psave = sizep;
|
||||||
unsigned short udpsz;
|
|
||||||
unsigned char *psave = sizep;
|
|
||||||
|
|
||||||
GETSHORT(udpsz, sizep);
|
GETSHORT(udpsz, sizep);
|
||||||
if (udpsz > daemon->edns_pktsz)
|
|
||||||
PUTSHORT(daemon->edns_pktsz, psave);
|
if (!is_sign && udpsz > daemon->edns_pktsz)
|
||||||
}
|
PUTSHORT(daemon->edns_pktsz, psave);
|
||||||
|
|
||||||
if (check_subnet && !check_source(header, plen, pheader, query_source))
|
if (check_subnet && !check_source(header, plen, pheader, query_source))
|
||||||
{
|
{
|
||||||
@@ -619,7 +622,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
|||||||
if (option_bool(OPT_DNSSEC_VALID))
|
if (option_bool(OPT_DNSSEC_VALID))
|
||||||
header->hb4 &= ~HB4_AD;
|
header->hb4 &= ~HB4_AD;
|
||||||
|
|
||||||
if (!(header->hb4 & HB4_CD) && cache_secure)
|
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
|
||||||
header->hb4 |= HB4_AD;
|
header->hb4 |= HB4_AD;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -705,7 +708,7 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
|
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
|
||||||
{
|
{
|
||||||
header->hb3 &= ~(HB3_QR | HB3_TC);
|
header->hb3 &= ~(HB3_QR | HB3_TC);
|
||||||
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
|
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -780,10 +783,26 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
answer aside, whilst we get that. */
|
answer aside, whilst we get that. */
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||||
{
|
{
|
||||||
struct frec *new;
|
struct frec *new, *orig;
|
||||||
|
|
||||||
if ((new = get_new_frec(now, NULL, 1)))
|
/* Free any saved query */
|
||||||
|
if (forward->stash)
|
||||||
|
blockdata_free(forward->stash);
|
||||||
|
|
||||||
|
/* Now save reply pending receipt of key data */
|
||||||
|
if (!(forward->stash = blockdata_alloc((char *)header, n)))
|
||||||
|
return;
|
||||||
|
forward->stash_len = n;
|
||||||
|
|
||||||
|
anotherkey:
|
||||||
|
/* Find the original query that started it all.... */
|
||||||
|
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||||
|
|
||||||
|
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
|
||||||
|
status = STAT_INSECURE;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
|
int fd;
|
||||||
struct frec *next = new->next;
|
struct frec *next = new->next;
|
||||||
*new = *forward; /* copy everything, then overwrite */
|
*new = *forward; /* copy everything, then overwrite */
|
||||||
new->next = next;
|
new->next = next;
|
||||||
@@ -794,80 +813,67 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
#endif
|
#endif
|
||||||
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
|
||||||
|
|
||||||
/* Free any saved query */
|
new->dependent = forward; /* to find query awaiting new one. */
|
||||||
if (forward->stash)
|
forward->blocking_query = new; /* for garbage cleaning */
|
||||||
blockdata_free(forward->stash);
|
/* validate routines leave name of required record in daemon->keyname */
|
||||||
|
if (status == STAT_NEED_KEY)
|
||||||
/* Now save reply pending receipt of key data */
|
{
|
||||||
if (!(forward->stash = blockdata_alloc((char *)header, n)))
|
new->flags |= FREC_DNSKEY_QUERY;
|
||||||
free_frec(new); /* malloc failure, unwind */
|
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
||||||
|
daemon->keyname, forward->class, T_DNSKEY, &server->addr);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int fd;
|
new->flags |= FREC_DS_QUERY;
|
||||||
|
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
|
||||||
|
daemon->keyname, forward->class, T_DS, &server->addr);
|
||||||
|
}
|
||||||
|
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
||||||
|
memcpy(new->hash, hash, HASH_SIZE);
|
||||||
|
new->new_id = get_id();
|
||||||
|
header->id = htons(new->new_id);
|
||||||
|
/* Save query for retransmission */
|
||||||
|
new->stash = blockdata_alloc((char *)header, nn);
|
||||||
|
new->stash_len = nn;
|
||||||
|
|
||||||
forward->stash_len = n;
|
/* Don't resend this. */
|
||||||
|
daemon->srv_save = NULL;
|
||||||
|
|
||||||
new->dependent = forward; /* to find query awaiting new one. */
|
if (server->sfd)
|
||||||
forward->blocking_query = new; /* for garbage cleaning */
|
fd = server->sfd->fd;
|
||||||
/* validate routines leave name of required record in daemon->keyname */
|
else
|
||||||
if (status == STAT_NEED_KEY)
|
{
|
||||||
{
|
fd = -1;
|
||||||
new->flags |= FREC_DNSKEY_QUERY;
|
|
||||||
nn = dnssec_generate_query(header, ((char *) header) + daemon->packet_buff_sz,
|
|
||||||
daemon->keyname, forward->class, T_DNSKEY, &server->addr);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if ((hash = hash_questions(header, nn, daemon->namebuff)))
|
|
||||||
memcpy(new->hash, hash, HASH_SIZE);
|
|
||||||
new->new_id = get_id();
|
|
||||||
header->id = htons(new->new_id);
|
|
||||||
/* Save query for retransmission */
|
|
||||||
new->stash = blockdata_alloc((char *)header, nn);
|
|
||||||
new->stash_len = nn;
|
|
||||||
|
|
||||||
/* Don't resend this. */
|
|
||||||
daemon->srv_save = NULL;
|
|
||||||
|
|
||||||
if (server->sfd)
|
|
||||||
fd = server->sfd->fd;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fd = -1;
|
|
||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
if (server->addr.sa.sa_family == AF_INET6)
|
if (server->addr.sa.sa_family == AF_INET6)
|
||||||
{
|
|
||||||
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
|
||||||
fd = new->rfd6->fd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
|
||||||
fd = new->rfd4->fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd != -1)
|
|
||||||
{
|
{
|
||||||
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
|
if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6)))
|
||||||
server->queries++;
|
fd = new->rfd6->fd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET)))
|
||||||
|
fd = new->rfd4->fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
if (fd != -1)
|
||||||
|
{
|
||||||
|
while (sendto(fd, (char *)header, nn, 0, &server->addr.sa, sa_len(&server->addr)) == -1 && retry_send());
|
||||||
|
server->queries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
|
/* Ok, we reached far enough up the chain-of-trust that we can validate something.
|
||||||
Now wind back down, pulling back answers which wouldn't previously validate
|
Now wind back down, pulling back answers which wouldn't previously validate
|
||||||
and validate them with the new data. Failure to find needed data here is an internal error.
|
and validate them with the new data. Note that if an answer needs multiple
|
||||||
Once we get to the original answer (FREC_DNSSEC_QUERY not set) and it validates,
|
keys to validate, we may find another key is needed, in which case we set off
|
||||||
return it to the original requestor. */
|
down another branch of the tree. Once we get to the original answer
|
||||||
|
(FREC_DNSSEC_QUERY not set) and it validates, return it to the original requestor. */
|
||||||
while (forward->dependent)
|
while (forward->dependent)
|
||||||
{
|
{
|
||||||
struct frec *prev = forward->dependent;
|
struct frec *prev = forward->dependent;
|
||||||
@@ -887,18 +893,23 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
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);
|
||||||
|
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
||||||
{
|
goto anotherkey;
|
||||||
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == STAT_TRUNCATED)
|
if (status == STAT_TRUNCATED)
|
||||||
header->hb3 |= HB3_TC;
|
header->hb3 |= HB3_TC;
|
||||||
else
|
else
|
||||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
|
{
|
||||||
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
char *result;
|
||||||
|
|
||||||
|
if (forward->work_counter == 0)
|
||||||
|
result = "ABANDONED";
|
||||||
|
else
|
||||||
|
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||||
|
|
||||||
|
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
|
||||||
|
}
|
||||||
|
|
||||||
no_cache_dnssec = 0;
|
no_cache_dnssec = 0;
|
||||||
|
|
||||||
@@ -906,17 +917,17 @@ void reply_query(int fd, int family, time_t now)
|
|||||||
cache_secure = 1;
|
cache_secure = 1;
|
||||||
else if (status == STAT_BOGUS)
|
else if (status == STAT_BOGUS)
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
|
|
||||||
/* restore CD bit to the value in the query */
|
|
||||||
if (forward->flags & FREC_CHECKING_DISABLED)
|
|
||||||
header->hb4 |= HB4_CD;
|
|
||||||
else
|
|
||||||
header->hb4 &= ~HB4_CD;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* restore CD bit to the value in the query */
|
||||||
|
if (forward->flags & FREC_CHECKING_DISABLED)
|
||||||
|
header->hb4 |= HB4_CD;
|
||||||
|
else
|
||||||
|
header->hb4 &= ~HB4_CD;
|
||||||
|
|
||||||
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
|
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
|
||||||
forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_HAS_SUBNET, &forward->source)))
|
||||||
{
|
{
|
||||||
header->id = htons(forward->orig_id);
|
header->id = htons(forward->orig_id);
|
||||||
header->hb4 |= HB4_RA; /* recursion if available */
|
header->hb4 |= HB4_RA; /* recursion if available */
|
||||||
@@ -1158,8 +1169,9 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
int ad_reqd;
|
||||||
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
|
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
|
||||||
dst_addr_4, netmask, now);
|
dst_addr_4, netmask, now, &ad_reqd);
|
||||||
|
|
||||||
if (m >= 1)
|
if (m >= 1)
|
||||||
{
|
{
|
||||||
@@ -1168,7 +1180,7 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
daemon->local_answer++;
|
daemon->local_answer++;
|
||||||
}
|
}
|
||||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||||
header, (size_t)n, now, NULL))
|
header, (size_t)n, now, NULL, ad_reqd))
|
||||||
daemon->queries_forwarded++;
|
daemon->queries_forwarded++;
|
||||||
else
|
else
|
||||||
daemon->local_answer++;
|
daemon->local_answer++;
|
||||||
@@ -1176,59 +1188,72 @@ void receive_query(struct listener *listen, time_t now)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
static int tcp_key_recurse(time_t now, int status, int class, char *keyname, struct server *server)
|
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)
|
||||||
{
|
{
|
||||||
/* Recurse up the key heirarchy */
|
/* Recurse up the key heirarchy */
|
||||||
size_t n;
|
|
||||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
|
||||||
unsigned char *payload = &packet[2];
|
|
||||||
struct dns_header *header = (struct dns_header *)payload;
|
|
||||||
u16 *length = (u16 *)packet;
|
|
||||||
int new_status;
|
int new_status;
|
||||||
unsigned char c1, c2;
|
|
||||||
|
|
||||||
n = dnssec_generate_query(header, ((char *) header) + 65536, keyname, class,
|
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
|
||||||
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
|
if (--(*keycount) == 0)
|
||||||
|
return STAT_INSECURE;
|
||||||
|
|
||||||
*length = htons(n);
|
if (status == STAT_NEED_KEY)
|
||||||
|
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
|
||||||
if (!read_write(server->tcpfd, packet, n + sizeof(u16), 0) ||
|
else if (status == STAT_NEED_DS)
|
||||||
!read_write(server->tcpfd, &c1, 1, 1) ||
|
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
|
||||||
!read_write(server->tcpfd, &c2, 1, 1) ||
|
|
||||||
!read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
|
|
||||||
{
|
|
||||||
close(server->tcpfd);
|
|
||||||
server->tcpfd = -1;
|
|
||||||
new_status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||||
|
|
||||||
|
/* 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)
|
||||||
{
|
{
|
||||||
n = (c1 << 8) | c2;
|
size_t m;
|
||||||
|
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||||
|
unsigned char *payload = &packet[2];
|
||||||
|
struct dns_header *new_header = (struct dns_header *)payload;
|
||||||
|
u16 *length = (u16 *)packet;
|
||||||
|
unsigned char c1, c2;
|
||||||
|
|
||||||
if (status == STAT_NEED_KEY)
|
if (!packet)
|
||||||
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
return STAT_INSECURE;
|
||||||
|
|
||||||
|
another_tcp_key:
|
||||||
|
m = dnssec_generate_query(new_header, ((char *) new_header) + 65536, keyname, class,
|
||||||
|
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, &server->addr);
|
||||||
|
|
||||||
|
*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))
|
||||||
|
new_status = STAT_INSECURE;
|
||||||
else
|
else
|
||||||
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
|
||||||
|
|
||||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
|
||||||
{
|
{
|
||||||
if ((new_status = tcp_key_recurse(now, new_status, class, daemon->keyname, server) == STAT_SECURE))
|
m = (c1 << 8) | c2;
|
||||||
|
|
||||||
|
if (tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount) == STAT_SECURE)
|
||||||
{
|
{
|
||||||
if (status == STAT_NEED_KEY)
|
/* Reached a validated record, now try again at this level.
|
||||||
new_status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
|
||||||
|
If so, go round again. */
|
||||||
|
|
||||||
|
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
|
else
|
||||||
new_status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, class);
|
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class);
|
||||||
|
|
||||||
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
|
||||||
{
|
goto another_tcp_key;
|
||||||
my_syslog(LOG_ERR, _("Unexpected missing data for DNSSEC validation"));
|
|
||||||
status = STAT_INSECURE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
free(packet);
|
free(packet);
|
||||||
|
}
|
||||||
|
|
||||||
return new_status;
|
return new_status;
|
||||||
}
|
}
|
||||||
@@ -1247,7 +1272,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
int local_auth = 0;
|
int local_auth = 0;
|
||||||
#endif
|
#endif
|
||||||
int checking_disabled, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
int checking_disabled, ad_question, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
|
||||||
size_t m;
|
size_t m;
|
||||||
unsigned short qtype;
|
unsigned short qtype;
|
||||||
unsigned int gotname;
|
unsigned int gotname;
|
||||||
@@ -1283,9 +1308,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
if ((checking_disabled = header->hb4 & HB4_CD))
|
if ((checking_disabled = header->hb4 & HB4_CD))
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
|
|
||||||
/* RFC 4035: sect 4.6 para 2 */
|
|
||||||
header->hb4 &= ~HB4_AD;
|
|
||||||
|
|
||||||
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
|
||||||
{
|
{
|
||||||
#ifdef HAVE_AUTH
|
#ifdef HAVE_AUTH
|
||||||
@@ -1328,7 +1350,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
{
|
{
|
||||||
/* m > 0 if answered from cache */
|
/* m > 0 if answered from cache */
|
||||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||||
dst_addr_4, netmask, now);
|
dst_addr_4, netmask, now, &ad_question);
|
||||||
|
|
||||||
/* Do this by steam now we're not in the select() loop */
|
/* Do this by steam now we're not in the select() loop */
|
||||||
check_log_writer(NULL);
|
check_log_writer(NULL);
|
||||||
@@ -1460,18 +1482,16 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
if (option_bool(OPT_DNSSEC_VALID) && !checking_disabled)
|
||||||
{
|
{
|
||||||
int class, status;
|
int keycount = DNSSEC_WORK; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||||
|
int status = tcp_key_recurse(now, STAT_TRUNCATED, header, m, 0, daemon->namebuff, daemon->keyname, last_server, &keycount);
|
||||||
|
char *result;
|
||||||
|
|
||||||
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
|
if (keycount == 0)
|
||||||
|
result = "ABANDONED";
|
||||||
|
else
|
||||||
|
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
||||||
|
|
||||||
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
|
log_query(F_KEYTAG | F_SECSTAT, "result", NULL, result);
|
||||||
{
|
|
||||||
if ((status = tcp_key_recurse(now, status, class, daemon->keyname, last_server)) == STAT_SECURE)
|
|
||||||
status = dnssec_validate_reply(now, header, m, daemon->namebuff, daemon->keyname, &class);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_query(F_KEYTAG | F_SECSTAT, "result", NULL,
|
|
||||||
status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
|
|
||||||
|
|
||||||
if (status == STAT_BOGUS)
|
if (status == STAT_BOGUS)
|
||||||
no_cache_dnssec = 1;
|
no_cache_dnssec = 1;
|
||||||
@@ -1510,7 +1530,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
|||||||
|
|
||||||
m = process_reply(header, now, last_server, (unsigned int)m,
|
m = process_reply(header, now, last_server, (unsigned int)m,
|
||||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
|
||||||
cache_secure, check_subnet, &peer_addr);
|
cache_secure, ad_question, check_subnet, &peer_addr);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-15
@@ -139,7 +139,7 @@ struct myoption {
|
|||||||
#define LOPT_QUIET_DHCP6 327
|
#define LOPT_QUIET_DHCP6 327
|
||||||
#define LOPT_QUIET_RA 328
|
#define LOPT_QUIET_RA 328
|
||||||
#define LOPT_SEC_VALID 329
|
#define LOPT_SEC_VALID 329
|
||||||
#define LOPT_DNSKEY 330
|
#define LOPT_TRUST_ANCHOR 330
|
||||||
#define LOPT_DNSSEC_DEBUG 331
|
#define LOPT_DNSSEC_DEBUG 331
|
||||||
|
|
||||||
#ifdef HAVE_GETOPT_LONG
|
#ifdef HAVE_GETOPT_LONG
|
||||||
@@ -277,7 +277,7 @@ static const struct myoption opts[] =
|
|||||||
{ "ipset", 1, 0, LOPT_IPSET },
|
{ "ipset", 1, 0, LOPT_IPSET },
|
||||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||||
{ "dnskey", 1, 0, LOPT_DNSKEY },
|
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
|
||||||
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
|
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
|
||||||
#ifdef OPTION6_PREFIX_CLASS
|
#ifdef OPTION6_PREFIX_CLASS
|
||||||
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
||||||
@@ -430,7 +430,7 @@ static struct {
|
|||||||
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
|
||||||
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
|
||||||
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
|
||||||
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), 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_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
|
||||||
#ifdef OPTION6_PREFIX_CLASS
|
#ifdef OPTION6_PREFIX_CLASS
|
||||||
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
||||||
@@ -591,6 +591,16 @@ static int atoi_check16(char *a, int *res)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int atoi_check8(char *a, int *res)
|
||||||
|
{
|
||||||
|
if (!(atoi_check(a, res)) ||
|
||||||
|
*res < 0 ||
|
||||||
|
*res > 0xff)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void add_txt(char *name, char *txt)
|
static void add_txt(char *name, char *txt)
|
||||||
{
|
{
|
||||||
size_t len = strlen(txt);
|
size_t len = strlen(txt);
|
||||||
@@ -3675,10 +3685,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
case LOPT_DNSKEY:
|
case LOPT_TRUST_ANCHOR:
|
||||||
{
|
{
|
||||||
struct dnskey *new = opt_malloc(sizeof(struct dnskey));
|
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
|
||||||
char *key64, *algo = NULL;
|
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
|
||||||
|
int len;
|
||||||
|
|
||||||
new->class = C_IN;
|
new->class = C_IN;
|
||||||
|
|
||||||
@@ -3700,19 +3711,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!comma || !algo || !(key64 = split(algo)) ||
|
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
|
||||||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
|
!atoi_check16(comma, &new->keytag) ||
|
||||||
|
!atoi_check8(algo, &new->algo) ||
|
||||||
|
!atoi_check8(digest, &new->digest_type) ||
|
||||||
!(new->name = canonicalise_opt(arg)))
|
!(new->name = canonicalise_opt(arg)))
|
||||||
ret_err(_("bad DNSKEY"));
|
ret_err(_("bad trust anchor"));
|
||||||
|
|
||||||
/* Upper bound on length */
|
/* Upper bound on length */
|
||||||
new->key = opt_malloc((3*strlen(key64)/4)+1);
|
len = (2*strlen(keyhex))+1;
|
||||||
unhide_metas(key64);
|
new->digest = opt_malloc(len);
|
||||||
if ((new->keylen = parse_base64(key64, new->key)) == -1)
|
unhide_metas(keyhex);
|
||||||
ret_err(_("bad base64 in DNSKEY"));
|
/* 4034: "Whitespace is allowed within digits" */
|
||||||
|
for (cp = keyhex; *cp; )
|
||||||
|
if (isspace(*cp))
|
||||||
|
for (cp1 = cp; *cp1; cp1++)
|
||||||
|
*cp1 = *(cp1+1);
|
||||||
|
else
|
||||||
|
cp++;
|
||||||
|
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
|
||||||
|
ret_err(_("bad HEX in trust anchor"));
|
||||||
|
|
||||||
new->next = daemon->dnskeys;
|
new->next = daemon->ds;
|
||||||
daemon->dnskeys = new;
|
daemon->ds = new;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-57
@@ -1452,7 +1452,8 @@ 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 */
|
/* 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,
|
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)
|
struct in_addr local_addr, struct in_addr local_netmask,
|
||||||
|
time_t now, int *ad_reqd)
|
||||||
{
|
{
|
||||||
char *name = daemon->namebuff;
|
char *name = daemon->namebuff;
|
||||||
unsigned char *p, *ansp, *pheader;
|
unsigned char *p, *ansp, *pheader;
|
||||||
@@ -1468,10 +1469,13 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
struct mx_srv_record *rec;
|
struct mx_srv_record *rec;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
/* Don't return AD set even for local data if checking disabled. */
|
/* Don't return AD set if checking disabled. */
|
||||||
if (header->hb4 & HB4_CD)
|
if (header->hb4 & HB4_CD)
|
||||||
sec_data = 0;
|
sec_data = 0;
|
||||||
|
|
||||||
|
/* RFC 6840 5.7 */
|
||||||
|
*ad_reqd = header->hb4 & HB4_AD;
|
||||||
|
|
||||||
/* If there is an RFC2671 pseudoheader then it will be overwritten by
|
/* 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
|
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
|
the query. We check to see if the do bit is set, if so we always
|
||||||
@@ -1490,6 +1494,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
GETSHORT(flags, pheader);
|
GETSHORT(flags, pheader);
|
||||||
|
|
||||||
sec_reqd = flags & 0x8000; /* do bit */
|
sec_reqd = flags & 0x8000; /* do bit */
|
||||||
|
*ad_reqd = 1;
|
||||||
|
|
||||||
/* If our client is advertising a larger UDP packet size
|
/* If our client is advertising a larger UDP packet size
|
||||||
than we allow, trim it so that we don't get an overlarge
|
than we allow, trim it so that we don't get an overlarge
|
||||||
@@ -1551,37 +1556,33 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
#ifdef HAVE_DNSSEC
|
||||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS || qtype == T_RRSIG))
|
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DNSKEY || qtype == T_DS))
|
||||||
{
|
{
|
||||||
int gotone = 0, have_rrsig = 0;
|
int gotone = 0;
|
||||||
struct blockdata *keydata;
|
struct blockdata *keydata;
|
||||||
|
|
||||||
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
|
/* Do we have RRSIG? Can't do DS or DNSKEY otherwise. */
|
||||||
crecp = NULL;
|
if (sec_reqd)
|
||||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
|
||||||
if (crecp->uid == qclass && (qtype == T_RRSIG || crecp->addr.sig.type_covered == qtype))
|
|
||||||
{
|
|
||||||
have_rrsig = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qtype == T_RRSIG && have_rrsig)
|
|
||||||
{
|
{
|
||||||
ans = gotone = 1;
|
|
||||||
auth = 0;
|
|
||||||
}
|
|
||||||
else if (qtype == T_DS && have_rrsig)
|
|
||||||
{
|
|
||||||
auth = 0;
|
|
||||||
crecp = NULL;
|
crecp = NULL;
|
||||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
||||||
if (crecp->uid == qclass)
|
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype)
|
||||||
{
|
break;
|
||||||
ans = gotone = 1;
|
}
|
||||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
|
|
||||||
|
if (!sec_reqd || crecp)
|
||||||
|
{
|
||||||
|
if (qtype == T_DS)
|
||||||
|
{
|
||||||
|
crecp = NULL;
|
||||||
|
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
|
||||||
|
if (crecp->uid == qclass)
|
||||||
{
|
{
|
||||||
struct all_addr a;
|
gotone = 1;
|
||||||
a.addr.keytag = crecp->addr.ds.keytag;
|
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");
|
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
|
||||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), &nameoffset,
|
crec_ttl(crecp, now), &nameoffset,
|
||||||
@@ -1589,20 +1590,16 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
|
||||||
anscount++;
|
anscount++;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else /* DNSKEY */
|
||||||
else if (qtype == T_DNSKEY)
|
{
|
||||||
{
|
crecp = NULL;
|
||||||
crecp = NULL;
|
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
|
||||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
|
if (crecp->uid == qclass)
|
||||||
if (crecp->uid == qclass)
|
|
||||||
{
|
|
||||||
if ((crecp->flags & F_CONFIG) || have_rrsig) /* Return configured keys without an RRISG */
|
|
||||||
{
|
{
|
||||||
if (!(crecp->flags & F_CONFIG))
|
gotone = 1;
|
||||||
auth = 0, gotone = 1;
|
|
||||||
ans = 1;
|
|
||||||
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
|
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
|
||||||
{
|
{
|
||||||
struct all_addr a;
|
struct all_addr a;
|
||||||
@@ -1615,26 +1612,27 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
anscount++;
|
anscount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now do RRSIGs */
|
/* Now do RRSIGs */
|
||||||
if (gotone)
|
if (gotone)
|
||||||
{
|
{
|
||||||
crecp = NULL;
|
ans = 1;
|
||||||
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
auth = 0;
|
||||||
if (crecp->uid == qclass && (qtype == T_RRSIG || (sec_reqd && crecp->addr.sig.type_covered == qtype)) &&
|
if (!dryrun && sec_reqd)
|
||||||
!dryrun &&
|
{
|
||||||
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
|
crecp = NULL;
|
||||||
{
|
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY | F_DS)))
|
||||||
if (qtype == T_RRSIG)
|
if (crecp->uid == qclass && crecp->addr.sig.type_covered == qtype &&
|
||||||
log_query(F_RRNAME, name, NULL, querystr("rrsig", crecp->addr.sig.type_covered));
|
(keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)))
|
||||||
if ((keydata = blockdata_retrieve(crecp->addr.sig.keydata, crecp->addr.sig.keylen, NULL)) &&
|
{
|
||||||
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||||
crec_ttl(crecp, now), &nameoffset,
|
crec_ttl(crecp, now), &nameoffset,
|
||||||
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata))
|
T_RRSIG, qclass, "t", crecp->addr.sig.keylen, keydata);
|
||||||
anscount++;
|
anscount++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2261,16 +2259,15 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
|||||||
header->nscount = htons(0);
|
header->nscount = htons(0);
|
||||||
header->arcount = htons(addncount);
|
header->arcount = htons(addncount);
|
||||||
|
|
||||||
header->hb4 &= ~HB4_AD;
|
|
||||||
len = ansp - (unsigned char *)header;
|
len = ansp - (unsigned char *)header;
|
||||||
|
|
||||||
if (have_pseudoheader)
|
if (have_pseudoheader)
|
||||||
{
|
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
|
||||||
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
|
|
||||||
if (sec_reqd && sec_data)
|
|
||||||
header->hb4 |= HB4_AD;
|
|
||||||
|
|
||||||
}
|
if (*ad_reqd && sec_data)
|
||||||
|
header->hb4 |= HB4_AD;
|
||||||
|
else
|
||||||
|
header->hb4 &= ~HB4_AD;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|||||||
-60
@@ -482,66 +482,6 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DNSSEC
|
|
||||||
static int charval(char c)
|
|
||||||
{
|
|
||||||
if (c >= 'A' && c <= 'Z')
|
|
||||||
return c - 'A';
|
|
||||||
|
|
||||||
if (c >= 'a' && c <= 'z')
|
|
||||||
return c - 'a' + 26;
|
|
||||||
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
return c - '0' + 52;
|
|
||||||
|
|
||||||
if (c == '+')
|
|
||||||
return 62;
|
|
||||||
|
|
||||||
if (c == '/')
|
|
||||||
return 63;
|
|
||||||
|
|
||||||
if (c == '=')
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int parse_base64(char *in, char *out)
|
|
||||||
{
|
|
||||||
char *p = out;
|
|
||||||
int i, val[4];
|
|
||||||
|
|
||||||
while (*in)
|
|
||||||
{
|
|
||||||
for (i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
while (*in == ' ')
|
|
||||||
in++;
|
|
||||||
if (*in == 0)
|
|
||||||
return -1;
|
|
||||||
if ((val[i] = charval(*in++)) == -2)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*in == ' ')
|
|
||||||
in++;
|
|
||||||
|
|
||||||
if (val[1] == -1)
|
|
||||||
return -1; /* too much padding */
|
|
||||||
|
|
||||||
*p++ = (val[0] << 2) | (val[1] >> 4);
|
|
||||||
|
|
||||||
if (val[2] != -1)
|
|
||||||
*p++ = (val[1] << 4) | ( val[2] >> 2);
|
|
||||||
|
|
||||||
if (val[3] != -1)
|
|
||||||
*p++ = (val[2] << 6) | val[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
return p - out;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* return 0 for no match, or (no matched octets) + 1 */
|
/* return 0 for no match, or (no matched octets) + 1 */
|
||||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
|
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
|
||||||
{
|
{
|
||||||
|
|||||||
+6
-5
@@ -1,8 +1,9 @@
|
|||||||
# The root DNSSEC trust anchors, valid as at 30/01/2014
|
# The root DNSSEC trust anchor, valid as at 30/01/2014
|
||||||
|
|
||||||
|
# Note that this is a DS record (ie a hash of the root Zone Signing Key)
|
||||||
|
# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
|
||||||
|
|
||||||
|
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
|
||||||
|
|
||||||
|
|
||||||
dnskey=.,257,8,AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
|
|
||||||
dnskey=.,256,8,AwEAAb8sU6pbYMWRbkRnEuEZw9NSir707TkOcF+UL1XiK4NDJOvXRyX1 95Am5dQ7bRnnuySZ3daf37vvjUUhuIWUAQ4stht8nJfYxVQXDYjSpGH5 I6Hf/0CZEoNP6cNvrQ7AFmKkmv00xWExKQjbvnRPI4bqpMwtHVzn6Wyb BZ6kuqED
|
|
||||||
dnskey=.,256,8,AwEAAYRU41/8smgAvuSojEP4jaj5Yll7WPaUKpYvnz2pnX2VIvRn4jsy Jns80bloenG6X9ebJVy2CFtZQLKHP8DcKmIFotdgs2HolyocY1am/+33 4RtzusM2ojkhjn1FRGtuSE9s2TSz1ISv0yVnFyu+EP/ZkiWnDfWeVrJI SEWBEr4V
|
|
||||||
|
|
||||||
|
|
||||||
Reference in new issue
Block a user