[Dnsmasq-discuss] DNSMASQ Cache

Simon Kelley simon at thekelleys.org.uk
Sun Jun 19 22:13:08 UTC 2022


On 16/06/2022 05:46, John Gilmour via Dnsmasq-discuss wrote:
> Hello,
> 
> 
> So, further to my email I have added the following lines to my version 
> of dnsmasq:
> 
> 
> src/cache.c/cache_reload:
> 
>     /* Make non-terminal records for all locally-define RRs */
> +  memset (&lrec, 0, sizeof (lrec));
>     lrec.flags = F_FORWARD | F_CONFIG | F_NAMEP | F_IMMORTAL;
> 
> 
> src/cache.c/make_non_terminals:
> 
>         if (crecp)        {
> +      memcpy(crecp, source, sizeof (source));
>            crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 
> | F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
> -         crecp->ttd = source->ttd;
>            crecp->name.namep = name;
> 
> 
> 
> Also, there seems to me to be a bug in make_non_terminals.  As best as I 
> can tell, there is a "daemon->txt" structure that contains a number of 
> entries. These are added to the daemon in "src/option.c/read_opts":
> #ifndef NO_ID
> add_txt("version.bind", "dnsmasq-" VERSION, 0 );
> add_txt("authors.bind", "Simon Kelley", 0);
> 
> .........
> 
> 
> The call to cache_reload calls make_non_terminals:
> 
>    for (txt = daemon->txt; txt; txt = txt->next)
>      {
>        lrec.name.namep = txt->name;
>        make_non_terminals(&lrec);
>      }
> 
> 
> I believe that there should be 1 entry in the cache for each of the 
> read_opts.  But there is not.  There is only one entry with the name 
> "bind".
> 
> 
> The comment for make_non_terminals says:
> 
>        Creates empty cache entries for subnames (ie, for three.two.one, 
> for two.one and one)
> 
> 
> What is happening is that the function splits the string up by the 
> delimiter ".". It does this by using:
> 
> while ((name = strchr (name, '.')))
> 
>      name++;
> 
> 
> So, when it gets to the following code the name equals "bind" and this 
> is all it adds to the cache. Subsequent attempts to add new read_opts 
> boil down to "bind" so after the reload the entire cache consists of 
> only one "bind" entry.
> 
>        crecp->flags = (source->flags | F_NAMEP) & ~(F_IPV4 | F_IPV6 | 
> F_CNAME | F_SRV | F_DNSKEY | F_DS | F_REVERSE);
> 
>        crecp->ttd = source->ttd;
> 
>        crecp->name.namep = name;
>        cache_hash(crecp);
> 
> 
> I am not sure what is supposed to be added to the cache - the full name, 
> each delimited name, just the last name.  If you could advise me on this 
> function I would be eternally grateful.
> 
> 

Non-terminals are best described by example.

Consider a DNS name "example.com" which has no resource records 
associated with it. A query for that name of any type would return a "no 
such domain" reply. Now consider the case again that there are no 
resource records associated with "example.com", but there is a resource 
record (of any type) associated with "www.example.com" querying 
www.example.com will return that resource record. In this case, querying 
"example.com" MUST NOT return NDDOMAIN. a query of any type should 
return a NODATA reply instead.

In other words, a domain name can exist either because there's at least 
one resource record at that name, OR because there's at least one 
subdomain of that name, (or both).


In the case you're looking at, a load of names of the form 
"version.bind" and "author.bind" get added to the cache. The purpose of 
make-non-terminals is simply to add a special cache record named "bind" 
which ensures that answers to queries for "bind" return NODATA replies 
and not NXDOMAIN replies, even though there are no resource records for 
"bind".

These particular records are of type TXT. TXT records are not held in 
the cache, they are answered directly from the daemon->txt datstructure, 
which is why you don't see a cache entry for them. See src/rfc1035.c 
around line 1507 for the code that does this. That's why you only see 
the "bind" cache entry required to convert the answer to a query for 
"bind" from NXDOMAIN to NODATA.


Simon.




More information about the Dnsmasq-discuss mailing list