-/* dnsmasq is Copyright (c) 2000-2007 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2015 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 dated June, 1991.
-
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
-static struct crec *dhcp_spare = NULL, *new_chain = NULL;
+#ifdef HAVE_DHCP
+static struct crec *dhcp_spare = NULL;
+#endif
+static struct crec *new_chain = NULL;
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
-static int uid = 0;
-static char *addrbuff = NULL;
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
{ 25, "KEY" },
{ 28, "AAAA" },
{ 33, "SRV" },
+ { 35, "NAPTR" },
{ 36, "KX" },
{ 37, "CERT" },
{ 38, "A6" },
{ 39, "DNAME" },
{ 41, "OPT" },
+ { 43, "DS" },
+ { 46, "RRSIG" },
+ { 47, "NSEC" },
{ 48, "DNSKEY" },
+ { 50, "NSEC3" },
{ 249, "TKEY" },
{ 250, "TSIG" },
{ 251, "IXFR" },
static void cache_free(struct crec *crecp);
static void cache_unlink(struct crec *crecp);
static void cache_link(struct crec *crecp);
-static char *record_source(struct hostsfile *add_hosts, int index);
static void rehash(int size);
static void cache_hash(struct crec *crecp);
+static unsigned int next_uid(void)
+{
+ static unsigned int uid = 0;
+
+ uid++;
+
+ /* uid == 0 used to indicate CNAME to interface name. */
+ if (uid == SRC_INTERFACE)
+ uid++;
+
+ return uid;
+}
+
void cache_init(void)
{
struct crec *crecp;
int i;
-
- if (daemon->options & OPT_LOG)
- addrbuff = safe_malloc(ADDRSTRLEN);
-
+
bignames_left = daemon->cachesize/10;
if (daemon->cachesize > 0)
{
cache_link(crecp);
crecp->flags = 0;
- crecp->uid = uid++;
+ crecp->uid = next_uid();
}
}
crecp->hash_next = *up;
*up = crecp;
}
-
+
+#ifdef HAVE_DNSSEC
+static void cache_blockdata_free(struct crec *crecp)
+{
+ if (crecp->flags & F_DNSKEY)
+ {
+ if (crecp->flags & F_DS)
+ blockdata_free(crecp->addr.sig.keydata);
+ else
+ blockdata_free(crecp->addr.key.keydata);
+ }
+ else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
+ blockdata_free(crecp->addr.ds.keydata);
+}
+#endif
+
static void cache_free(struct crec *crecp)
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
- crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
-
+ crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
+
if (cache_tail)
cache_tail->next = crecp;
else
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
+
+#ifdef HAVE_DNSSEC
+ cache_blockdata_free(crecp);
+#endif
}
/* insert a new cache entry at the head of the list (youngest entry) */
{
if (crecp->flags & F_BIGNAME)
return crecp->name.bname->name;
- else if (crecp->flags & F_DHCP)
+ else if (crecp->flags & F_NAMEP)
return crecp->name.namep;
return crecp->name.sname;
}
+char *cache_get_cname_target(struct crec *crecp)
+{
+ if (crecp->addr.cname.uid != SRC_INTERFACE)
+ return cache_get_name(crecp->addr.cname.target.cache);
+
+ return crecp->addr.cname.target.int_name->name;
+}
+
+
+
+struct crec *cache_enumerate(int init)
+{
+ static int bucket;
+ static struct crec *cache;
+
+ if (init)
+ {
+ bucket = 0;
+ cache = NULL;
+ }
+ else if (cache && cache->hash_next)
+ cache = cache->hash_next;
+ else
+ {
+ cache = NULL;
+ while (bucket < hash_size)
+ if ((cache = hash_table[bucket++]))
+ break;
+ }
+
+ return cache;
+}
+
static int is_outdated_cname_pointer(struct crec *crecp)
{
- if (!(crecp->flags & F_CNAME))
+ if (!(crecp->flags & F_CNAME) || crecp->addr.cname.uid == SRC_INTERFACE)
return 0;
- if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
+ /* NB. record may be reused as DS or DNSKEY, where uid is
+ overloaded for something completely different */
+ if (crecp->addr.cname.target.cache &&
+ (crecp->addr.cname.target.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
+ crecp->addr.cname.uid == crecp->addr.cname.target.cache->uid)
return 0;
return 1;
return 1;
}
-static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
+static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
{
/* Scan and remove old entries.
If (flags & F_FORWARD) then remove any forward entries for name and any expired
entries in the whole cache.
If (flags == 0) remove any expired entries in the whole cache.
- In the flags & F_FORWARD case, the return code is valid, and returns zero if the
- name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
+ In the flags & F_FORWARD case, the return code is valid, and returns a non-NULL pointer
+ to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
so that when we hit an entry which isn't reverse and is immortal, we're done. */
if (flags & F_FORWARD)
{
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
- if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
- {
- *up = crecp->hash_next;
- if (!(crecp->flags & (F_HOSTS | F_DHCP)))
- {
- cache_unlink(crecp);
- cache_free(crecp);
- }
- }
- else if ((crecp->flags & F_FORWARD) &&
- ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
- hostname_isequal(cache_get_name(crecp), name))
- {
- if (crecp->flags & (F_HOSTS | F_DHCP))
- return 0;
- *up = crecp->hash_next;
- cache_unlink(crecp);
- cache_free(crecp);
- }
- else
+ {
+ if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
+ {
+ *up = crecp->hash_next;
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
+ {
+ cache_unlink(crecp);
+ cache_free(crecp);
+ }
+ continue;
+ }
+
+ if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
+ {
+ /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
+ if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) ||
+ (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
+ {
+ if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
+ return crecp;
+ *up = crecp->hash_next;
+ cache_unlink(crecp);
+ cache_free(crecp);
+ continue;
+ }
+
+#ifdef HAVE_DNSSEC
+ /* Deletion has to be class-sensitive for DS, DNSKEY, RRSIG, also
+ type-covered sensitive for RRSIG */
+ if ((flags & (F_DNSKEY | F_DS)) &&
+ (flags & (F_DNSKEY | F_DS)) == (crecp->flags & (F_DNSKEY | F_DS)) &&
+ crecp->uid == addr->addr.dnssec.class &&
+ (!((flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY)) ||
+ crecp->addr.sig.type_covered == addr->addr.dnssec.type))
+ {
+ if (crecp->flags & F_CONFIG)
+ return crecp;
+ *up = crecp->hash_next;
+ cache_unlink(crecp);
+ cache_free(crecp);
+ continue;
+ }
+#endif
+ }
up = &crecp->hash_next;
+ }
}
else
{
if (is_expired(now, crecp))
{
*up = crecp->hash_next;
- if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
{
cache_unlink(crecp);
cache_free(crecp);
}
}
- else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
+ else if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) &&
(flags & crecp->flags & F_REVERSE) &&
(flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
up = &crecp->hash_next;
}
- return 1;
+ return NULL;
}
/* Note: The normal calling sequence is
struct crec *new;
union bigname *big_name = NULL;
int freed_all = flags & F_REVERSE;
+ int free_avail = 0;
- log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
-
- /* CONFIG bit no needed except for logging */
- flags &= ~F_CONFIG;
+ /* Don't log DNSSEC records here, done elsewhere */
+ if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
+ {
+ log_query(flags | F_UPSTREAM, name, addr, NULL);
+ /* Don't mess with TTL for DNSSEC records. */
+ if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
+ ttl = daemon->max_cache_ttl;
+ if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
+ ttl = daemon->min_cache_ttl;
+ }
/* if previous insertion failed give up now. */
if (insert_error)
return NULL;
-
+
/* First remove any expired entries and entries for the name/address we
- are currently inserting. Fail is we attempt to delete a name from
- /etc/hosts or DHCP. */
- if (!cache_scan_free(name, addr, now, flags))
+ are currently inserting. */
+ if ((new = cache_scan_free(name, addr, now, flags)))
{
+ /* We're trying to insert a record over one from
+ /etc/hosts or DHCP, or other config. If the
+ existing record is for an A or AAAA and
+ the record we're trying to insert is the same,
+ just drop the insert, but don't error the whole process. */
+ if ((flags & (F_IPV4 | F_IPV6)) && (flags & F_FORWARD))
+ {
+ if ((flags & F_IPV4) && (new->flags & F_IPV4) &&
+ new->addr.addr.addr.addr4.s_addr == addr->addr.addr4.s_addr)
+ return new;
+#ifdef HAVE_IPV6
+ else if ((flags & F_IPV6) && (new->flags & F_IPV6) &&
+ IN6_ARE_ADDR_EQUAL(&new->addr.addr.addr.addr6, &addr->addr.addr6))
+ return new;
+#endif
+ }
+
insert_error = 1;
return NULL;
}
if (new->flags & (F_FORWARD | F_REVERSE))
{
+ /* If free_avail set, we believe that an entry has been freed.
+ Bugs have been known to make this not true, resulting in
+ a tight loop here. If that happens, abandon the
+ insert. Once in this state, all inserts will probably fail. */
+ if (free_avail)
+ {
+ static int warned = 0;
+ if (!warned)
+ {
+ my_syslog(LOG_ERR, _("Internal error in cache."));
+ warned = 1;
+ }
+ insert_error = 1;
+ return NULL;
+ }
+
if (freed_all)
{
- cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
+ struct all_addr free_addr = new->addr.addr;;
+
+#ifdef HAVE_DNSSEC
+ /* For DNSSEC records, addr holds class and type_covered for RRSIG */
+ if (new->flags & (F_DS | F_DNSKEY))
+ {
+ free_addr.addr.dnssec.class = new->uid;
+ if ((new->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
+ free_addr.addr.dnssec.type = new->addr.sig.type_covered;
+ }
+#endif
+
+ free_avail = 1; /* Must be free space now. */
+ cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
cache_live_freed++;
}
else
}
/* Check if we need to and can allocate extra memory for a long name.
- If that fails, give up now. */
+ If that fails, give up now, always succeed for DNSSEC records. */
if (name && (strlen(name) > SMALLDNAME-1))
{
if (big_free)
big_name = big_free;
big_free = big_free->next;
}
- else if (!bignames_left ||
+ else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
!(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
{
insert_error = 1;
return NULL;
}
- else
+ else if (bignames_left != 0)
bignames_left--;
}
*cache_get_name(new) = 0;
if (addr)
- new->addr.addr = *addr;
- else
- new->addr.cname.cache = NULL;
-
+ {
+#ifdef HAVE_DNSSEC
+ if (flags & (F_DS | F_DNSKEY))
+ new->uid = addr->addr.dnssec.class;
+ else
+#endif
+ new->addr.addr = *addr;
+ }
+
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
new_chain = NULL;
}
-struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
+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;
+ prot &= ~F_NO_RR;
+
if (crecp) /* iterating */
ans = crecp->next;
else
/* first search, look for relevant entries and push to top of list
also free anything which has expired */
struct crec *next, **up, **insert = NULL, **chainp = &ans;
-
+ unsigned short ins_flags = 0;
+
for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
{
next = crecp->hash_next;
if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
{
if ((crecp->flags & F_FORWARD) &&
+#ifdef HAVE_DNSSEC
+ (((crecp->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
+#endif
(crecp->flags & prot) &&
hostname_isequal(cache_get_name(crecp), name))
{
- if (crecp->flags & (F_HOSTS | F_DHCP))
+ if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
{
*chainp = crecp;
chainp = &crecp->next;
cache_link(crecp);
}
- /* move all but the first entry up the hash chain
- this implements round-robin */
- if (!insert)
- {
- insert = up;
- up = &crecp->hash_next;
- }
- else
+ /* Move all but the first entry up the hash chain
+ this implements round-robin.
+ Make sure that re-ordering doesn't break the hash-chain
+ order invariants.
+ */
+ if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
{
*up = crecp->hash_next;
crecp->hash_next = *insert;
*insert = crecp;
insert = &crecp->hash_next;
}
+ else
+ {
+ if (!insert && !no_rr)
+ {
+ insert = up;
+ ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
+ }
+ up = &crecp->hash_next;
+ }
}
else
/* case : not expired, incorrect entry. */
{
/* expired entry, free it */
*up = crecp->hash_next;
- if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
{
cache_unlink(crecp);
cache_free(crecp);
if (ans &&
(ans->flags & F_FORWARD) &&
- (ans->flags & prot) &&
+#ifdef HAVE_DNSSEC
+ (((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) || (prot & F_NSIGMATCH)) &&
+#endif
+ (ans->flags & prot) &&
hostname_isequal(cache_get_name(ans), name))
return ans;
}
struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
- time_t now, unsigned short prot)
+ time_t now, unsigned int prot)
{
struct crec *ans;
#ifdef HAVE_IPV6
if ((crecp->flags & prot) &&
memcmp(&crecp->addr.addr, addr, addrlen) == 0)
{
- if (crecp->flags & (F_HOSTS | F_DHCP))
+ if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
{
*chainp = crecp;
chainp = &crecp->next;
else
{
*up = crecp->hash_next;
- if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
{
cache_unlink(crecp);
cache_free(crecp);
return NULL;
}
-static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
- unsigned short flags, int index, int addr_dup)
+static void add_hosts_cname(struct crec *target)
{
- struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
- int i;
+ struct crec *crec;
+ struct cname *a;
+ for (a = daemon->cnames; a; a = a->next)
+ if (hostname_isequal(cache_get_name(target), a->target) &&
+ (crec = whine_malloc(sizeof(struct crec))))
+ {
+ crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME;
+ crec->name.namep = a->alias;
+ crec->addr.cname.target.cache = target;
+ crec->addr.cname.uid = target->uid;
+ crec->uid = next_uid();
+ cache_hash(crec);
+ add_hosts_cname(crec); /* handle chains */
+ }
+}
+
+static void add_hosts_entry(struct crec *cache, struct 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, nameexists = 0;
+ unsigned int j;
+
/* Remove duplicates in hosts files. */
- if (lookup && (lookup->flags & F_HOSTS) &&
- memcmp(&lookup->addr.addr, addr, addrlen) == 0)
- free(cache);
- else
+ if (lookup && (lookup->flags & F_HOSTS))
{
- /* Ensure there is only one address -> name mapping (first one trumps)
- We do this by steam here, first we see if the address is the same as
- the last one we saw, which eliminates most in the case of an ad-block
- file with thousands of entries for the same address.
- Then we search and bail at the first matching address that came from
- a HOSTS file. Since the first host entry gets reverse, we know
- then that it must exist without searching exhaustively for it. */
-
- if (addr_dup)
- flags &= ~F_REVERSE;
- else
- for (i=0; i<hash_size; i++)
+ nameexists = 1;
+ if (memcmp(&lookup->addr.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
+ by ->next (which is unused at this point) held in hash buckets in
+ the array rhash, hashed on address. Note that rhash and the values
+ in ->next are only valid whilst reading hosts files: the buckets are
+ then freed, and the ->next pointer used for other things.
+
+ Only insert each unique address once into this hashing structure.
+
+ This complexity avoids O(n^2) divergent CPU use whilst reading
+ large (10000 entry) hosts files.
+
+ Note that we only do this process when bulk-reading hosts files,
+ for incremental reads, rhash is NULL, and we use cache lookups
+ instead.
+ */
+
+ if (rhash)
+ {
+ /* hash address */
+ for (j = 0, i = 0; i < addrlen; i++)
+ j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
+
+ for (lookup = rhash[j]; lookup; lookup = lookup->next)
+ if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
+ memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
- for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
- if ((lookup->flags & F_HOSTS) &&
- (lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
- memcmp(&lookup->addr.addr, addr, addrlen) == 0)
- {
- flags &= ~F_REVERSE;
- break;
- }
- if (lookup)
- break;
+ cache->flags &= ~F_REVERSE;
+ break;
}
- cache->flags = flags;
- cache->uid = index;
- memcpy(&cache->addr.addr, addr, addrlen);
- cache_hash(cache);
+ /* maintain address hash chain, insert new unique address */
+ if (!lookup)
+ {
+ cache->next = rhash[j];
+ rhash[j] = cache;
+ }
+ }
+ else
+ {
+ /* incremental read, lookup in cache */
+ lookup = cache_find_by_addr(NULL, addr, 0, cache->flags & (F_IPV4 | F_IPV6));
+ if (lookup && lookup->flags & F_HOSTS)
+ cache->flags &= ~F_REVERSE;
+ }
+
+ cache->uid = index;
+ memcpy(&cache->addr.addr, addr, addrlen);
+ cache_hash(cache);
+
+ /* don't need to do alias stuff for second and subsequent addresses. */
+ if (!nameexists)
+ add_hosts_cname(cache);
+}
+
+static int eatspace(FILE *f)
+{
+ int c, nl = 0;
+
+ while (1)
+ {
+ if ((c = getc(f)) == '#')
+ while (c != '\n' && c != EOF)
+ c = getc(f);
+
+ if (c == EOF)
+ return 1;
+
+ if (!isspace(c))
+ {
+ ungetc(c, f);
+ return nl;
+ }
+
+ if (c == '\n')
+ nl = 1;
+ }
+}
+
+static int gettok(FILE *f, char *token)
+{
+ int c, count = 0;
+
+ while (1)
+ {
+ if ((c = getc(f)) == EOF)
+ return (count == 0) ? EOF : 1;
+
+ if (isspace(c) || c == '#')
+ {
+ ungetc(c, f);
+ return eatspace(f);
+ }
+
+ if (count < (MAXDNAME - 1))
+ {
+ token[count++] = c;
+ token[count] = 0;
+ }
}
}
-static int read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index, int cache_size)
+int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz)
{
FILE *f = fopen(filename, "r");
- char *line;
+ char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 0;
- unsigned short flags, saved_flags = 0;
- struct all_addr addr, saved_addr;
+ unsigned short flags = 0;
+ struct all_addr addr;
+ int atnl, addrlen = 0;
if (!f)
{
my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
return 0;
}
-
- while ((line = fgets(buff, MAXDNAME, f)))
+
+ eatspace(f);
+
+ while ((atnl = gettok(f, token)) != EOF)
{
- char *token = strtok(line, " \t\n\r");
- int addrlen, addr_dup = 0;
-
lineno++;
-
- if (!token || (*token == '#'))
- continue;
-
-#ifdef HAVE_IPV6
+
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
+ domain_suffix = get_domain(addr.addr.addr4);
}
+#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
- }
-#else
- if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
- {
- flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
- addrlen = INADDRSZ;
+ domain_suffix = get_domain6(&addr.addr.addr6);
}
#endif
else
{
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
+ while (atnl == 0)
+ atnl = gettok(f, token);
continue;
}
+
+ addr_count++;
+
+ /* rehash every 1000 names. */
+ if (rhash && ((name_count - cache_size) > 1000))
+ {
+ rehash(name_count);
+ cache_size = name_count;
+ }
+
+ while (atnl == 0)
+ {
+ struct crec *cache;
+ int fqdn, nomem;
+ char *canon;
+
+ if ((atnl = gettok(f, token)) == EOF)
+ break;
- if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
- addr_dup = 1;
- else
- {
- saved_flags = flags;
- saved_addr = addr;
- }
-
- addr_count++;
-
- /* rehash every 1000 names. */
- if ((name_count - cache_size) > 1000)
- {
- rehash(name_count);
- cache_size = name_count;
- }
-
- while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
- {
- struct crec *cache;
- int fqdn = !!strchr(token, '.');
- if (canonicalise(token))
- {
- /* If set, add a version of the name with a default domain appended */
- if ((opts & OPT_EXPAND) && domain_suffix && !fqdn &&
- (cache = whine_malloc(sizeof(struct crec) +
- strlen(token)+2+strlen(domain_suffix)-SMALLDNAME)))
- {
- strcpy(cache->name.sname, token);
- strcat(cache->name.sname, ".");
- strcat(cache->name.sname, domain_suffix);
- add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
- addr_dup = 1;
- name_count++;
- }
- if ((cache = whine_malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
- {
- strcpy(cache->name.sname, token);
- add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
- name_count++;
- }
- }
- else
- my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
- }
- }
-
- fclose(f);
- rehash(name_count);
+ fqdn = !!strchr(token, '.');
- my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
+ if ((canon = canonicalise(token, &nomem)))
+ {
+ /* If set, add a version of the name with a default domain appended */
+ if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
+ (cache = whine_malloc(sizeof(struct crec) +
+ strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
+ {
+ strcpy(cache->name.sname, canon);
+ strcat(cache->name.sname, ".");
+ strcat(cache->name.sname, domain_suffix);
+ cache->flags = flags;
+ add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
+ name_count++;
+ }
+ if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
+ {
+ strcpy(cache->name.sname, canon);
+ cache->flags = flags;
+ add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
+ name_count++;
+ }
+ free(canon);
+
+ }
+ else if (!nomem)
+ my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno);
+ }
+ }
+ fclose(f);
+
+ if (rhash)
+ rehash(name_count);
+
+ my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
+
return name_count;
}
-void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
+void cache_reload(void)
{
struct crec *cache, **up, *tmp;
- int i, total_size = daemon->cachesize;
+ int revhashsz, i, total_size = daemon->cachesize;
+ struct hostsfile *ah;
+ struct host_record *hr;
+ struct name_list *nl;
+ struct cname *a;
+ struct interface_name *intr;
+#ifdef HAVE_DNSSEC
+ struct ds_config *ds;
+#endif
cache_inserted = cache_live_freed = 0;
for (i=0; i<hash_size; i++)
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
{
+#ifdef HAVE_DNSSEC
+ cache_blockdata_free(cache);
+#endif
tmp = cache->hash_next;
- if (cache->flags & F_HOSTS)
+ if (cache->flags & (F_HOSTS | F_CONFIG))
{
*up = cache->hash_next;
free(cache);
up = &cache->hash_next;
}
- if ((opts & OPT_NO_HOSTS) && !addn_hosts)
+ /* Add CNAMEs to interface_names to the cache */
+ for (a = daemon->cnames; a; a = a->next)
+ for (intr = daemon->int_names; intr; intr = intr->next)
+ if (hostname_isequal(a->target, intr->name) &&
+ ((cache = whine_malloc(sizeof(struct crec)))))
+ {
+ cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG;
+ cache->name.namep = a->alias;
+ cache->addr.cname.target.int_name = intr;
+ cache->addr.cname.uid = SRC_INTERFACE;
+ cache->uid = next_uid();
+ cache_hash(cache);
+ add_hosts_cname(cache); /* handle chains */
+ }
+
+#ifdef HAVE_DNSSEC
+ for (ds = daemon->ds; ds; ds = ds->next)
+ if ((cache = whine_malloc(sizeof(struct crec))) &&
+ (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
+ {
+ cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
+ cache->name.namep = ds->name;
+ cache->addr.ds.keylen = ds->digestlen;
+ cache->addr.ds.algo = ds->algo;
+ cache->addr.ds.keytag = ds->keytag;
+ cache->addr.ds.digest = ds->digest_type;
+ cache->uid = ds->class;
+ cache_hash(cache);
+ }
+#endif
+
+ /* borrow the packet buffer for a temporary by-address hash */
+ memset(daemon->packet, 0, daemon->packet_buff_sz);
+ revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
+ /* we overwrote the buffer... */
+ daemon->srv_save = NULL;
+
+ /* Do host_records in config. */
+ for (hr = daemon->host_records; hr; hr = hr->next)
+ for (nl = hr->names; nl; nl = nl->next)
+ {
+ if (hr->addr.s_addr != 0 &&
+ (cache = whine_malloc(sizeof(struct crec))))
+ {
+ cache->name.namep = nl->name;
+ cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
+ add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+ }
+#ifdef HAVE_IPV6
+ if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
+ (cache = whine_malloc(sizeof(struct crec))))
+ {
+ cache->name.namep = nl->name;
+ cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
+ add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz);
+ }
+#endif
+ }
+
+ if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
{
if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache"));
- return;
}
-
- if (!(opts & OPT_NO_HOSTS))
- total_size = read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0, total_size);
- while (addn_hosts)
+ else
{
- total_size = read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index, total_size);
- addn_hosts = addn_hosts->next;
- }
+ if (!option_bool(OPT_NO_HOSTS))
+ total_size = read_hostsfile(HOSTSFILE, SRC_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+
+ daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
+ for (ah = daemon->addn_hosts; ah; ah = ah->next)
+ if (!(ah->flags & AH_INACTIVE))
+ total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
+ }
+
+#ifdef HAVE_INOTIFY
+ set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz);
+#endif
+
}
+#ifdef HAVE_DHCP
+struct in_addr a_record_from_hosts(char *name, time_t now)
+{
+ struct crec *crecp = NULL;
+ struct in_addr ret;
+
+ while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
+ if (crecp->flags & F_HOSTS)
+ return *(struct in_addr *)&crecp->addr;
+
+ my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
+
+ ret.s_addr = 0;
+ return ret;
+}
+
void cache_unhash_dhcp(void)
{
struct crec *cache, **up;
up = &cache->hash_next;
}
-void cache_add_dhcp_entry(char *host_name,
- struct in_addr *host_address, time_t ttd)
+static void add_dhcp_cname(struct crec *target, time_t ttd)
{
- struct crec *crec;
- unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
+ struct crec *aliasc;
+ struct cname *a;
+
+ for (a = daemon->cnames; a; a = a->next)
+ if (hostname_isequal(cache_get_name(target), a->target))
+ {
+ if ((aliasc = dhcp_spare))
+ dhcp_spare = dhcp_spare->next;
+ else /* need new one */
+ aliasc = whine_malloc(sizeof(struct crec));
+
+ if (aliasc)
+ {
+ aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME | F_CONFIG;
+ if (ttd == 0)
+ aliasc->flags |= F_IMMORTAL;
+ else
+ aliasc->ttd = ttd;
+ aliasc->name.namep = a->alias;
+ aliasc->addr.cname.target.cache = target;
+ aliasc->addr.cname.uid = target->uid;
+ aliasc->uid = next_uid();
+ cache_hash(aliasc);
+ add_dhcp_cname(aliasc, ttd);
+ }
+ }
+}
- if (!host_name)
- return;
+void cache_add_dhcp_entry(char *host_name, int prot,
+ struct all_addr *host_address, time_t ttd)
+{
+ struct crec *crec = NULL, *fail_crec = NULL;
+ unsigned short flags = F_IPV4;
+ int in_hosts = 0;
+ size_t addrlen = sizeof(struct in_addr);
- if ((crec = cache_find_by_name(NULL, host_name, 0, F_IPV4 | F_CNAME)))
+#ifdef HAVE_IPV6
+ if (prot == AF_INET6)
{
- if (crec->flags & F_HOSTS)
+ flags = F_IPV6;
+ addrlen = sizeof(struct in6_addr);
+ }
+#endif
+
+ inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
+
+ while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
+ {
+ /* check all addresses associated with name */
+ if (crec->flags & (F_HOSTS | F_CONFIG))
{
- if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
- {
- strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
- my_syslog(LOG_WARNING,
- _("not giving name %s to the DHCP lease of %s because "
- "the name exists in %s with address %s"),
- host_name, inet_ntoa(*host_address),
- record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
- }
- return;
+ if (crec->flags & F_CNAME)
+ my_syslog(MS_DHCP | LOG_WARNING,
+ _("%s is a CNAME, not giving it to the DHCP lease of %s"),
+ host_name, daemon->addrbuff);
+ else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)
+ in_hosts = 1;
+ else
+ fail_crec = crec;
}
else if (!(crec->flags & F_DHCP))
- cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
+ {
+ cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
+ /* scan_free deletes all addresses associated with name */
+ break;
+ }
}
-
- if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
+
+ /* if in hosts, don't need DHCP record */
+ if (in_hosts)
+ return;
+
+ /* Name in hosts, address doesn't match */
+ if (fail_crec)
+ {
+ inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);
+ my_syslog(MS_DHCP | LOG_WARNING,
+ _("not giving name %s to the DHCP lease of %s because "
+ "the name exists in %s with address %s"),
+ host_name, daemon->addrbuff,
+ record_source(fail_crec->uid), daemon->namebuff);
+ return;
+ }
+
+ if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
{
if (crec->flags & F_NEG)
- cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
- else
- /* avoid multiple reverse mappings */
- flags &= ~F_REVERSE;
+ {
+ flags |= F_REVERSE;
+ cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
+ }
}
-
+ else
+ flags |= F_REVERSE;
+
if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
if (crec) /* malloc may fail */
{
- crec->flags = flags;
+ crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
if (ttd == 0)
crec->flags |= F_IMMORTAL;
else
crec->ttd = ttd;
- crec->addr.addr.addr.addr4 = *host_address;
+ crec->addr.addr = *host_address;
crec->name.namep = host_name;
+ crec->uid = next_uid();
cache_hash(crec);
+
+ add_dhcp_cname(crec, ttd);
+ }
+}
+#endif
+
+int cache_make_stat(struct txt_record *t)
+{
+ static char *buff = NULL;
+ static int bufflen = 60;
+ int len;
+ struct server *serv, *serv1;
+ char *p;
+
+ if (!buff && !(buff = whine_malloc(60)))
+ return 0;
+
+ p = buff;
+
+ switch (t->stat)
+ {
+ case TXT_STAT_CACHESIZE:
+ sprintf(buff+1, "%d", daemon->cachesize);
+ break;
+
+ case TXT_STAT_INSERTS:
+ sprintf(buff+1, "%d", cache_inserted);
+ break;
+
+ case TXT_STAT_EVICTIONS:
+ sprintf(buff+1, "%d", cache_live_freed);
+ break;
+
+ case TXT_STAT_MISSES:
+ sprintf(buff+1, "%u", daemon->queries_forwarded);
+ break;
+
+ case TXT_STAT_HITS:
+ sprintf(buff+1, "%u", daemon->local_answer);
+ break;
+
+#ifdef HAVE_AUTH
+ case TXT_STAT_AUTH:
+ sprintf(buff+1, "%u", daemon->auth_answer);
+ break;
+#endif
+
+ case TXT_STAT_SERVERS:
+ /* sum counts from different records for same server */
+ for (serv = daemon->servers; serv; serv = serv->next)
+ serv->flags &= ~SERV_COUNTED;
+
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if (!(serv->flags &
+ (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
+ {
+ char *new, *lenp;
+ int port, newlen, bytes_avail, bytes_needed;
+ unsigned int queries = 0, failed_queries = 0;
+ for (serv1 = serv; serv1; serv1 = serv1->next)
+ if (!(serv1->flags &
+ (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
+ sockaddr_isequal(&serv->addr, &serv1->addr))
+ {
+ serv1->flags |= SERV_COUNTED;
+ queries += serv1->queries;
+ failed_queries += serv1->failed_queries;
+ }
+ port = prettyprint_addr(&serv->addr, daemon->addrbuff);
+ lenp = p++; /* length */
+ bytes_avail = (p - buff) + bufflen;
+ bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
+ if (bytes_needed >= bytes_avail)
+ {
+ /* expand buffer if necessary */
+ newlen = bytes_needed + 1 + bufflen - bytes_avail;
+ if (!(new = whine_malloc(newlen)))
+ return 0;
+ memcpy(new, buff, bufflen);
+ free(buff);
+ p = new + (p - buff);
+ lenp = p - 1;
+ buff = new;
+ bufflen = newlen;
+ bytes_avail = (p - buff) + bufflen;
+ bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries);
+ }
+ *lenp = bytes_needed;
+ p += bytes_needed;
+ }
+ t->txt = (unsigned char *)buff;
+ t->len = p - buff;
+ return 1;
}
+
+ len = strlen(buff+1);
+ t->txt = (unsigned char *)buff;
+ t->len = len + 1;
+ *buff = len;
+ return 1;
}
void dump_cache(time_t now)
{
- my_syslog(LOG_INFO, _("time %lu, cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
- (unsigned long)now, daemon->cachesize, cache_live_freed, cache_inserted);
+ struct server *serv, *serv1;
+ char *t = "";
+
+ my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
+ my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
+ daemon->cachesize, cache_live_freed, cache_inserted);
+ my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
+ daemon->queries_forwarded, daemon->local_answer);
+#ifdef HAVE_AUTH
+ my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
+#endif
+#ifdef HAVE_DNSSEC
+ blockdata_report();
+#endif
+
+ /* sum counts from different records for same server */
+ for (serv = daemon->servers; serv; serv = serv->next)
+ serv->flags &= ~SERV_COUNTED;
+
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if (!(serv->flags &
+ (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
+ {
+ int port;
+ unsigned int queries = 0, failed_queries = 0;
+ for (serv1 = serv; serv1; serv1 = serv1->next)
+ if (!(serv1->flags &
+ (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
+ sockaddr_isequal(&serv->addr, &serv1->addr))
+ {
+ serv1->flags |= SERV_COUNTED;
+ queries += serv1->queries;
+ failed_queries += serv1->failed_queries;
+ }
+ port = prettyprint_addr(&serv->addr, daemon->addrbuff);
+ my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
+ }
- if ((daemon->options & (OPT_DEBUG | OPT_LOG)) &&
- (addrbuff || (addrbuff = whine_malloc(ADDRSTRLEN))))
+ if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
int i;
- my_syslog(LOG_DEBUG, "Host Address Flags Expires");
+ my_syslog(LOG_INFO, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
{
- char *a, *p = daemon->namebuff;
- p += sprintf(p, "%-40.40s ", cache_get_name(cache));
- if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
- a = "";
- else if (cache->flags & F_CNAME)
+ char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
+ *a = 0;
+ if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
+ n = "<Root>";
+ p += sprintf(p, "%-30.30s ", n);
+ if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
+ a = cache_get_cname_target(cache);
+#ifdef HAVE_DNSSEC
+ else if (cache->flags & F_DS)
{
- a = "";
- if (!is_outdated_cname_pointer(cache))
- a = cache_get_name(cache->addr.cname.cache);
+ if (cache->flags & F_DNSKEY)
+ /* RRSIG */
+ sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
+ cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
+ else if (!(cache->flags & F_NEG))
+ sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
+ cache->addr.ds.algo, cache->addr.ds.digest);
}
-#ifdef HAVE_IPV6
- else
+ else if (cache->flags & F_DNSKEY)
+ sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
+ cache->addr.key.algo, cache->addr.key.flags);
+#endif
+ else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
- a = addrbuff;
+ a = daemon->addrbuff;
if (cache->flags & F_IPV4)
- inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+ inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
+#ifdef HAVE_IPV6
else if (cache->flags & F_IPV6)
- inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+ inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
+#endif
}
-#else
- else
- a = inet_ntoa(cache->addr.addr.addr.addr4);
+
+ if (cache->flags & F_IPV4)
+ t = "4";
+ else if (cache->flags & F_IPV6)
+ t = "6";
+ else if (cache->flags & F_CNAME)
+ t = "C";
+#ifdef HAVE_DNSSEC
+ else if ((cache->flags & (F_DS | F_DNSKEY)) == (F_DS | F_DNSKEY))
+ t = "G"; /* DNSKEY and DS set -> RRISG */
+ else if (cache->flags & F_DS)
+ t = "S";
+ else if (cache->flags & F_DNSKEY)
+ t = "K";
#endif
- p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
- cache->flags & F_IPV4 ? "4" : "",
- cache->flags & F_IPV6 ? "6" : "",
- cache->flags & F_CNAME ? "C" : "",
+ p += sprintf(p, "%-40.40s %s%s%s%s%s%s%s%s%s ", a, t,
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
cache->flags & F_IMMORTAL ? "I" : " ",
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
- cache->flags & F_HOSTS ? "H" : " ");
+ cache->flags & F_HOSTS ? "H" : " ",
+ cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
- my_syslog(LOG_DEBUG, daemon->namebuff);
+ my_syslog(LOG_INFO, daemon->namebuff);
}
}
}
-static char *record_source(struct hostsfile *addn_hosts, int index)
+char *record_source(unsigned int index)
{
- char *source = HOSTSFILE;
- while (addn_hosts)
- {
- if (addn_hosts->index == index)
- {
- source = addn_hosts->fname;
- break;
- }
- addn_hosts = addn_hosts->next;
+ struct hostsfile *ah;
+
+ if (index == SRC_CONFIG)
+ return "config";
+ else if (index == SRC_HOSTS)
+ return HOSTSFILE;
+
+ for (ah = daemon->addn_hosts; ah; ah = ah->next)
+ if (ah->index == index)
+ return ah->fname;
+
+#ifdef HAVE_INOTIFY
+ for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
+ if (ah->index == index)
+ return ah->fname;
+#endif
+
+ return "<unknown>";
+}
+
+char *querystr(char *desc, unsigned short type)
+{
+ unsigned int i;
+ int len = 10; /* strlen("type=xxxxx") */
+ const char *types = NULL;
+ static char *buff = NULL;
+ static int bufflen = 0;
+
+ for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
+ if (typestr[i].type == type)
+ {
+ types = typestr[i].name;
+ len = strlen(types);
+ break;
+ }
+
+ len += 3; /* braces, terminator */
+ len += strlen(desc);
+
+ if (!buff || bufflen < len)
+ {
+ if (buff)
+ free(buff);
+ else if (len < 20)
+ len = 20;
+
+ buff = whine_malloc(len);
+ bufflen = len;
}
- return source;
+ if (buff)
+ {
+ if (types)
+ sprintf(buff, "%s[%s]", desc, types);
+ else
+ sprintf(buff, "%s[type=%d]", desc, type);
+ }
+
+ return buff ? buff : "";
}
-void log_query(unsigned short flags, char *name, struct all_addr *addr,
- unsigned short type, struct hostsfile *addn_hosts, int index)
+void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{
- char *source, *dest = addrbuff;
+ char *source, *dest = daemon->addrbuff;
char *verb = "is";
- char types[20];
- if (!(daemon->options & OPT_LOG))
+ if (!option_bool(OPT_LOG))
return;
if (addr)
{
+ if (flags & F_KEYTAG)
+ sprintf(daemon->addrbuff, arg, addr->addr.keytag);
+ else
+ {
#ifdef HAVE_IPV6
- inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
- addr, addrbuff, ADDRSTRLEN);
+ inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
+ addr, daemon->addrbuff, ADDRSTRLEN);
#else
- strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
+ strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
+ }
}
+ else
+ dest = arg;
if (flags & F_REVERSE)
{
dest = name;
- name = addrbuff;
+ name = daemon->addrbuff;
}
if (flags & F_NEG)
{
if (flags & F_NXDOMAIN)
- {
- if (flags & F_IPV4)
- dest = "NXDOMAIN-IPv4";
- else
- dest = "NXDOMAIN-IPv6";
- }
+ dest = "NXDOMAIN";
else
{
if (flags & F_IPV4)
dest = "NODATA-IPv4";
- else
+ else if (flags & F_IPV6)
dest = "NODATA-IPv6";
+ else
+ dest = "NODATA";
}
}
else if (flags & F_CNAME)
- {
- /* nasty abuse of IPV4 and IPV6 flags */
- if (flags & F_IPV4)
- dest = "<MX>";
- else if (flags & F_IPV6)
- dest = "<SRV>";
- else if (flags & F_NXDOMAIN)
- dest = "<TXT>";
- else if (flags & F_BIGNAME)
- dest = "<PTR>";
- else
- dest = "<CNAME>";
- }
+ dest = "<CNAME>";
+ else if (flags & F_RRNAME)
+ dest = arg;
- if (flags & F_DHCP)
+ if (flags & F_CONFIG)
+ source = "config";
+ else if (flags & F_DHCP)
source = "DHCP";
else if (flags & F_HOSTS)
- source = record_source(addn_hosts, index);
- else if (flags & F_CONFIG)
- source = "config";
+ source = arg;
else if (flags & F_UPSTREAM)
source = "reply";
+ else if (flags & F_SECSTAT)
+ source = "validation";
+ else if (flags & F_AUTH)
+ source = "auth";
else if (flags & F_SERVER)
{
source = "forwarded";
}
else if (flags & F_QUERY)
{
- unsigned int i;
-
- if (type != 0)
- {
- sprintf(types, "query[type=%d]", type);
- for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
- if (typestr[i].type == type)
- sprintf(types,"query[%s]", typestr[i].name);
- }
- source = types;
+ source = arg;
verb = "from";
}
+ else if (flags & F_DNSSEC)
+ {
+ source = arg;
+ verb = "to";
+ }
+ else if (flags & F_IPSET)
+ {
+ source = "ipset add";
+ dest = name;
+ name = arg;
+ verb = daemon->addrbuff;
+ }
else
source = "cached";
if (strlen(name) == 0)
name = ".";
- my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
+ if (option_bool(OPT_EXTRALOG))
+ {
+ int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
+ if (flags & F_NOEXTRA)
+ my_syslog(LOG_INFO, "* %s/%u %s %s %s %s", daemon->addrbuff2, port, source, name, verb, dest);
+ else
+ my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest);
+ }
+ else
+ my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}
+