--- embedaddon/dnsmasq/src/cache.c 2013/07/29 19:37:40 1.1.1.1 +++ embedaddon/dnsmasq/src/cache.c 2016/11/02 09:57:01 1.1.1.3 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley +/* dnsmasq is Copyright (c) 2000-2016 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 @@ -24,10 +24,6 @@ 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; -#ifdef HAVE_DNSSEC -static struct keydata *keyblock_free = NULL; -#endif /* type->string mapping: this is also used by the name-hash function as a mixing table. */ static const struct { @@ -56,7 +52,11 @@ static const struct { { 38, "A6" }, { 39, "DNAME" }, { 41, "OPT" }, + { 43, "DS" }, + { 46, "RRSIG" }, + { 47, "NSEC" }, { 48, "DNSKEY" }, + { 50, "NSEC3" }, { 249, "TKEY" }, { 250, "TSIG" }, { 251, "IXFR" }, @@ -72,11 +72,24 @@ static void cache_link(struct crec *crecp); 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; - + bignames_left = daemon->cachesize/10; if (daemon->cachesize > 0) @@ -87,7 +100,7 @@ void cache_init(void) { cache_link(crecp); crecp->flags = 0; - crecp->uid = uid++; + crecp->uid = next_uid(); } } @@ -171,13 +184,23 @@ static void cache_hash(struct crec *crecp) crecp->hash_next = *up; *up = crecp; } - + +#ifdef HAVE_DNSSEC +static void cache_blockdata_free(struct crec *crecp) +{ + if (crecp->flags & F_DNSKEY) + 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 @@ -193,9 +216,9 @@ static void cache_free(struct crec *crecp) big_free = crecp->name.bname; crecp->flags &= ~F_BIGNAME; } + #ifdef HAVE_DNSSEC - else if (crecp->flags & (F_DNSKEY | F_DS)) - keydata_free(crecp->addr.key.keydata); + cache_blockdata_free(crecp); #endif } @@ -235,6 +258,16 @@ char *cache_get_name(struct crec *crecp) 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; @@ -260,14 +293,14 @@ struct crec *cache_enumerate(int init) 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; /* NB. record may be reused as DS or DNSKEY, where uid is overloaded for something completely different */ - if (crecp->addr.cname.cache && - (crecp->addr.cname.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) && - crecp->addr.cname.uid == crecp->addr.cname.cache->uid) + 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; @@ -284,7 +317,7 @@ static int is_expired(time_t now, struct crec *crecp) 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 @@ -293,8 +326,8 @@ static int cache_scan_free(char *name, struct all_addr 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 ,, so that when we hit an entry which isn't reverse and is immortal, we're done. */ @@ -304,27 +337,47 @@ static int cache_scan_free(char *name, struct all_addr 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_TYPE) || ((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 and DNSKEY */ + if ((flags & crecp->flags & (F_DNSKEY | F_DS)) && crecp->uid == addr->addr.dnssec.class) + { + if (crecp->flags & F_CONFIG) + return crecp; + *up = crecp->hash_next; + cache_unlink(crecp); + cache_free(crecp); + continue; + } +#endif + } up = &crecp->hash_next; + } } else { @@ -341,13 +394,13 @@ static int cache_scan_free(char *name, struct all_addr 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) @@ -360,7 +413,7 @@ static int cache_scan_free(char *name, struct all_addr up = &crecp->hash_next; } - return 1; + return NULL; } /* Note: The normal calling sequence is @@ -394,22 +447,42 @@ struct crec *cache_insert(char *name, struct all_addr int freed_all = flags & F_REVERSE; int free_avail = 0; - if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) - ttl = daemon->max_cache_ttl; + /* Don't log 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; + } - /* Don't log keys */ - if (flags & (F_IPV4 | F_IPV6)) - log_query(flags | F_UPSTREAM, name, addr, NULL); - /* 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) && addr) + { + 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; } @@ -434,14 +507,28 @@ struct crec *cache_insert(char *name, struct all_addr 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) { + struct all_addr free_addr = new->addr.addr;; + +#ifdef HAVE_DNSSEC + /* For DNSSEC records, addr holds class. */ + if (new->flags & (F_DS | F_DNSKEY)) + free_addr.addr.dnssec.class = new->uid; +#endif + free_avail = 1; /* Must be free space now. */ - cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags); + cache_scan_free(cache_get_name(new), &free_addr, now, new->flags); cache_live_freed++; } else @@ -453,7 +540,7 @@ struct crec *cache_insert(char *name, struct all_addr } /* 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) @@ -461,13 +548,13 @@ struct crec *cache_insert(char *name, struct all_addr 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--; } @@ -490,7 +577,14 @@ struct crec *cache_insert(char *name, struct all_addr *cache_get_name(new) = 0; if (addr) - new->addr.addr = *addr; + { +#ifdef HAVE_DNSSEC + if (flags & (F_DS | F_DNSKEY)) + new->uid = addr->addr.dnssec.class; + else +#endif + new->addr.addr = *addr; + } new->ttd = now + (time_t)ttl; new->next = new_chain; @@ -522,10 +616,13 @@ void cache_end_insert(void) 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 @@ -545,7 +642,7 @@ struct crec *cache_find_by_name(struct crec *crecp, ch (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; @@ -570,7 +667,7 @@ struct crec *cache_find_by_name(struct crec *crecp, ch } else { - if (!insert) + if (!insert && !no_rr) { insert = up; ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL); @@ -586,7 +683,7 @@ struct crec *cache_find_by_name(struct crec *crecp, ch { /* 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); @@ -599,7 +696,7 @@ struct crec *cache_find_by_name(struct crec *crecp, ch if (ans && (ans->flags & F_FORWARD) && - (ans->flags & prot) && + (ans->flags & prot) && hostname_isequal(cache_get_name(ans), name)) return ans; @@ -607,7 +704,7 @@ struct crec *cache_find_by_name(struct crec *crecp, ch } 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 @@ -636,7 +733,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, st 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; @@ -652,7 +749,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, st 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); @@ -680,17 +777,19 @@ static void add_hosts_cname(struct crec *target) if (hostname_isequal(cache_get_name(target), a->target) && (crec = whine_malloc(sizeof(struct crec)))) { - crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME; + crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME; + crec->ttd = a->ttl; crec->name.namep = a->alias; - crec->addr.cname.cache = target; + 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, - int index, struct crec **rhash, int hashsz) + 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; @@ -717,27 +816,42 @@ static void add_hosts_entry(struct crec *cache, struct 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. */ + 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. + */ - /* 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) - { - cache->flags &= ~F_REVERSE; - break; - } - - /* maintain address hash chain, insert new unique address */ - if (!lookup) + if (rhash) { - cache->next = rhash[j]; - rhash[j] = cache; + /* 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) + { + cache->flags &= ~F_REVERSE; + break; + } + + /* 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); @@ -794,7 +908,7 @@ static int gettok(FILE *f, char *token) } } -static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash, int hashsz) +int read_hostsfile(char *filename, unsigned int index, int cache_size, struct crec **rhash, int hashsz) { FILE *f = fopen(filename, "r"); char *token = daemon->namebuff, *domain_suffix = NULL; @@ -806,7 +920,7 @@ static int read_hostsfile(char *filename, int index, i if (!f) { my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno)); - return 0; + return cache_size; } eatspace(f); @@ -840,7 +954,7 @@ static int read_hostsfile(char *filename, int index, i addr_count++; /* rehash every 1000 names. */ - if ((name_count - cache_size) > 1000) + if (rhash && ((name_count - cache_size) > 1000)) { rehash(name_count); cache_size = name_count; @@ -868,6 +982,7 @@ static int read_hostsfile(char *filename, int index, i strcat(cache->name.sname, "."); strcat(cache->name.sname, domain_suffix); cache->flags = flags; + cache->ttd = daemon->local_ttl; add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; } @@ -875,6 +990,7 @@ static int read_hostsfile(char *filename, int index, i { strcpy(cache->name.sname, canon); cache->flags = flags; + cache->ttd = daemon->local_ttl; add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; } @@ -887,8 +1003,10 @@ static int read_hostsfile(char *filename, int index, i } fclose(f); - rehash(name_count); + if (rhash) + rehash(name_count); + my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count); return name_count; @@ -901,14 +1019,22 @@ void cache_reload(void) 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; ihash_next; - if (cache->flags & F_HOSTS) + if (cache->flags & (F_HOSTS | F_CONFIG)) { *up = cache->hash_next; free(cache); @@ -927,6 +1053,39 @@ void cache_reload(void) up = &cache->hash_next; } + /* 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->ttd = a->ttl; + 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->ttd = daemon->local_ttl; + 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 *); @@ -941,16 +1100,18 @@ void cache_reload(void) (cache = whine_malloc(sizeof(struct crec)))) { cache->name.namep = nl->name; + cache->ttd = hr->ttl; 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, 0, (struct crec **)daemon->packet, revhashsz); + 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->ttd = hr->ttl; 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, 0, (struct crec **)daemon->packet, revhashsz); + add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz); } #endif } @@ -959,49 +1120,23 @@ void cache_reload(void) { if (daemon->cachesize > 0) my_syslog(LOG_INFO, _("cleared cache")); - return; } - - if (!option_bool(OPT_NO_HOSTS)) - total_size = read_hostsfile(HOSTSFILE, 0, 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); -} + else + { + 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); + } -char *get_domain(struct in_addr addr) -{ - struct cond_domain *c; - - for (c = daemon->cond_domain; c; c = c->next) - if (!c->is6 && - ntohl(addr.s_addr) >= ntohl(c->start.s_addr) && - ntohl(addr.s_addr) <= ntohl(c->end.s_addr)) - return c->domain; - - return daemon->domain_suffix; -} - - -#ifdef HAVE_IPV6 -char *get_domain6(struct in6_addr *addr) -{ - struct cond_domain *c; - - u64 addrpart = addr6part(addr); - - for (c = daemon->cond_domain; c; c = c->next) - if (c->is6 && - is_same_net6(addr, &c->start6, 64) && - addrpart >= addr6part(&c->start6) && - addrpart <= addr6part(&c->end6)) - return c->domain; - - return daemon->domain_suffix; -} +#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) @@ -1051,14 +1186,15 @@ static void add_dhcp_cname(struct crec *target, time_t if (aliasc) { - aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME; + 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.cache = target; + aliasc->addr.cname.target.cache = target; aliasc->addr.cname.uid = target->uid; + aliasc->uid = next_uid(); cache_hash(aliasc); add_dhcp_cname(aliasc, ttd); } @@ -1086,7 +1222,7 @@ void cache_add_dhcp_entry(char *host_name, int prot, while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME))) { /* check all addresses associated with name */ - if (crec->flags & F_HOSTS) + if (crec->flags & (F_HOSTS | F_CONFIG)) { if (crec->flags & F_CNAME) my_syslog(MS_DHCP | LOG_WARNING, @@ -1146,7 +1282,7 @@ void cache_add_dhcp_entry(char *host_name, int prot, crec->ttd = ttd; crec->addr.addr = *host_address; crec->name.namep = host_name; - crec->uid = uid++; + crec->uid = next_uid(); cache_hash(crec); add_dhcp_cname(crec, ttd); @@ -1154,16 +1290,132 @@ void cache_add_dhcp_entry(char *host_name, int prot, } #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 = bufflen - (p - buff ); + 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 = bufflen - (p - buff ); + 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; +} + +/* There can be names in the cache containing control chars, don't + mess up logging or open security holes. */ +static char *sanitise(char *name) +{ + unsigned char *r; + if (name) + for (r = (unsigned char *)name; *r; r++) + if (!isprint((int)*r)) + return ""; + + return name; +} + + void dump_cache(time_t now) { 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) @@ -1192,35 +1444,30 @@ void dump_cache(time_t now) { struct crec *cache ; int i; - my_syslog(LOG_INFO, "Host Address Flags Expires"); + my_syslog(LOG_INFO, "Host Address Flags Expires"); for (i=0; ihash_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) - { - a = ""; - if (!is_outdated_cname_pointer(cache)) - a = cache_get_name(cache->addr.cname.cache); - } + char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache); + *a = 0; + if (strlen(n) == 0 && !(cache->flags & F_REVERSE)) + n = ""; + p += sprintf(p, "%-30.30s ", sanitise(n)); + if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) + a = sanitise(cache_get_cname_target(cache)); #ifdef HAVE_DNSSEC - else if (cache->flags & F_DNSKEY) - { - a = daemon->addrbuff; - sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid); - } else if (cache->flags & F_DS) { - a = daemon->addrbuff; - sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid, - cache->addr.key.algo, cache->addr.key.digest, cache->uid); + if (!(cache->flags & F_NEG)) + sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag, + cache->addr.ds.algo, cache->addr.ds.digest); } + else if (cache->flags & F_DNSKEY) + sprintf(a, "%5u %3u %3u", cache->addr.key.keytag, + cache->addr.key.algo, cache->addr.key.flags); #endif - else + else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD)) { a = daemon->addrbuff; if (cache->flags & F_IPV4) @@ -1231,12 +1478,19 @@ void dump_cache(time_t now) #endif } - p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a, - cache->flags & F_IPV4 ? "4" : "", - cache->flags & F_IPV6 ? "6" : "", - cache->flags & F_DNSKEY ? "K" : "", - cache->flags & F_DS ? "S" : "", - cache->flags & F_CNAME ? "C" : "", + 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) + t = "S"; + else if (cache->flags & F_DNSKEY) + t = "K"; +#endif + 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" : " ", @@ -1257,28 +1511,67 @@ void dump_cache(time_t now) } } -char *record_source(int index) +char *record_source(unsigned int index) { struct hostsfile *ah; - if (index == 0) + 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 ""; } -void querystr(char *desc, char *str, unsigned short type) +char *querystr(char *desc, unsigned short type) { unsigned int i; - - sprintf(str, "%s[type=%d]", desc, type); + 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) - sprintf(str,"%s[%s]", desc, typestr[i].name); + { + 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; + } + + 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 int flags, char *name, struct all_addr *addr, char *arg) @@ -1289,15 +1582,24 @@ void log_query(unsigned int flags, char *name, struct if (!option_bool(OPT_LOG)) return; + name = sanitise(name); + if (addr) { + if (flags & F_KEYTAG) + sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest); + else + { #ifdef HAVE_IPV6 - inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, - addr, daemon->addrbuff, ADDRSTRLEN); + inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, + addr, daemon->addrbuff, ADDRSTRLEN); #else - strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN); + strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN); #endif + } } + else + dest = arg; if (flags & F_REVERSE) { @@ -1308,14 +1610,7 @@ void log_query(unsigned int flags, char *name, struct if (flags & F_NEG) { if (flags & F_NXDOMAIN) - { - if (flags & F_IPV4) - dest = "NXDOMAIN-IPv4"; - else if (flags & F_IPV6) - dest = "NXDOMAIN-IPv6"; - else - dest = "NXDOMAIN"; - } + dest = "NXDOMAIN"; else { if (flags & F_IPV4) @@ -1339,6 +1634,8 @@ void log_query(unsigned int flags, char *name, struct 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) @@ -1351,59 +1648,34 @@ void log_query(unsigned int flags, char *name, struct 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_INFO, "%s %s %s %s", source, name, verb, dest); -} - -#ifdef HAVE_DNSSEC -struct keydata *keydata_alloc(char *data, size_t len) -{ - struct keydata *block, *ret = NULL; - struct keydata **prev = &ret; - while (len > 0) + if (option_bool(OPT_EXTRALOG)) { - if (keyblock_free) - { - block = keyblock_free; - keyblock_free = block->next; - } + 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 - block = whine_malloc(sizeof(struct keydata)); - - if (!block) - { - /* failed to alloc, free partial chain */ - keydata_free(ret); - return NULL; - } - - memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len); - data += KEYBLOCK_LEN; - len -= KEYBLOCK_LEN; - *prev = block; - prev = &block->next; - block->next = NULL; + my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest); } - - return ret; + else + my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest); } -void keydata_free(struct keydata *blocks) -{ - struct keydata *tmp; - - if (blocks) - { - for (tmp = blocks; tmp->next; tmp = tmp->next); - tmp->next = keyblock_free; - keyblock_free = blocks; - } -} -#endif - - +