diff --git a/src/cache.c b/src/cache.c index cb80cde..abab9ed 100644 --- a/src/cache.c +++ b/src/cache.c @@ -17,13 +17,16 @@ #include "dnsmasq.h" static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL; -#ifdef HAVE_DHCP -static struct crec *dhcp_spare = NULL; -#endif +static struct crec *config_spare = NULL; static struct crec *new_chain = NULL; static int insert_error; static union bigname *big_free = NULL; static int bignames_left, hash_size; +struct nameblock { + struct nameblock *next; + unsigned int last, index; + char data[NAMEBLOCK_CHARS]; +} *hostblocks = NULL; static void make_non_terminals(struct crec *source); static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class, @@ -192,6 +195,61 @@ void cache_init(void) rehash(daemon->cachesize); } +static struct crec *get_config_crec(void) +{ + struct crec *ret; + + if (config_spare) + { + ret = config_spare; + config_spare = config_spare->next; + } + else + ret = whine_malloc(SIZEOF_POINTER_CREC); + + return ret; +} + +static void free_config_crec(struct crec *p) +{ + p->next = config_spare; + config_spare = p; +} + +static char *store_name(unsigned int namelen, unsigned int index) +{ + struct nameblock *block; + char *ret = NULL; + + for (block = hostblocks; block; block = block->next) + if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen) + break; + + if (!block && ((block = whine_malloc(sizeof(struct nameblock))))) + { + block->next = hostblocks; + block->index = index; + hostblocks = block; + } + + if (block && NAMEBLOCK_CHARS - block->last >= namelen) + { + ret = &block->data[block->last]; + block->last += namelen; + } + + return ret; +} + +static void free_names(unsigned int index) +{ + struct nameblock *block; + + for (block = hostblocks; block; block = block->next) + if (index == UID_NONE || block->index == index) + block->last = 0; +} + /* In most cases, we create the hash table once here by calling this with (hash_table == NULL) but if the hosts file(s) are big (some people have 50000 ad-block entries), the table will be much too small, so the hosts reading code calls rehash every 1000 addresses, to @@ -441,12 +499,14 @@ unsigned int cache_remove_uid(const unsigned int uid) if ((crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid) { *up = tmp; - free(crecp); + free_config_crec(crecp); removed++; } else up = &crecp->hash_next; } + + free_names(uid); return removed; } @@ -1261,7 +1321,7 @@ static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrle 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); + free_config_crec(cache); return; } @@ -1384,13 +1444,13 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr { if (inet_pton(AF_INET, token, &addr) > 0) { - flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4; + flags = F_NAMEP | F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4; addrlen = INADDRSZ; domain_suffix = get_domain(addr.addr4); } else if (inet_pton(AF_INET6, token, &addr) > 0) { - flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; + flags = F_NAMEP | F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6; addrlen = IN6ADDRSZ; domain_suffix = get_domain6(&addr.addr6); } @@ -1424,27 +1484,37 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr 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_BARE_CREC + strlen(canon) + 2 + strlen(domain_suffix)))) + if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn && (cache = get_config_crec())) { - strcpy(cache->name.sname, canon); - 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++; - names_done++; + if (!(cache->name.namep = store_name(strlen(canon) + 2 + strlen(domain_suffix), index))) + free_config_crec(cache); + else + { + strcpy(cache->name.namep, canon); + strcat(cache->name.namep, "."); + strcat(cache->name.namep, domain_suffix); + cache->flags = flags; + 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))) + if ((cache = get_config_crec())) { - strcpy(cache->name.sname, canon); - cache->flags = flags; - cache->ttd = daemon->local_ttl; - add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); - name_count++; - names_done++; + if (!(cache->name.namep = store_name(strlen(canon) + 1, index))) + free_config_crec(cache); + else + { + strcpy(cache->name.namep, canon); + cache->flags = flags; + cache->ttd = daemon->local_ttl; + add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz); + name_count++; + names_done++; + } } + free(canon); } @@ -1495,7 +1565,7 @@ void cache_reload(void) if (cache->flags & (F_HOSTS | F_CONFIG)) { *up = cache->hash_next; - free(cache); + free_config_crec(cache); } else if (!(cache->flags & F_DHCP)) { @@ -1510,11 +1580,13 @@ void cache_reload(void) else up = &cache->hash_next; } + + free_names(UID_NONE); /* free everything */ /* Add locally-configured CNAMEs to the cache */ for (a = daemon->cnames; a; a = a->next) if (a->alias[1] != '*' && - ((cache = whine_malloc(SIZEOF_POINTER_CREC)))) + ((cache = get_config_crec()))) { cache->flags = F_FORWARD | F_NAMEP | F_CNAME | F_IMMORTAL | F_CONFIG; cache->ttd = a->ttl; @@ -1528,25 +1600,30 @@ void cache_reload(void) #ifdef HAVE_DNSSEC for (ds = daemon->ds; ds; ds = ds->next) - if ((cache = whine_malloc(SIZEOF_POINTER_CREC)) && - (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen))) + if ((cache = get_config_crec())) { - cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP; - cache->ttd = daemon->local_ttl; - cache->name.namep = ds->name; - cache->uid = ds->class; - if (ds->digestlen != 0) - { - 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; - } - else - cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR; - cache_hash(cache); - make_non_terminals(cache); + if (!(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen))) + free_config_crec(cache); + else + { + cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP; + cache->ttd = daemon->local_ttl; + cache->name.namep = ds->name; + cache->uid = ds->class; + if (ds->digestlen != 0) + { + 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; + } + else + cache->flags |= F_NEG | F_DNSSECOK | F_NO_RR; + + cache_hash(cache); + make_non_terminals(cache); + } } #endif @@ -1561,7 +1638,7 @@ void cache_reload(void) for (nl = hr->names; nl; nl = nl->next) { if ((hr->flags & HR_4) && - (cache = whine_malloc(SIZEOF_POINTER_CREC))) + (cache = get_config_crec())) { cache->name.namep = nl->name; cache->ttd = hr->ttl; @@ -1570,7 +1647,7 @@ void cache_reload(void) } if ((hr->flags & HR_6) && - (cache = whine_malloc(SIZEOF_POINTER_CREC))) + (cache = get_config_crec())) { cache->name.namep = nl->name; cache->ttd = hr->ttl; @@ -1662,8 +1739,7 @@ void cache_unhash_dhcp(void) if (cache->flags & F_DHCP) { *up = cache->hash_next; - cache->next = dhcp_spare; - dhcp_spare = cache; + free_config_crec(cache); } else up = &cache->hash_next; @@ -1734,12 +1810,7 @@ void cache_add_dhcp_entry(char *host_name, int prot, else flags |= F_REVERSE; - if ((crec = dhcp_spare)) - dhcp_spare = dhcp_spare->next; - else /* need new one */ - crec = whine_malloc(SIZEOF_POINTER_CREC); - - if (crec) /* malloc may fail */ + if ((crec = get_config_crec())) { crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD; if (ttd == 0) @@ -1786,15 +1857,7 @@ static void make_non_terminals(struct crec *source) hostname_isequal(name, cache_get_name(crecp))) { *up = crecp->hash_next; -#ifdef HAVE_DHCP - if (type & F_DHCP) - { - crecp->next = dhcp_spare; - dhcp_spare = crecp; - } - else -#endif - free(crecp); + free_config_crec(crecp); break; } else @@ -1827,17 +1890,7 @@ static void make_non_terminals(struct crec *source) continue; } -#ifdef HAVE_DHCP - if ((source->flags & F_DHCP) && dhcp_spare) - { - crecp = dhcp_spare; - dhcp_spare = dhcp_spare->next; - } - else -#endif - crecp = whine_malloc(SIZEOF_POINTER_CREC); - - if (crecp) + if ((crecp = get_config_crec())) { crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | F_CNAME | F_RR | F_DNSKEY | F_DS | F_REVERSE); if (!(crecp->flags & F_IMMORTAL)) @@ -2166,9 +2219,9 @@ static 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; - + static struct iovec buff = { NULL, 0 }; + char *buffp; + for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++) if (typestr[i].type == type) { @@ -2183,37 +2236,28 @@ static char *querystr(char *desc, unsigned short type) len += strlen(desc); } len++; /* terminator */ - - if (!buff || bufflen < len) - { - if (buff) - free(buff); - else if (len < 20) - len = 20; - - buff = whine_malloc(len); - bufflen = len; - } - if (buff) + if (!expand_buf(&buff, len)) + return ""; + + buffp = buff.iov_base; + + if (desc) { - if (desc) - { - if (types) - sprintf(buff, "%s[%s]", desc, types); - else - sprintf(buff, "%s[type=%d]", desc, type); - } + if (types) + sprintf(buffp, "%s[%s]", desc, types); else - { - if (types) - sprintf(buff, "<%s>", types); - else - sprintf(buff, "", type); - } + sprintf(buffp, "%s[type=%d]", desc, type); + } + else + { + if (types) + sprintf(buffp, "<%s>", types); + else + sprintf(buffp, "", type); } - return buff ? buff : ""; + return buffp; } static char *edestr(int ede) diff --git a/src/config.h b/src/config.h index b18754d..a73c134 100644 --- a/src/config.h +++ b/src/config.h @@ -22,6 +22,7 @@ #define TCP_BACKLOG 32 /* kernel backlog limit for TCP connections */ #define EDNS_PKTSZ 1232 /* default max EDNS.0 UDP packet from from /dnsflagday.net/2020 */ #define KEYBLOCK_LEN 40 /* choose to minimise fragmentation when storing DNSSEC keys */ +#define NAMEBLOCK_CHARS 1500 /* quantum of memory allocation for names from /etc/hosts */ #define DNSSEC_LIMIT_WORK 40 /* Max number of queries to validate one question */ #define DNSSEC_LIMIT_SIG_FAIL 20 /* Number of signature that can fail to validate in one answer */ #define DNSSEC_LIMIT_CRYPTO 200 /* max no. of crypto operations to validate one query. */