mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
33
Commits
v2.88test1
...
v2.88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6841ea2e0 | ||
|
|
e939b45c9f | ||
|
|
e3068ed111 | ||
|
|
efbf80be58 | ||
|
|
022ad63f0c | ||
|
|
02f8754339 | ||
|
|
142456cfd0 | ||
|
|
207ce40db2 | ||
|
|
881eaa4dbc | ||
|
|
d6d7527c95 | ||
|
|
11b4be2036 | ||
|
|
3e306c1202 | ||
|
|
7f227a83f2 | ||
|
|
9ed3ee67ec | ||
|
|
1f9215f5f9 | ||
|
|
f52cfdd8c3 | ||
|
|
2fc904111d | ||
|
|
262dadf50e | ||
|
|
6c596f1cc1 | ||
|
|
dafa16c400 | ||
|
|
1db9943c68 | ||
|
|
5b868c213b | ||
|
|
2d8905dafd | ||
|
|
9002108551 | ||
|
|
d3c21c596e | ||
|
|
34fac952b6 | ||
|
|
92c32e0bac | ||
|
|
1bcad67806 | ||
|
|
fe9a134baf | ||
|
|
930428fb97 | ||
|
|
936be022d9 | ||
|
|
0017dd74d5 | ||
|
|
0ba25a0512 |
No files matched your search
@@ -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.)
|
||||
@@ -18,6 +18,50 @@ version 2.88
|
||||
flushed when its TTL expires, so the cache becomes
|
||||
strictly least-recently-used.
|
||||
|
||||
Make --hostsdir (but NOT --dhcp-hostsdir and --dhcp-optsdir)
|
||||
handle removal of whole files or entries within files.
|
||||
Thanks to Dominik Derigs for the initial patches for this.
|
||||
|
||||
Fix bug, introduced in 2.87, which could result in DNS
|
||||
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 (i.e., hard coded or another dynamic source)
|
||||
could mysteriously disappear. Thanks to all reporting this,
|
||||
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
|
||||
(it will mysteriously fail to work if the dnsmasq instance
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
Vendored
+8
@@ -1,3 +1,11 @@
|
||||
dnsmasq (2.88-1) unstable; urgency=low
|
||||
|
||||
* New upstream.
|
||||
* Fix loss of server configuration (closes: #1020830)
|
||||
Git commit 930428fb970f4991e5c2933fd5a5d2504c18a551
|
||||
|
||||
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 2 Nov 2022 22:15:45 +0000
|
||||
|
||||
dnsmasq (2.87-1) unstable; urgency=low
|
||||
|
||||
* New upstream. (closes: #1001209, #1003156)
|
||||
|
||||
+22
-7
@@ -60,7 +60,8 @@ in alphabetical order.
|
||||
.TP
|
||||
.B --hostsdir=<path>
|
||||
Read all the hosts files contained in the directory. New or changed files
|
||||
are read automatically. See \fB--dhcp-hostsdir\fP for details.
|
||||
are read automatically and modified and deleted files have removed records
|
||||
automatically deleted.
|
||||
.TP
|
||||
.B \-E, --expand-hosts
|
||||
Add the domain to simple names (without a period) in /etc/hosts
|
||||
@@ -461,8 +462,8 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
|
||||
or domain parts, to upstream nameservers. If the name is not known
|
||||
from /etc/hosts or DHCP then a "not found" answer is returned.
|
||||
.TP
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
|
||||
Specify IP address of upstream servers directly. Setting this flag does
|
||||
.B \-S, --local, --server=[/[<domain>]/[domain/]][<server>[#<port>]][@<interface>][@<source-ip>[#<port>]]
|
||||
Specify upstream servers directly. Setting this flag does
|
||||
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
|
||||
optional domains are given, that server is used only for those domains
|
||||
and they are queried only using the specified server. This is
|
||||
@@ -530,8 +531,14 @@ The query-port flag is ignored for any servers which have a
|
||||
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.
|
||||
|
||||
Upstream servers may be specified with a hostname rather than an IP address.
|
||||
In this case, dnsmasq will try to use the system resolver to get the IP address
|
||||
of a server during startup. If name resolution fails, starting dnsmasq fails, too.
|
||||
If the system's configuration is such that the system resolver sends DNS queries
|
||||
through the dnsmasq instance which is starting up then this will time-out and fail.
|
||||
.TP
|
||||
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
||||
.B --rev-server=<ip-address>[/<prefix-len>][,<server>][#<port>][@<interface>][@<source-ip>[#<port>]]
|
||||
This is functionally the same as
|
||||
.B --server,
|
||||
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
|
||||
@@ -815,11 +822,18 @@ Disable negative caching. Negative caching allows dnsmasq to remember
|
||||
"no such domain" answers from upstream nameservers and answer
|
||||
identical queries without forwarding them again.
|
||||
.TP
|
||||
.B --use-stale-cache
|
||||
.B --no-round-robin
|
||||
Dnsmasq normally permutes the order of A or AAAA records for the same
|
||||
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[=<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
|
||||
@@ -1153,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
|
||||
|
||||
+846
-679
File diff suppressed because it is too large.
Load diff
+63
-22
@@ -189,7 +189,7 @@ static void rehash(int size)
|
||||
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
|
||||
return;
|
||||
|
||||
for(i = 0; i < new_size; i++)
|
||||
for (i = 0; i < new_size; i++)
|
||||
new[i] = NULL;
|
||||
|
||||
old = hash_table;
|
||||
@@ -233,7 +233,8 @@ static void cache_hash(struct crec *crecp)
|
||||
immortal entries are at the end of the hash-chain.
|
||||
This allows reverse searches and garbage collection to be optimised */
|
||||
|
||||
struct crec **up = hash_bucket(cache_get_name(crecp));
|
||||
char *name = cache_get_name(crecp);
|
||||
struct crec **up = hash_bucket(name);
|
||||
|
||||
if (!(crecp->flags & F_REVERSE))
|
||||
{
|
||||
@@ -244,6 +245,11 @@ static void cache_hash(struct crec *crecp)
|
||||
while (*up && !((*up)->flags & F_IMMORTAL))
|
||||
up = &((*up)->hash_next);
|
||||
}
|
||||
|
||||
/* Preserve order when inserting the same name multiple times. */
|
||||
while (*up && hostname_isequal(cache_get_name(*up), name))
|
||||
up = &((*up)->hash_next);
|
||||
|
||||
crecp->hash_next = *up;
|
||||
*up = crecp;
|
||||
}
|
||||
@@ -374,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)
|
||||
@@ -388,6 +402,27 @@ static int is_expired(time_t now, struct crec *crecp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Remove entries with a given UID from the cache */
|
||||
unsigned int cache_remove_uid(const unsigned int uid)
|
||||
{
|
||||
int i;
|
||||
unsigned int removed = 0;
|
||||
struct crec *crecp, **up;
|
||||
|
||||
for (i = 0; i < hash_size; i++)
|
||||
for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
|
||||
if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
free(crecp);
|
||||
removed++;
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
|
||||
unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
|
||||
{
|
||||
@@ -701,7 +736,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
|
||||
new->ttd = now + (time_t)ttl;
|
||||
new->next = new_chain;
|
||||
new_chain = new;
|
||||
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
@@ -879,7 +914,7 @@ int cache_find_non_terminal(char *name, time_t now)
|
||||
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
|
||||
{
|
||||
struct crec *ans;
|
||||
int no_rr = prot & F_NO_RR;
|
||||
int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
|
||||
|
||||
prot &= ~F_NO_RR;
|
||||
|
||||
@@ -1027,16 +1062,17 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
|
||||
static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
|
||||
unsigned int index, struct crec **rhash, int hashsz)
|
||||
{
|
||||
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
|
||||
int i;
|
||||
unsigned int j;
|
||||
struct crec *lookup = NULL;
|
||||
|
||||
/* Remove duplicates in hosts files. */
|
||||
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
{
|
||||
free(cache);
|
||||
return;
|
||||
}
|
||||
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
|
||||
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
|
||||
{
|
||||
free(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure there is only one address -> name mapping (first one trumps)
|
||||
We do this by steam here, The entries are kept in hash chains, linked
|
||||
@@ -1141,7 +1177,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
char *token = daemon->namebuff, *domain_suffix = NULL;
|
||||
int addr_count = 0, name_count = cache_size, lineno = 1;
|
||||
int names_done = 0, name_count = cache_size, lineno = 1;
|
||||
unsigned int flags = 0;
|
||||
union all_addr addr;
|
||||
int atnl, addrlen = 0;
|
||||
@@ -1177,8 +1213,6 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
continue;
|
||||
}
|
||||
|
||||
addr_count++;
|
||||
|
||||
/* rehash every 1000 names. */
|
||||
if (rhash && ((name_count - cache_size) > 1000))
|
||||
{
|
||||
@@ -1210,6 +1244,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
|
||||
{
|
||||
@@ -1218,6 +1253,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
cache->ttd = daemon->local_ttl;
|
||||
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
|
||||
name_count++;
|
||||
names_done++;
|
||||
}
|
||||
free(canon);
|
||||
|
||||
@@ -1234,7 +1270,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
if (rhash)
|
||||
rehash(name_count);
|
||||
|
||||
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
|
||||
my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
|
||||
|
||||
return name_count;
|
||||
}
|
||||
@@ -1734,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]);
|
||||
@@ -1861,7 +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)
|
||||
@@ -1872,9 +1911,11 @@ char *record_source(unsigned int index)
|
||||
return ah->fname;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
if (ah->index == index)
|
||||
return ah->fname;
|
||||
/* Dynamic directories contain multiple files */
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
for (ah = dd->files; ah; ah = ah->next)
|
||||
if (ah->index == index)
|
||||
return ah->fname;
|
||||
#endif
|
||||
|
||||
return "<unknown>";
|
||||
|
||||
@@ -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
|
||||
|
||||
+21
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+61
-17
@@ -292,6 +292,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
u16 flags = 0;
|
||||
char interface[IF_NAMESIZE];
|
||||
char *str_addr, *str_domain = NULL;
|
||||
struct server_details sdetails = { 0 };
|
||||
sdetails.addr = &addr;
|
||||
sdetails.source_addr = &source_addr;
|
||||
sdetails.interface = interface;
|
||||
sdetails.flags = &flags;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
@@ -374,20 +379,6 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
strcpy(str_addr, str);
|
||||
}
|
||||
|
||||
/* parse the IP address */
|
||||
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_LITERAL_ADDRESS;
|
||||
|
||||
if (strings)
|
||||
{
|
||||
char *p;
|
||||
@@ -401,7 +392,31 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
else
|
||||
p = NULL;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
if (strings && strlen(str_addr) == 0)
|
||||
add_update_server(SERV_LITERAL_ADDRESS | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
else
|
||||
{
|
||||
if ((addr_err = parse_server(str_addr, &sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((addr_err = parse_server_addr(&sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain, NULL);
|
||||
}
|
||||
}
|
||||
} while ((str_domain = p));
|
||||
}
|
||||
else
|
||||
@@ -415,11 +430,40 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
|
||||
if (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING)
|
||||
dbus_message_iter_get_basic(&string_iter, &str);
|
||||
dbus_message_iter_next (&string_iter);
|
||||
|
||||
if ((addr_err = parse_server(str_addr, &sdetails)))
|
||||
{
|
||||
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s': %s",
|
||||
str, addr_err);
|
||||
break;
|
||||
}
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((addr_err = parse_server_addr(&sdetails)))
|
||||
{
|
||||
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_LITERAL_ADDRESS;
|
||||
else
|
||||
flags &= ~SERV_LITERAL_ADDRESS;
|
||||
|
||||
add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str, NULL);
|
||||
}
|
||||
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
|
||||
}
|
||||
|
||||
|
||||
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 },
|
||||
|
||||
+26
-5
@@ -133,6 +133,7 @@ typedef unsigned long long u64;
|
||||
#include <sys/uio.h>
|
||||
#include <syslog.h>
|
||||
#include <dirent.h>
|
||||
#include <netdb.h>
|
||||
#ifndef HAVE_LINUX_NETWORK
|
||||
# include <net/if_dl.h>
|
||||
#endif
|
||||
@@ -279,7 +280,7 @@ 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 71
|
||||
#define OPT_LAST 72
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
@@ -687,10 +688,17 @@ struct hostsfile {
|
||||
struct hostsfile *next;
|
||||
int flags;
|
||||
char *fname;
|
||||
unsigned int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
struct dyndir {
|
||||
struct dyndir *next;
|
||||
struct hostsfile *files;
|
||||
int flags;
|
||||
char *dname;
|
||||
#ifdef HAVE_INOTIFY
|
||||
int wd; /* inotify watch descriptor */
|
||||
#endif
|
||||
unsigned int index; /* matches to cache entries for logging */
|
||||
};
|
||||
|
||||
/* packet-dump flags */
|
||||
@@ -1152,6 +1160,7 @@ extern struct daemon {
|
||||
u32 umbrella_org;
|
||||
u32 umbrella_asset;
|
||||
u8 umbrella_device[8];
|
||||
int host_index;
|
||||
struct hostsfile *addn_hosts;
|
||||
struct dhcp_context *dhcp, *dhcp6;
|
||||
struct ra_interface *ra_interfaces;
|
||||
@@ -1172,7 +1181,8 @@ extern struct daemon {
|
||||
int doing_ra, doing_dhcp6;
|
||||
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
|
||||
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
|
||||
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
|
||||
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
|
||||
struct dyndir *dynamic_dirs;
|
||||
int dhcp_max, tftp_max, tftp_mtu;
|
||||
int dhcp_server_port, dhcp_client_port;
|
||||
int start_tftp_port, end_tftp_port;
|
||||
@@ -1190,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;
|
||||
@@ -1282,6 +1293,14 @@ extern struct daemon {
|
||||
#endif
|
||||
} *daemon;
|
||||
|
||||
struct server_details {
|
||||
union mysockaddr *addr, *source_addr;
|
||||
struct addrinfo *hostinfo, *orig_hostinfo;
|
||||
char *interface, *source, *scope_id, *interface_opt;
|
||||
int serv_port, source_port, addr_type, scope_index, valid;
|
||||
u16 *flags;
|
||||
};
|
||||
|
||||
/* cache.c */
|
||||
void cache_init(void);
|
||||
void next_uid(struct crec *crecp);
|
||||
@@ -1295,6 +1314,7 @@ struct crec *cache_find_by_name(struct crec *crecp,
|
||||
char *name, time_t now, unsigned int prot);
|
||||
void cache_end_insert(void);
|
||||
void cache_start_insert(void);
|
||||
unsigned int cache_remove_uid(const unsigned int uid);
|
||||
int cache_recv_insert(time_t now, int fd);
|
||||
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
@@ -1456,8 +1476,9 @@ 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);
|
||||
char *parse_server(char *arg, union mysockaddr *addr,
|
||||
union mysockaddr *source_addr, char *interface, u16 *flags);
|
||||
char *parse_server(char *arg, struct server_details *sdetails);
|
||||
char *parse_server_addr(struct server_details *sdetails);
|
||||
int parse_server_next(struct server_details *sdetails);
|
||||
int option_read_dynfile(char *file, int flags);
|
||||
|
||||
/* forward.c */
|
||||
|
||||
+37
-22
@@ -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;
|
||||
}
|
||||
|
||||
+40
-21
@@ -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,25 +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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
+11
-1
@@ -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)
|
||||
|
||||
+92
-45
@@ -133,81 +133,112 @@ void inotify_dnsmasq_init()
|
||||
}
|
||||
}
|
||||
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
|
||||
{
|
||||
/* Check if this file is already known in dd->files */
|
||||
struct hostsfile *ah = NULL;
|
||||
for(ah = dd->files; ah; ah = ah->next)
|
||||
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
|
||||
return ah;
|
||||
|
||||
/* Not known, create new hostsfile record for this dyndir */
|
||||
struct hostsfile *newah = NULL;
|
||||
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
|
||||
return NULL;
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
newah->next = dd->files;
|
||||
dd->files = newah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
newah->flags = dd->flags;
|
||||
newah->index = daemon->host_index++;
|
||||
newah->fname = path;
|
||||
|
||||
return newah;
|
||||
}
|
||||
|
||||
|
||||
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
|
||||
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
struct dyndir *dd;
|
||||
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
{
|
||||
DIR *dir_stream = NULL;
|
||||
struct dirent *ent;
|
||||
struct stat buf;
|
||||
|
||||
if (!(ah->flags & flag))
|
||||
|
||||
if (!(dd->flags & flag))
|
||||
continue;
|
||||
|
||||
if (stat(ah->fname, &buf) == -1)
|
||||
|
||||
if (stat(dd->dname, &buf) == -1)
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
dd->dname, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(S_ISDIR(buf.st_mode)))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
|
||||
ah->fname, _("not a directory"));
|
||||
dd->dname, _("not a directory"));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ah->flags & AH_WD_DONE))
|
||||
|
||||
if (!(dd->flags & AH_WD_DONE))
|
||||
{
|
||||
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
|
||||
ah->flags |= AH_WD_DONE;
|
||||
dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
|
||||
dd->flags |= AH_WD_DONE;
|
||||
}
|
||||
|
||||
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
|
||||
a race which misses files being added as we start */
|
||||
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
|
||||
if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
dd->dname, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((ent = readdir(dir_stream)))
|
||||
{
|
||||
size_t lendir = strlen(ah->fname);
|
||||
size_t lendir = strlen(dd->dname);
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
char *path;
|
||||
|
||||
|
||||
/* ignore emacs backups and dotfiles */
|
||||
if (lenfile == 0 ||
|
||||
ent->d_name[lenfile - 1] == '~' ||
|
||||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
|
||||
ent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
|
||||
if ((path = whine_malloc(lendir + lenfile + 2)))
|
||||
{
|
||||
strcpy(path, ah->fname);
|
||||
struct hostsfile *ah;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
if (!(ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
{
|
||||
if (ah->flags & AH_HOSTS)
|
||||
if (dd->flags & AH_HOSTS)
|
||||
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
|
||||
#ifdef HAVE_DHCP
|
||||
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
option_read_dynfile(path, ah->flags);
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
option_read_dynfile(path, dd->flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +249,7 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
int inotify_check(time_t now)
|
||||
{
|
||||
int hit = 0;
|
||||
struct hostsfile *ah;
|
||||
struct dyndir *dd;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -249,36 +280,51 @@ int inotify_check(time_t now)
|
||||
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
|
||||
hit = 1;
|
||||
|
||||
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
|
||||
if (ah->wd == in->wd)
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
if (dd->wd == in->wd)
|
||||
{
|
||||
size_t lendir = strlen(ah->fname);
|
||||
size_t lendir = strlen(dd->dname);
|
||||
char *path;
|
||||
|
||||
|
||||
if ((path = whine_malloc(lendir + in->len + 2)))
|
||||
{
|
||||
strcpy(path, ah->fname);
|
||||
struct hostsfile *ah = NULL;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
|
||||
|
||||
if (ah->flags & AH_HOSTS)
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
if ((ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (ah->flags & AH_DHCP_HST)
|
||||
else if (dd->flags & AH_DHCP_HST)
|
||||
{
|
||||
if (option_read_dynfile(path, AH_DHCP_HST))
|
||||
{
|
||||
@@ -289,11 +335,12 @@ int inotify_check(time_t now)
|
||||
lease_update_dns(1);
|
||||
}
|
||||
}
|
||||
else if (ah->flags & AH_DHCP_OPT)
|
||||
else if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
#endif
|
||||
|
||||
free(path);
|
||||
if (!ah)
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+369
-142
@@ -184,6 +184,7 @@ struct myoption {
|
||||
#define LOPT_RANDPORT_LIM 375
|
||||
#define LOPT_FAST_RETRY 376
|
||||
#define LOPT_STALE_CACHE 377
|
||||
#define LOPT_NORR 378
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -236,6 +237,7 @@ static const struct myoption opts[] =
|
||||
{ "localmx", 0, 0, 'L' },
|
||||
{ "local-ttl", 1, 0, 'T' },
|
||||
{ "no-negcache", 0, 0, 'N' },
|
||||
{ "no-round-robin", 0, 0, LOPT_NORR },
|
||||
{ "addn-hosts", 1, 0, 'H' },
|
||||
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
|
||||
{ "query-port", 1, 0, 'Q' },
|
||||
@@ -371,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 }
|
||||
};
|
||||
|
||||
@@ -429,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},
|
||||
@@ -567,6 +569,7 @@ static struct {
|
||||
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
|
||||
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
|
||||
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
|
||||
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -852,117 +855,241 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
|
||||
char *parse_server(char *arg, struct server_details *sdetails)
|
||||
{
|
||||
int source_port = 0, serv_port = NAMESERVER_PORT;
|
||||
char *portno, *source;
|
||||
char *interface_opt = NULL;
|
||||
int scope_index = 0;
|
||||
char *scope_id;
|
||||
|
||||
*interface = 0;
|
||||
sdetails->serv_port = NAMESERVER_PORT;
|
||||
char *portno;
|
||||
int ecode = 0;
|
||||
struct addrinfo hints;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
*sdetails->interface = 0;
|
||||
sdetails->addr_type = AF_UNSPEC;
|
||||
|
||||
if (strcmp(arg, "#") == 0)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_USE_RESOLV;
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_USE_RESOLV;
|
||||
sdetails->addr_type = AF_LOCAL;
|
||||
sdetails->valid = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(source, '#')) &&
|
||||
!atoi_check16(portno, &source_port))
|
||||
if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
|
||||
(portno = split_chr(sdetails->source, '#')) &&
|
||||
!atoi_check16(portno, &sdetails->source_port))
|
||||
return _("bad port");
|
||||
|
||||
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
|
||||
!atoi_check16(portno, &serv_port))
|
||||
!atoi_check16(portno, &sdetails->serv_port))
|
||||
return _("bad port");
|
||||
|
||||
scope_id = split_chr(arg, '%');
|
||||
sdetails->scope_id = split_chr(arg, '%');
|
||||
|
||||
if (source) {
|
||||
interface_opt = split_chr(source, '@');
|
||||
if (sdetails->source) {
|
||||
sdetails->interface_opt = split_chr(sdetails->source, '@');
|
||||
|
||||
if (interface_opt)
|
||||
if (sdetails->interface_opt)
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
source = interface_opt;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
sdetails->source = sdetails->interface_opt;
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
|
||||
if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
|
||||
sdetails->addr_type = AF_INET;
|
||||
else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
|
||||
sdetails->addr_type = AF_INET6;
|
||||
else
|
||||
{
|
||||
addr->in.sin_port = htons(serv_port);
|
||||
addr->sa.sa_family = 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);
|
||||
/* if the argument is neither an IPv4 not an IPv6 address, it might be a
|
||||
hostname and we should try to resolve it to a suitable address. */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
|
||||
the result only if the local system has at least one IPv4 address
|
||||
configured, and IPv6 addresses are returned only if the local system
|
||||
has at least one IPv6 address configured. The loopback address is not
|
||||
considered for this case as valid as a configured address. This flag is
|
||||
useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
|
||||
does not return IPv6 socket addresses that would always fail in
|
||||
subsequent connect() or bind() attempts. */
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#if defined(HAVE_IDN) && defined(AI_IDN)
|
||||
/* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
|
||||
the node name given in node is converted to IDN format if necessary.
|
||||
The source encoding is that of the current locale. */
|
||||
hints.ai_flags |= AI_IDN;
|
||||
#endif
|
||||
source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
source_addr->in.sin_port = htons(daemon->query_port);
|
||||
|
||||
if (source)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_HAS_SOURCE;
|
||||
source_addr->in.sin_port = htons(source_port);
|
||||
if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
|
||||
{
|
||||
if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
|
||||
return _("bad interface name");
|
||||
|
||||
addr->in6.sin6_port = htons(serv_port);
|
||||
addr->in6.sin6_scope_id = scope_index;
|
||||
source_addr->in6.sin6_addr = in6addr_any;
|
||||
source_addr->in6.sin6_port = htons(daemon->query_port);
|
||||
source_addr->in6.sin6_scope_id = 0;
|
||||
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
|
||||
addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
|
||||
#endif
|
||||
if (source)
|
||||
{
|
||||
if (flags)
|
||||
*flags |= SERV_HAS_SOURCE;
|
||||
source_addr->in6.sin6_port = htons(source_port);
|
||||
if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
source_addr->in6.sin6_addr = in6addr_any;
|
||||
safe_strncpy(interface, source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return _("bad address");
|
||||
/* The value AF_UNSPEC indicates that getaddrinfo() should return socket
|
||||
addresses for any address family (either IPv4 or IPv6, for example)
|
||||
that can be used with node <arg> and service "domain". */
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
/* Get addresses suitable for sending datagrams. We assume that we can use the
|
||||
same addresses for TCP connections. Settting this to zero gets each address
|
||||
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
/* Get address associated with this hostname */
|
||||
ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
|
||||
if (ecode == 0)
|
||||
{
|
||||
/* The getaddrinfo() function allocated and initialized a linked list of
|
||||
addrinfo structures, one for each network address that matches node
|
||||
and service, subject to the restrictions imposed by our <hints>
|
||||
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->orig_hostinfo = sdetails->hostinfo;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Lookup failed, return human readable error string */
|
||||
if (ecode == EAI_AGAIN)
|
||||
return _("Cannot resolve server name");
|
||||
else
|
||||
return _((char*)gai_strerror(ecode));
|
||||
}
|
||||
}
|
||||
|
||||
sdetails->valid = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *parse_server_addr(struct server_details *sdetails)
|
||||
{
|
||||
if (sdetails->addr_type == AF_INET)
|
||||
{
|
||||
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
|
||||
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);
|
||||
|
||||
if (sdetails->source)
|
||||
{
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_HAS_SOURCE;
|
||||
sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
|
||||
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
|
||||
{
|
||||
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
|
||||
{
|
||||
sdetails->source_addr->sa.sa_family = AF_INET6;
|
||||
/* 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->orig_hostinfo)
|
||||
return _("cannot use IPv4 server address with IPv6 source address");
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (sdetails->interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sdetails->addr_type == AF_INET6)
|
||||
{
|
||||
if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
|
||||
return _("bad interface name");
|
||||
|
||||
sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
|
||||
sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
|
||||
sdetails->source_addr->in6.sin6_addr = in6addr_any;
|
||||
sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
|
||||
sdetails->source_addr->in6.sin6_scope_id = 0;
|
||||
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(sdetails->addr->in6);
|
||||
#endif
|
||||
if (sdetails->source)
|
||||
{
|
||||
if (sdetails->flags)
|
||||
*sdetails->flags |= SERV_HAS_SOURCE;
|
||||
sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
|
||||
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
|
||||
{
|
||||
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
|
||||
{
|
||||
sdetails->source_addr->sa.sa_family = AF_INET;
|
||||
/* 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->orig_hostinfo)
|
||||
return _("cannot use IPv6 server address with IPv4 source address");
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (sdetails->interface_opt)
|
||||
return _("interface can only be specified once");
|
||||
|
||||
sdetails->source_addr->in6.sin6_addr = in6addr_any;
|
||||
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
|
||||
#else
|
||||
return _("interface binding not supported");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sdetails->addr_type != AF_LOCAL)
|
||||
return _("bad address");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int parse_server_next(struct server_details *sdetails)
|
||||
{
|
||||
/* Looping over resolved addresses? */
|
||||
if (sdetails->hostinfo)
|
||||
{
|
||||
/* Get address type */
|
||||
sdetails->addr_type = sdetails->hostinfo->ai_family;
|
||||
|
||||
/* Get address */
|
||||
if (sdetails->addr_type == AF_INET)
|
||||
memcpy(&sdetails->addr->in.sin_addr,
|
||||
&((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
|
||||
sizeof(sdetails->addr->in.sin_addr));
|
||||
else if (sdetails->addr_type == AF_INET6)
|
||||
memcpy(&sdetails->addr->in6.sin6_addr,
|
||||
&((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
|
||||
sizeof(sdetails->addr->in6.sin6_addr));
|
||||
|
||||
/* Iterate to the next available address */
|
||||
sdetails->valid = sdetails->hostinfo->ai_next != NULL;
|
||||
sdetails->hostinfo = sdetails->hostinfo->ai_next;
|
||||
return 1;
|
||||
}
|
||||
else if (sdetails->valid)
|
||||
{
|
||||
/* When using an IP address, we return the address only once */
|
||||
sdetails->valid = 0;
|
||||
return 1;
|
||||
}
|
||||
/* Stop iterating here, we used all available addresses */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
|
||||
{
|
||||
int i, j;
|
||||
@@ -973,22 +1100,29 @@ 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;
|
||||
|
||||
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, &serv_addr, &source_addr, interface, &flags)))
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
return string;
|
||||
|
||||
|
||||
if (from_file)
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
|
||||
rem = size & 0x7;
|
||||
addrbytes = (32 - size) >> 3;
|
||||
addrbits = (32 - size) & 7;
|
||||
|
||||
if (size > 32 || size < 1)
|
||||
return _("bad IPv4 prefix length");
|
||||
|
||||
|
||||
/* Zero out last address bits according to CIDR mask */
|
||||
((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
|
||||
|
||||
@@ -1002,23 +1136,40 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
|
||||
*domain = 0;
|
||||
string = domain;
|
||||
msize = size/8;
|
||||
|
||||
|
||||
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
|
||||
{
|
||||
int dig = ((unsigned char *)addr4)[j];
|
||||
|
||||
|
||||
if (j == msize)
|
||||
dig += i;
|
||||
|
||||
|
||||
string += sprintf(string, "%d.", dig);
|
||||
}
|
||||
|
||||
|
||||
sprintf(string, "in-addr.arpa");
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((string = parse_server_addr(&sdetails)))
|
||||
return string;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1032,10 +1183,17 @@ 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;
|
||||
|
||||
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, &serv_addr, &source_addr, interface, &flags)))
|
||||
else if ((string = parse_server(server, &sdetails)))
|
||||
return string;
|
||||
|
||||
if (from_file)
|
||||
@@ -1076,10 +1234,27 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
|
||||
|
||||
sprintf(string, "ip6.arpa");
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
else
|
||||
{
|
||||
while (parse_server_next(&sdetails))
|
||||
{
|
||||
if ((string = parse_server_addr(&sdetails)))
|
||||
return string;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
|
||||
return _("error");
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2162,15 +2337,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
|
||||
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
|
||||
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
|
||||
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
|
||||
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
|
||||
case LOPT_HOST_INOTIFY: /* --hostsdir */
|
||||
case 'H': /* --addn-hosts */
|
||||
{
|
||||
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
|
||||
static unsigned int hosts_index = SRC_AH;
|
||||
new->fname = opt_string_alloc(arg);
|
||||
new->index = hosts_index++;
|
||||
new->index = daemon->host_index++;
|
||||
new->flags = 0;
|
||||
if (option == 'H')
|
||||
{
|
||||
@@ -2186,21 +2357,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
{
|
||||
new->next = daemon->dhcp_opts_file;
|
||||
daemon->dhcp_opts_file = new;
|
||||
}
|
||||
else
|
||||
{
|
||||
new->next = daemon->dynamic_dirs;
|
||||
daemon->dynamic_dirs = new;
|
||||
if (option == LOPT_DHCP_INOTIFY)
|
||||
new->flags |= AH_DHCP_HST;
|
||||
else if (option == LOPT_DHOPT_INOTIFY)
|
||||
new->flags |= AH_DHCP_OPT;
|
||||
else if (option == LOPT_HOST_INOTIFY)
|
||||
new->flags |= AH_HOSTS;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
|
||||
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
|
||||
case LOPT_HOST_INOTIFY: /* --hostsdir */
|
||||
{
|
||||
struct dyndir *new = opt_malloc(sizeof(struct dyndir));
|
||||
new->dname = opt_string_alloc(arg);
|
||||
new->flags = 0;
|
||||
new->next = daemon->dynamic_dirs;
|
||||
daemon->dynamic_dirs = new;
|
||||
if (option == LOPT_DHCP_INOTIFY)
|
||||
new->flags |= AH_DHCP_HST;
|
||||
else if (option == LOPT_DHOPT_INOTIFY)
|
||||
new->flags |= AH_DHCP_OPT;
|
||||
else if (option == LOPT_HOST_INOTIFY)
|
||||
new->flags |= AH_HOSTS;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_AUTHSERV: /* --auth-server */
|
||||
comma = split(arg);
|
||||
@@ -2786,13 +2965,20 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
case LOPT_LOCAL: /* --local */
|
||||
case 'A': /* --address */
|
||||
{
|
||||
char *lastdomain = NULL, *domain = "";
|
||||
char *lastdomain = NULL, *domain = "", *cur_domain;
|
||||
u16 flags = 0;
|
||||
char *err;
|
||||
union all_addr addr;
|
||||
union mysockaddr serv_addr, source_addr;
|
||||
char interface[IF_NAMESIZE+1];
|
||||
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;
|
||||
|
||||
unhide_metas(arg);
|
||||
|
||||
/* split the domain args, if any and skip to the end of them. */
|
||||
@@ -2825,36 +3011,55 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
|
||||
if ((err = parse_server(arg, &sdetails)))
|
||||
ret_err(err);
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
flags |= SERV_FROM_FILE;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (lastdomain)
|
||||
{
|
||||
if (strlen(domain) == 0)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
else
|
||||
flags &= ~SERV_FOR_NODOTS;
|
||||
|
||||
/* address=/#/ matches the same as without domain */
|
||||
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
|
||||
domain[0] = 0;
|
||||
cur_domain = domain;
|
||||
while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
|
||||
{
|
||||
cur_domain = domain;
|
||||
|
||||
if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
|
||||
ret_err(err);
|
||||
|
||||
/* When source is set only use DNS records of the same type and skip all others */
|
||||
if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
|
||||
continue;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (lastdomain)
|
||||
{
|
||||
if (strlen(cur_domain) == 0)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
else
|
||||
flags &= ~SERV_FOR_NODOTS;
|
||||
|
||||
/* address=/#/ matches the same as without domain */
|
||||
if (option == 'A' && cur_domain[0] == '#' && cur_domain[1] == 0)
|
||||
cur_domain[0] = 0;
|
||||
}
|
||||
|
||||
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, cur_domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!lastdomain || cur_domain == lastdomain)
|
||||
break;
|
||||
|
||||
cur_domain += strlen(cur_domain) + 1;
|
||||
}
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
if (!lastdomain || domain == lastdomain)
|
||||
|
||||
if (flags & SERV_LITERAL_ADDRESS)
|
||||
break;
|
||||
|
||||
domain += strlen(domain) + 1;
|
||||
}
|
||||
|
||||
if (sdetails.orig_hostinfo)
|
||||
freeaddrinfo(sdetails.orig_hostinfo);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -4946,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);
|
||||
@@ -5532,7 +5755,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
daemon->soa_retry = SOA_RETRY;
|
||||
daemon->soa_expiry = SOA_EXPIRY;
|
||||
daemon->randport_limit = 1;
|
||||
|
||||
daemon->host_index = SRC_AH;
|
||||
|
||||
#ifndef NO_ID
|
||||
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
|
||||
add_txt("authors.bind", "Simon Kelley", 0);
|
||||
@@ -5547,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
|
||||
|
||||
+18
-16
@@ -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)))
|
||||
|
||||
+8
-1
@@ -1153,15 +1153,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
tagif_netid = run_tag_if(&context->netid);
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
apply_delay(mess->xid, recvtime, tagif_netid);
|
||||
|
||||
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
|
||||
{
|
||||
rapid_commit = 1;
|
||||
/* If a lease exists for this host and another address, squash it. */
|
||||
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
|
||||
{
|
||||
lease_prune(lease, now);
|
||||
lease = NULL;
|
||||
}
|
||||
goto rapid_commit;
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
|
||||
daemon->metrics[METRIC_DHCPOFFER]++;
|
||||
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
|
||||
|
||||
|
||||
Reference in new issue
Block a user