Compare commits
16 Commits
v2.88test3
...
v2.88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6841ea2e0 | ||
|
|
e939b45c9f | ||
|
|
e3068ed111 | ||
|
|
efbf80be58 | ||
|
|
022ad63f0c | ||
|
|
02f8754339 | ||
|
|
142456cfd0 | ||
|
|
207ce40db2 | ||
|
|
881eaa4dbc | ||
|
|
d6d7527c95 | ||
|
|
11b4be2036 | ||
|
|
3e306c1202 | ||
|
|
7f227a83f2 | ||
|
|
9ed3ee67ec | ||
|
|
1f9215f5f9 | ||
|
|
f52cfdd8c3 |
34
CHANGELOG
34
CHANGELOG
@@ -5,10 +5,10 @@ version 2.88
|
||||
Add --fast-dns-retry option. This gives dnsmasq the ability
|
||||
to originate retries for upstream DNS queries itself, rather
|
||||
than relying on the downstream client. This is most useful
|
||||
when doing DNSSEC over unreliable upstream network. It comes
|
||||
when doing DNSSEC over unreliable upstream networks. It comes
|
||||
with some cost in memory usage and network bandwidth.
|
||||
|
||||
Add -use-stale-cache option. When set, if a DNS name exists
|
||||
Add --use-stale-cache option. When set, if a DNS name exists
|
||||
in the cache, but its time-to-live has expired, dnsmasq will
|
||||
return the data anyway. (It attempts to refresh the
|
||||
data with an upstream query after returning the stale data.)
|
||||
@@ -26,26 +26,44 @@ version 2.88
|
||||
servers being removed from the configuration when reloading
|
||||
server configuration from DBus, or re-reading /etc/resolv.conf
|
||||
Only servers from the same source should be replaced, but some
|
||||
servers from other sources (ie hard coded or another dynamic source)
|
||||
servers from other sources (i.e., hard coded or another dynamic source)
|
||||
could mysteriously disappear. Thanks to all reporting this,
|
||||
but especially Christopher J. Masden who reduced the problem
|
||||
to an easily reproducible case which saved mucg labour in
|
||||
but especially Christopher J. Madsen who reduced the problem
|
||||
to an easily reproducible case which saved much labour in
|
||||
finding it.
|
||||
|
||||
Add --no-round-robin option.
|
||||
|
||||
Allow domain names as well as IP addresses when specifying
|
||||
upstream DNS servers. There are some gotchas associated with this,
|
||||
upstream DNS servers. There are some gotchas associated with this
|
||||
(it will mysteriously fail to work if the dnsmasq instance
|
||||
being started is in the path from the system resolver to the DNS)
|
||||
being started is in the path from the system resolver to the DNS),
|
||||
and a seemingly sensible configuration like
|
||||
--server=domain.name@1.2.3.4 is unactionable if domain.name
|
||||
only resolves to an IPv6 address). There are, however,
|
||||
cases where is can be useful. Thanks to Dominik Derigs for
|
||||
the patch.
|
||||
|
||||
Handle DS records for unsupported crypto algorithms correctly.
|
||||
Such a DS, as long as it is validated, should allow answers
|
||||
in the domain it attests to be returned as unvalidated, and not
|
||||
as a validation error.
|
||||
|
||||
Optimise reading large numbers of --server options. When re-reading
|
||||
upstream servers from /etc/resolv.conf or other sources that
|
||||
can change dnsmasq tries to avoid memory fragmentation by re-using
|
||||
existing records that are being re-read unchanged. This involves
|
||||
seaching all the server records for each new one installed.
|
||||
During startup this search is pointless, and can cause long
|
||||
start times with thousands of --server options because the work
|
||||
needed is O(n^2). Handle this case more intelligently.
|
||||
Thanks to Ye Zhou for spotting the problem and an initial patch.
|
||||
|
||||
(version 2.87
|
||||
If we detect that a DNS reply from upstream is malformed don't
|
||||
return it to the requestor; send a SEVFAIL rcode instead.
|
||||
|
||||
|
||||
version 2.87
|
||||
Allow arbitrary prefix lengths in --rev-server and
|
||||
--domain=....,local
|
||||
|
||||
|
||||
@@ -828,11 +828,12 @@ name on successive queries, for load-balancing. This turns off that
|
||||
behaviour, so that the records are always returned in the order
|
||||
that they are received from upstream.
|
||||
.TP
|
||||
.B --use-stale-cache
|
||||
.B --use-stale-cache[=<max TTL excess in s>]
|
||||
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
|
||||
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
|
||||
of sometimes returning out-of-date data and less efficient cache utilisation, since old data cannot be flushed when its TTL expires, so the cache becomes
|
||||
strictly least-recently-used.
|
||||
mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records can be specified in seconds
|
||||
(defaulting to not serve anything older than one day). Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired.
|
||||
.TP
|
||||
.B \-0, --dns-forward-max=<queries>
|
||||
Set the maximum number of concurrent DNS queries. The default value is
|
||||
@@ -1166,7 +1167,8 @@ given in a
|
||||
.B --dhcp-host
|
||||
option, but aliases are possible by using CNAMEs. (See
|
||||
.B --cname
|
||||
).
|
||||
). Note that /etc/hosts is NOT used when the DNS server side of dnsmasq
|
||||
is disabled by setting the DNS server port to zero.
|
||||
|
||||
More than one
|
||||
.B --dhcp-host
|
||||
|
||||
1525
po/pt_BR.po
1525
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
20
src/cache.c
20
src/cache.c
@@ -380,9 +380,17 @@ static int is_outdated_cname_pointer(struct crec *crecp)
|
||||
|
||||
static int is_expired(time_t now, struct crec *crecp)
|
||||
{
|
||||
/* Don't dump expired entries if we're using them, cache becomes strictly LRU in that case.
|
||||
Never use expired DS or DNSKEY entries. */
|
||||
if (option_bool(OPT_STALE_CACHE) && !(crecp->flags & (F_DS | F_DNSKEY)))
|
||||
/* Don't dump expired entries if they are within the accepted timeout range.
|
||||
The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
|
||||
Possible values for daemon->cache_max_expiry:
|
||||
-1 == serve cached content regardless how long ago it expired
|
||||
0 == the option is disabled, expired content isn't served
|
||||
<n> == serve cached content only if it expire less than <n> seconds
|
||||
ago (where n is a positive integer) */
|
||||
if (daemon->cache_max_expiry != 0 &&
|
||||
(daemon->cache_max_expiry == -1 ||
|
||||
difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
|
||||
!(crecp->flags & (F_DS | F_DNSKEY)))
|
||||
return 0;
|
||||
|
||||
if (crecp->flags & F_IMMORTAL)
|
||||
@@ -1762,7 +1770,7 @@ void dump_cache(time_t now)
|
||||
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
|
||||
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
|
||||
if (option_bool(OPT_STALE_CACHE))
|
||||
if (daemon->cache_max_expiry != 0)
|
||||
my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
|
||||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
|
||||
@@ -1889,8 +1897,10 @@ void dump_cache(time_t now)
|
||||
char *record_source(unsigned int index)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
#ifdef HAVE_INOTIFY
|
||||
struct dyndir *dd;
|
||||
|
||||
#endif
|
||||
|
||||
if (index == SRC_CONFIG)
|
||||
return "config";
|
||||
else if (index == SRC_HOSTS)
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
|
||||
#define LOOP_TEST_TYPE T_TXT
|
||||
#define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */
|
||||
#define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */
|
||||
|
||||
/* compile-time options: uncomment below to enable or do eg.
|
||||
make COPTS=-DHAVE_BROKEN_RTC
|
||||
|
||||
29
src/crypto.c
29
src/crypto.c
@@ -309,14 +309,14 @@ static int dnsmasq_gostdsa_verify(struct blockdata *key_data, unsigned int key_l
|
||||
mpz_init(y);
|
||||
}
|
||||
|
||||
mpz_import(x, 32 , 1, 1, 0, 0, p);
|
||||
mpz_import(y, 32 , 1, 1, 0, 0, p + 32);
|
||||
mpz_import(x, 32, -1, 1, 0, 0, p);
|
||||
mpz_import(y, 32, -1, 1, 0, 0, p + 32);
|
||||
|
||||
if (!ecc_point_set(gost_key, x, y))
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig);
|
||||
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig + 32);
|
||||
mpz_import(sig_struct->s, 32, 1, 1, 0, 0, sig);
|
||||
mpz_import(sig_struct->r, 32, 1, 1, 0, 0, sig + 32);
|
||||
|
||||
return nettle_gostdsa_verify(gost_key, digest_len, digest, sig_struct);
|
||||
}
|
||||
@@ -390,7 +390,12 @@ static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key
|
||||
return dnsmasq_ecdsa_verify;
|
||||
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: case 16:
|
||||
case 15:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 16:
|
||||
return dnsmasq_eddsa_verify;
|
||||
#endif
|
||||
}
|
||||
@@ -425,7 +430,9 @@ char *ds_digest_name(int digest)
|
||||
{
|
||||
case 1: return "sha1";
|
||||
case 2: return "sha256";
|
||||
case 3: return "gosthash94";
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 3: return "gosthash94cp";
|
||||
#endif
|
||||
case 4: return "sha384";
|
||||
default: return NULL;
|
||||
}
|
||||
@@ -444,11 +451,17 @@ char *algo_digest_name(int algo)
|
||||
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
|
||||
case 8: return "sha256"; /* RSA/SHA-256 */
|
||||
case 10: return "sha512"; /* RSA/SHA-512 */
|
||||
case 12: return "gosthash94"; /* ECC-GOST */
|
||||
#if MIN_VERSION(3, 6)
|
||||
case 12: return "gosthash94cp"; /* ECC-GOST */
|
||||
#endif
|
||||
case 13: return "sha256"; /* ECDSAP256SHA256 */
|
||||
case 14: return "sha384"; /* ECDSAP384SHA384 */
|
||||
#if MIN_VERSION(3, 1)
|
||||
case 15: return "null_hash"; /* ED25519 */
|
||||
# if MIN_VERSION(3, 6)
|
||||
case 16: return "null_hash"; /* ED448 */
|
||||
# endif
|
||||
#endif
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,8 +461,8 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
if (sdetails.resolved)
|
||||
freeaddrinfo(sdetails.hostinfo);
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
|
||||
/* jump to next element in outer array */
|
||||
dbus_message_iter_next(&array_iter);
|
||||
|
||||
@@ -722,6 +722,8 @@ static const struct opttab_t opttab6[] = {
|
||||
{ "sntp-server", 31, OT_ADDR_LIST },
|
||||
{ "information-refresh-time", 32, OT_TIME },
|
||||
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
|
||||
{ "posix-timezone", 41, OT_NAME }, /* RFC 4833, Sec. 3 */
|
||||
{ "tzdb-timezone", 42, OT_NAME }, /* RFC 4833, Sec. 3 */
|
||||
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
|
||||
{ "bootfile-url", 59, OT_NAME },
|
||||
{ "bootfile-param", 60, OT_CSTRING },
|
||||
|
||||
@@ -280,9 +280,8 @@ struct event_desc {
|
||||
#define OPT_FILTER_AAAA 68
|
||||
#define OPT_STRIP_ECS 69
|
||||
#define OPT_STRIP_MAC 70
|
||||
#define OPT_STALE_CACHE 71
|
||||
#define OPT_NORR 72
|
||||
#define OPT_LAST 73
|
||||
#define OPT_NORR 71
|
||||
#define OPT_LAST 72
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -1201,6 +1200,7 @@ extern struct daemon {
|
||||
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
|
||||
u32 metrics[__METRIC_MAX];
|
||||
int fast_retry_time, fast_retry_timeout;
|
||||
int cache_max_expiry;
|
||||
#ifdef HAVE_DNSSEC
|
||||
struct ds_config *ds;
|
||||
char *timestamp_file;
|
||||
@@ -1295,9 +1295,9 @@ extern struct daemon {
|
||||
|
||||
struct server_details {
|
||||
union mysockaddr *addr, *source_addr;
|
||||
struct addrinfo *hostinfo;
|
||||
struct addrinfo *hostinfo, *orig_hostinfo;
|
||||
char *interface, *source, *scope_id, *interface_opt;
|
||||
int serv_port, source_port, addr_type, scope_index, valid, resolved;
|
||||
int serv_port, source_port, addr_type, scope_index, valid;
|
||||
u16 *flags;
|
||||
};
|
||||
|
||||
|
||||
59
src/dnssec.c
59
src/dnssec.c
@@ -979,10 +979,13 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
}
|
||||
|
||||
/* The DNS packet is expected to contain the answer to a DS query
|
||||
Put all DSs in the answer which are valid into the cache.
|
||||
Put all DSs in the answer which are valid and have hash and signature algos
|
||||
we support into the cache.
|
||||
Also handles replies which prove that there's no DS at this location,
|
||||
either because the zone is unsigned or this isn't a zone cut. These are
|
||||
cached too.
|
||||
If none of the DS's are for supported algos, treat the answer as if
|
||||
it's a proof of no DS at this location. RFC4035 para 5.2.
|
||||
return codes:
|
||||
STAT_OK At least one valid DS found and in cache.
|
||||
STAT_BOGUS no DS in reply or not signed, fails validation, bad packet.
|
||||
@@ -993,8 +996,8 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)(header+1);
|
||||
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;
|
||||
int aclass, atype, rdlen;
|
||||
int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
|
||||
int aclass, atype, rdlen, flags;
|
||||
unsigned long ttl;
|
||||
union all_addr a;
|
||||
|
||||
@@ -1065,14 +1068,22 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
algo = *p++;
|
||||
digest = *p++;
|
||||
|
||||
if ((key = blockdata_alloc((char*)p, rdlen - 4)))
|
||||
if (!ds_digest_name(digest) || !algo_digest_name(algo))
|
||||
{
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
neg_ttl = ttl;
|
||||
}
|
||||
else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
|
||||
{
|
||||
a.ds.digest = digest;
|
||||
a.ds.keydata = key;
|
||||
a.ds.algo = algo;
|
||||
a.ds.keytag = keytag;
|
||||
a.ds.keylen = rdlen - 4;
|
||||
|
||||
|
||||
if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
|
||||
{
|
||||
blockdata_free(key);
|
||||
@@ -1083,26 +1094,29 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
a.log.keytag = keytag;
|
||||
a.log.algo = algo;
|
||||
a.log.digest = digest;
|
||||
if (ds_digest_name(digest) && algo_digest_name(algo))
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||
else
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
|
||||
log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
|
||||
found_supported = 1;
|
||||
}
|
||||
}
|
||||
|
||||
p = psave;
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, plen, rdlen))
|
||||
return STAT_BOGUS; /* bad packet */
|
||||
}
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
/* Fall through if no supported algo DS found. */
|
||||
if (found_supported)
|
||||
return STAT_OK;
|
||||
}
|
||||
else
|
||||
|
||||
flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
|
||||
|
||||
if (neganswer)
|
||||
{
|
||||
int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
|
||||
|
||||
if (RCODE(header) == NXDOMAIN)
|
||||
flags |= F_NXDOMAIN;
|
||||
|
||||
@@ -1110,17 +1124,18 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
to store presence/absence of NS. */
|
||||
if (nons)
|
||||
flags &= ~F_DNSSECOK;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
|
||||
return STAT_BOGUS;
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||
}
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* Use TTL from NSEC for negative cache entries */
|
||||
if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
|
||||
return STAT_BOGUS;
|
||||
|
||||
cache_end_insert();
|
||||
|
||||
if (neganswer)
|
||||
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
|
||||
|
||||
return STAT_OK;
|
||||
}
|
||||
|
||||
@@ -546,11 +546,23 @@ static int order_qsort(const void *a, const void *b)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* When loading large numbers of server=.... lines during startup,
|
||||
there's no possibility that there will be server records that can be reused, but
|
||||
searching a long list for each server added grows as O(n^2) and slows things down.
|
||||
This flag is set only if is known there may be free server records that can be reused.
|
||||
There's a call to mark_servers(0) in read_opts() to reset the flag before
|
||||
main config read. */
|
||||
|
||||
static int maybe_free_servers = 0;
|
||||
|
||||
/* Must be called before add_update_server() to set daemon->servers_tail */
|
||||
void mark_servers(int flag)
|
||||
{
|
||||
struct server *serv, **up;
|
||||
struct server *serv, *next, **up;
|
||||
|
||||
maybe_free_servers = !!flag;
|
||||
|
||||
daemon->servers_tail = NULL;
|
||||
|
||||
/* mark everything with argument flag */
|
||||
@@ -568,11 +580,13 @@ void mark_servers(int flag)
|
||||
1) numerous and 2) not reloaded often. We just delete
|
||||
and recreate. */
|
||||
if (flag)
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next)
|
||||
for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = next)
|
||||
{
|
||||
next = serv->next;
|
||||
|
||||
if (serv->flags & flag)
|
||||
{
|
||||
*up = serv->next;
|
||||
*up = next;
|
||||
free(serv->domain);
|
||||
free(serv);
|
||||
}
|
||||
@@ -667,27 +681,30 @@ int add_update_server(int flags,
|
||||
and move to the end of the list, for order. The entry found may already
|
||||
be at the end. */
|
||||
struct server **up, *tmp;
|
||||
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
{
|
||||
/* Need to move down? */
|
||||
if (serv->next)
|
||||
{
|
||||
*up = serv->next;
|
||||
daemon->servers_tail->next = serv;
|
||||
daemon->servers_tail = serv;
|
||||
serv->next = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
serv = NULL;
|
||||
|
||||
if (maybe_free_servers)
|
||||
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
|
||||
{
|
||||
tmp = serv->next;
|
||||
if ((serv->flags & SERV_MARK) &&
|
||||
hostname_isequal(alloc_domain, serv->domain))
|
||||
{
|
||||
/* Need to move down? */
|
||||
if (serv->next)
|
||||
{
|
||||
*up = serv->next;
|
||||
daemon->servers_tail->next = serv;
|
||||
daemon->servers_tail = serv;
|
||||
serv->next = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
up = &serv->next;
|
||||
}
|
||||
|
||||
if (serv)
|
||||
{
|
||||
free(alloc_domain);
|
||||
|
||||
@@ -821,12 +821,22 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
n = rrfilter(header, n, RRFILTER_AAAA);
|
||||
}
|
||||
|
||||
if (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||
switch (extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
|
||||
{
|
||||
case 1:
|
||||
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
|
||||
munged = 1;
|
||||
cache_secure = 0;
|
||||
ede = EDE_BLOCKED;
|
||||
break;
|
||||
|
||||
/* extract_addresses() found a malformed answer. */
|
||||
case 2:
|
||||
munged = 1;
|
||||
SET_RCODE(header, SERVFAIL);
|
||||
cache_secure = 0;
|
||||
ede = EDE_OTHER;
|
||||
break;
|
||||
}
|
||||
|
||||
if (doctored)
|
||||
|
||||
72
src/option.c
72
src/option.c
@@ -373,7 +373,7 @@ static const struct myoption opts[] =
|
||||
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
|
||||
{ "port-limit", 1, 0, LOPT_RANDPORT_LIM },
|
||||
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
|
||||
{ "use-stale-cache", 0, 0 , LOPT_STALE_CACHE },
|
||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -431,7 +431,7 @@ static struct {
|
||||
{ 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
|
||||
{ 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
|
||||
{ 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
|
||||
{ LOPT_STALE_CACHE, OPT_STALE_CACHE, NULL, gettext_noop("Use expired cache data for faster reply."), NULL },
|
||||
{ LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
|
||||
{ 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
|
||||
{ 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
|
||||
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
|
||||
@@ -860,11 +860,13 @@ char *parse_server(char *arg, struct server_details *sdetails)
|
||||
sdetails->serv_port = NAMESERVER_PORT;
|
||||
char *portno;
|
||||
int ecode = 0;
|
||||
struct addrinfo hints = { 0 };
|
||||
struct addrinfo hints;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
*sdetails->interface = 0;
|
||||
sdetails->addr_type = AF_UNSPEC;
|
||||
|
||||
|
||||
if (strcmp(arg, "#") == 0)
|
||||
{
|
||||
if (sdetails->flags)
|
||||
@@ -943,7 +945,7 @@ char *parse_server(char *arg, struct server_details *sdetails)
|
||||
above, and returns a pointer to the start of the list in <hostinfo>.
|
||||
The items in the linked list are linked by the <ai_next> field. */
|
||||
sdetails->valid = 1;
|
||||
sdetails->resolved = 1;
|
||||
sdetails->orig_hostinfo = sdetails->hostinfo;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
@@ -967,7 +969,7 @@ char *parse_server_addr(struct server_details *sdetails)
|
||||
sdetails->addr->in.sin_port = htons(sdetails->serv_port);
|
||||
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
|
||||
sdetails->source_addr->in.sin_len = sdetails->addr->in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
sdetails->source_addr->in.sin_port = htons(daemon->query_port);
|
||||
@@ -985,7 +987,7 @@ char *parse_server_addr(struct server_details *sdetails)
|
||||
/* When resolving a server IP by hostname, we can simply skip mismatching
|
||||
server / source IP pairs. Otherwise, when an IP address is given directly,
|
||||
this is a fatal error. */
|
||||
if (!sdetails->resolved)
|
||||
if (!sdetails->orig_hostinfo)
|
||||
return _("cannot use IPv4 server address with IPv6 source address");
|
||||
}
|
||||
else
|
||||
@@ -1016,7 +1018,7 @@ char *parse_server_addr(struct server_details *sdetails)
|
||||
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
|
||||
sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(addr->in6);
|
||||
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(sdetails->addr->in6);
|
||||
#endif
|
||||
if (sdetails->source)
|
||||
{
|
||||
@@ -1031,7 +1033,7 @@ char *parse_server_addr(struct server_details *sdetails)
|
||||
/* When resolving a server IP by hostname, we can simply skip mismatching
|
||||
server / source IP pairs. Otherwise, when an IP address is given directly,
|
||||
this is a fatal error. */
|
||||
if(!sdetails->resolved)
|
||||
if(!sdetails->orig_hostinfo)
|
||||
return _("cannot use IPv6 server address with IPv4 source address");
|
||||
}
|
||||
else
|
||||
@@ -1098,12 +1100,14 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
int count = 1, rem, addrbytes, addrbits;
|
||||
struct server_details sdetails = { 0 };
|
||||
struct server_details sdetails;
|
||||
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
|
||||
if (!server)
|
||||
flags = SERV_LITERAL_ADDRESS;
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
@@ -1161,8 +1165,8 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.resolved)
|
||||
freeaddrinfo(sdetails.hostinfo);
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1179,12 +1183,14 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
int count = 1, rem, addrbytes, addrbits;
|
||||
struct server_details sdetails = { 0 };
|
||||
struct server_details sdetails;
|
||||
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
|
||||
if (!server)
|
||||
flags = SERV_LITERAL_ADDRESS;
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
@@ -1244,8 +1250,8 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.resolved)
|
||||
freeaddrinfo(sdetails.hostinfo);
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2965,13 +2971,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
union all_addr addr;
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
struct server_details sdetails;
|
||||
|
||||
struct server_details sdetails = { 0 };
|
||||
memset(&sdetails, 0, sizeof(struct server_details));
|
||||
sdetails.addr = &serv_addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
/* split the domain args, if any and skip to the end of them. */
|
||||
@@ -3051,8 +3058,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
}
|
||||
|
||||
if (sdetails.resolved)
|
||||
freeaddrinfo(sdetails.hostinfo);
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -5144,6 +5151,24 @@ err:
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_STALE_CACHE:
|
||||
{
|
||||
int max_expiry = STALE_CACHE_EXPIRY;
|
||||
if (arg)
|
||||
{
|
||||
/* Don't accept negative TTLs here, they'd have the counter-intuitive
|
||||
side-effect of evicting cache records before they expire */
|
||||
if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
|
||||
ret_err(gen_err);
|
||||
/* Store "serve expired forever" as -1 internally, the option isn't
|
||||
active for daemon->cache_max_expiry == 0 */
|
||||
if (max_expiry == 0)
|
||||
max_expiry = -1;
|
||||
}
|
||||
daemon->cache_max_expiry = max_expiry;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
|
||||
daemon->timestamp_file = opt_string_alloc(arg);
|
||||
@@ -5746,7 +5771,10 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
#endif
|
||||
add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
|
||||
#endif
|
||||
|
||||
|
||||
/* See comment above make_servers(). Optimises server-read code. */
|
||||
mark_servers(0);
|
||||
|
||||
while (1)
|
||||
{
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
|
||||
@@ -538,7 +538,9 @@ static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||
/* Note that the following code can create CNAME chains that don't point to a real record,
|
||||
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
|
||||
expired and cleaned out that way.
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
|
||||
Return 1 if we reject an address because it look like part of dns-rebinding attack.
|
||||
Return 2 if the packet is malformed.
|
||||
*/
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
|
||||
int no_cache_dnssec, int secure, int *doctored)
|
||||
@@ -589,7 +591,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
namep = p = (unsigned char *)(header+1);
|
||||
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
@@ -607,13 +609,13 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
@@ -651,7 +653,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
@@ -677,7 +679,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,14 +724,14 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
cname_loop1:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
@@ -747,7 +749,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -790,7 +792,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
|
||||
if (qtype != T_CNAME)
|
||||
goto cname_loop1;
|
||||
@@ -813,25 +815,25 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
unsigned char *tmp = namep;
|
||||
|
||||
if (!CHECK_LEN(header, p1, qlen, 6))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
GETSHORT(addr.srv.priority, p1);
|
||||
GETSHORT(addr.srv.weight, p1);
|
||||
GETSHORT(addr.srv.srvport, p1);
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
||||
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
||||
return 0;
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
else if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* copy address into aligned storage */
|
||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
memcpy(&addr, p1, addrlen);
|
||||
|
||||
/* check for returned address in private space */
|
||||
@@ -875,7 +877,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
if (aqtype == T_TXT)
|
||||
{
|
||||
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||
return 0;
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, NULL, aqtype);
|
||||
@@ -883,7 +885,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
|
||||
if (!found && (qtype != T_ANY || (flags & F_NXDOMAIN)))
|
||||
|
||||
Reference in New Issue
Block a user