--- embedaddon/dnsmasq/src/cache.c 2014/06/15 16:31:38 1.1.1.2 +++ embedaddon/dnsmasq/src/cache.c 2016/11/02 09:57:01 1.1.1.3 @@ -1,4 +1,4 @@ -/* dnsmasq is Copyright (c) 2000-2014 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 @@ -189,12 +189,7 @@ static void cache_hash(struct crec *crecp) 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); - } + blockdata_free(crecp->addr.key.keydata); else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG)) blockdata_free(crecp->addr.ds.keydata); } @@ -322,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 @@ -331,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. */ @@ -361,7 +356,7 @@ static int cache_scan_free(char *name, struct all_addr (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) - return 0; + return crecp; *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); @@ -369,16 +364,11 @@ static int cache_scan_free(char *name, struct all_addr } #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)) + /* 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 0; + return crecp; *up = crecp->hash_next; cache_unlink(crecp); cache_free(crecp); @@ -423,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 @@ -461,9 +451,11 @@ struct crec *cache_insert(char *name, struct all_addr if (flags & (F_IPV4 | F_IPV6 | F_CNAME)) { log_query(flags | F_UPSTREAM, name, addr, NULL); - /* Don;t mess with TTL for DNSSEC records. */ + /* 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. */ @@ -471,10 +463,26 @@ struct crec *cache_insert(char *name, struct all_addr return NULL; /* First remove any expired entries and entries for the name/address we - are currently inserting. Fail if 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; } @@ -514,13 +522,9 @@ struct crec *cache_insert(char *name, struct all_addr struct all_addr free_addr = new->addr.addr;; #ifdef HAVE_DNSSEC - /* For DNSSEC records, addr holds class and type_covered for RRSIG */ + /* For DNSSEC records, addr holds class. */ 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; - } + free_addr.addr.dnssec.class = new->uid; #endif free_avail = 1; /* Must be free space now. */ @@ -635,9 +639,6 @@ struct crec *cache_find_by_name(struct crec *crecp, ch 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))) && -#endif (crecp->flags & prot) && hostname_isequal(cache_get_name(crecp), name)) { @@ -695,9 +696,6 @@ struct crec *cache_find_by_name(struct crec *crecp, ch if (ans && (ans->flags & F_FORWARD) && -#ifdef HAVE_DNSSEC - ((ans->flags & (F_DNSKEY | F_DS)) == (prot & (F_DNSKEY | F_DS))) && -#endif (ans->flags & prot) && hostname_isequal(cache_get_name(ans), name)) return ans; @@ -780,6 +778,7 @@ static void add_hosts_cname(struct crec *target) (crec = whine_malloc(sizeof(struct crec)))) { crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_CONFIG | F_CNAME; + crec->ttd = a->ttl; crec->name.namep = a->alias; crec->addr.cname.target.cache = target; crec->addr.cname.uid = target->uid; @@ -817,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); @@ -894,7 +908,7 @@ static int gettok(FILE *f, char *token) } } -static int read_hostsfile(char *filename, unsigned 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; @@ -906,7 +920,7 @@ static int read_hostsfile(char *filename, unsigned int if (!f) { my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno)); - return 0; + return cache_size; } eatspace(f); @@ -940,7 +954,7 @@ static int read_hostsfile(char *filename, unsigned int 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; @@ -968,6 +982,7 @@ static int read_hostsfile(char *filename, unsigned int 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++; } @@ -975,6 +990,7 @@ static int read_hostsfile(char *filename, unsigned int { strcpy(cache->name.sname, canon); cache->flags = flags; + cache->ttd = daemon->local_ttl; add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); name_count++; } @@ -987,8 +1003,10 @@ static int read_hostsfile(char *filename, unsigned int } fclose(f); - rehash(name_count); + if (rhash) + rehash(name_count); + my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count); return name_count; @@ -1042,6 +1060,7 @@ void cache_reload(void) ((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; @@ -1056,6 +1075,7 @@ void cache_reload(void) (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; @@ -1080,6 +1100,7 @@ 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, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz); } @@ -1088,6 +1109,7 @@ 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_IPV6 | F_NAMEP | F_CONFIG; add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, SRC_CONFIG, (struct crec **)daemon->packet, revhashsz); } @@ -1098,16 +1120,22 @@ 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, 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); + 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); + } + +#ifdef HAVE_INOTIFY + set_dynamic_inotify(AH_HOSTS, total_size, (struct crec **)daemon->packet, revhashsz); +#endif + } #ifdef HAVE_DHCP @@ -1326,7 +1354,7 @@ int cache_make_stat(struct txt_record *t) } port = prettyprint_addr(&serv->addr, daemon->addrbuff); lenp = p++; /* length */ - bytes_avail = (p - buff) + bufflen; + 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) { @@ -1340,7 +1368,7 @@ int cache_make_stat(struct txt_record *t) lenp = p - 1; buff = new; bufflen = newlen; - bytes_avail = (p - buff) + bufflen; + bytes_avail = bufflen - (p - buff ); bytes_needed = snprintf(p, bytes_avail, "%s#%d %u %u", daemon->addrbuff, port, queries, failed_queries); } *lenp = bytes_needed; @@ -1358,6 +1386,20 @@ int cache_make_stat(struct txt_record *t) 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; @@ -1411,17 +1453,13 @@ void dump_cache(time_t now) *a = 0; if (strlen(n) == 0 && !(cache->flags & F_REVERSE)) n = ""; - p += sprintf(p, "%-40.40s ", n); + p += sprintf(p, "%-30.30s ", sanitise(n)); if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache)) - a = cache_get_cname_target(cache); + a = sanitise(cache_get_cname_target(cache)); #ifdef HAVE_DNSSEC else if (cache->flags & F_DS) { - 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)) + if (!(cache->flags & F_NEG)) sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag, cache->addr.ds.algo, cache->addr.ds.digest); } @@ -1447,14 +1485,12 @@ void dump_cache(time_t now) 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 ", a, t, + 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" : " ", @@ -1487,7 +1523,13 @@ char *record_source(unsigned int index) 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 ""; } @@ -1540,10 +1582,12 @@ 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.keytag); + sprintf(daemon->addrbuff, arg, addr->addr.log.keytag, addr->addr.log.algo, addr->addr.log.digest); else { #ifdef HAVE_IPV6 @@ -1622,7 +1666,16 @@ void log_query(unsigned int flags, char *name, struct if (strlen(name) == 0) name = "."; - my_syslog(LOG_INFO, "%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); }