Rebased from upstream / out of band repository.
[librecmc/librecmc.git] / package / network / services / dnsmasq / patches / 0024-Cache-SRV-records.patch
1 From 5b99eae59d59a8e34a7e512059b98bbd803312f2 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Sun, 6 Jan 2019 23:09:50 +0000
4 Subject: [PATCH 24/30] Cache SRV records.
5
6 Inpsired by a patch from Jeremy Allison, but completely re-rolled
7 by srk. All bugs are mine.
8
9 Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
10 ---
11  src/auth.c      |   2 +-
12  src/blockdata.c |  12 ++---
13  src/cache.c     |  64 ++++++++++++++--------
14  src/dnsmasq.c   |   2 -
15  src/dnsmasq.h   |  11 ++--
16  src/rfc1035.c   | 141 ++++++++++++++++++++++++++++++++++++++----------
17  6 files changed, 166 insertions(+), 66 deletions(-)
18
19 --- a/src/auth.c
20 +++ b/src/auth.c
21 @@ -129,7 +129,7 @@ size_t answer_auth(struct dns_header *he
22  
23    for (q = ntohs(header->qdcount); q != 0; q--)
24      {
25 -      unsigned short flag = 0;
26 +      unsigned int flag = 0;
27        int found = 0;
28        int cname_wildcard = 0;
29    
30 --- a/src/blockdata.c
31 +++ b/src/blockdata.c
32 @@ -16,8 +16,6 @@
33  
34  #include "dnsmasq.h"
35  
36 -#ifdef HAVE_DNSSEC
37 -
38  static struct blockdata *keyblock_free;
39  static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
40  
41 @@ -54,11 +52,10 @@ void blockdata_init(void)
42  
43  void blockdata_report(void)
44  {
45 -  if (option_bool(OPT_DNSSEC_VALID))
46 -    my_syslog(LOG_INFO, _("DNSSEC memory in use %u, max %u, allocated %u"), 
47 -             blockdata_count * sizeof(struct blockdata),  
48 -             blockdata_hwm * sizeof(struct blockdata),  
49 -             blockdata_alloced * sizeof(struct blockdata));
50 +  my_syslog(LOG_INFO, _("pool memory in use %u, max %u, allocated %u"), 
51 +           blockdata_count * sizeof(struct blockdata),  
52 +           blockdata_hwm * sizeof(struct blockdata),  
53 +           blockdata_alloced * sizeof(struct blockdata));
54  } 
55  
56  static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len)
57 @@ -178,4 +175,3 @@ struct blockdata *blockdata_read(int fd,
58    return blockdata_alloc_real(fd, NULL, len);
59  }
60  
61 -#endif
62 --- a/src/cache.c
63 +++ b/src/cache.c
64 @@ -27,7 +27,7 @@ static int bignames_left, hash_size;
65  
66  static void make_non_terminals(struct crec *source);
67  static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
68 -                                 time_t now,  unsigned long ttl, unsigned short flags);
69 +                                 time_t now,  unsigned long ttl, unsigned int flags);
70  
71  /* type->string mapping: this is also used by the name-hash function as a mixing table. */
72  static const struct {
73 @@ -198,15 +198,17 @@ static void cache_hash(struct crec *crec
74    *up = crecp;
75  }
76  
77 -#ifdef HAVE_DNSSEC
78  static void cache_blockdata_free(struct crec *crecp)
79  {
80 -  if (crecp->flags & F_DNSKEY)
81 +  if (crecp->flags & F_SRV)
82 +    blockdata_free(crecp->addr.srv.target);
83 +#ifdef HAVE_DNSSEC
84 +  else if (crecp->flags & F_DNSKEY)
85      blockdata_free(crecp->addr.key.keydata);
86    else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
87      blockdata_free(crecp->addr.ds.keydata);
88 -}
89  #endif
90 +}
91  
92  static void cache_free(struct crec *crecp)
93  {
94 @@ -230,9 +232,7 @@ static void cache_free(struct crec *crec
95        crecp->flags &= ~F_BIGNAME;
96      }
97  
98 -#ifdef HAVE_DNSSEC
99    cache_blockdata_free(crecp);
100 -#endif
101  }    
102  
103  /* insert a new cache entry at the head of the list (youngest entry) */
104 @@ -331,7 +331,7 @@ static int is_expired(time_t now, struct
105  }
106  
107  static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
108 -                                   unsigned short flags, struct crec **target_crec, unsigned int *target_uid)
109 +                                   unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
110  {
111    /* Scan and remove old entries.
112       If (flags & F_FORWARD) then remove any forward entries for name and any expired
113 @@ -360,7 +360,7 @@ static struct crec *cache_scan_free(char
114           if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
115             {
116               /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
117 -             if ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || 
118 +             if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) || 
119                   (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))))
120                 {
121                   if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
122 @@ -467,10 +467,10 @@ void cache_start_insert(void)
123  }
124  
125  struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
126 -                         time_t now,  unsigned long ttl, unsigned short flags)
127 +                         time_t now,  unsigned long ttl, unsigned int flags)
128  {
129    /* Don't log DNSSEC records here, done elsewhere */
130 -  if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
131 +  if (flags & (F_IPV4 | F_IPV6 | F_CNAME | F_SRV))
132      {
133        log_query(flags | F_UPSTREAM, name, addr, NULL);
134        /* Don't mess with TTL for DNSSEC records. */
135 @@ -485,7 +485,7 @@ struct crec *cache_insert(char *name, un
136  
137  
138  static struct crec *really_insert(char *name, union all_addr *addr, unsigned short class,
139 -                                 time_t now,  unsigned long ttl, unsigned short flags)
140 +                                 time_t now,  unsigned long ttl, unsigned int flags)
141  {
142    struct crec *new, *target_crec = NULL;
143    union bigname *big_name = NULL;
144 @@ -649,7 +649,7 @@ void cache_end_insert(void)
145             {
146               char *name = cache_get_name(new_chain);
147               ssize_t m = strlen(name);
148 -             unsigned short flags = new_chain->flags;
149 +             unsigned int flags = new_chain->flags;
150  #ifdef HAVE_DNSSEC
151               u16 class = new_chain->uid;
152  #endif
153 @@ -659,8 +659,10 @@ void cache_end_insert(void)
154               read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), 0);
155               read_write(daemon->pipe_to_parent, (unsigned  char *)&flags, sizeof(flags), 0);
156  
157 -             if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS))
158 +             if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
159                 read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), 0);
160 +             if (flags & F_SRV)
161 +                blockdata_write(new_chain->addr.srv.target, new_chain->addr.srv.targetlen, daemon->pipe_to_parent);
162  #ifdef HAVE_DNSSEC
163               if (flags & F_DNSKEY)
164                 {
165 @@ -699,7 +701,7 @@ int cache_recv_insert(time_t now, int fd
166    union all_addr addr;
167    unsigned long ttl;
168    time_t ttd;
169 -  unsigned short flags;
170 +  unsigned int flags;
171    struct crec *crecp = NULL;
172    
173    cache_start_insert();
174 @@ -725,13 +727,16 @@ int cache_recv_insert(time_t now, int fd
175  
176        ttl = difftime(ttd, now);
177        
178 -      if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS))
179 +      if (flags & (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS | F_SRV))
180         {
181           unsigned short class = C_IN;
182  
183           if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
184             return 0;
185 -         
186 +
187 +         if (flags & F_SRV && !(addr.srv.target = blockdata_read(fd, addr.srv.targetlen)))
188 +           return 0;
189 +       
190  #ifdef HAVE_DNSSEC
191            if (flags & F_DNSKEY)
192              {
193 @@ -802,7 +807,7 @@ struct crec *cache_find_by_name(struct c
194        /* first search, look for relevant entries and push to top of list
195          also free anything which has expired */
196        struct crec *next, **up, **insert = NULL, **chainp = &ans;
197 -      unsigned short ins_flags = 0;
198 +      unsigned int ins_flags = 0;
199        
200        for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
201         {
202 @@ -1086,7 +1091,7 @@ int read_hostsfile(char *filename, unsig
203    FILE *f = fopen(filename, "r");
204    char *token = daemon->namebuff, *domain_suffix = NULL;
205    int addr_count = 0, name_count = cache_size, lineno = 0;
206 -  unsigned short flags = 0;
207 +  unsigned int flags = 0;
208    union all_addr addr;
209    int atnl, addrlen = 0;
210  
211 @@ -1201,9 +1206,8 @@ void cache_reload(void)
212    for (i=0; i<hash_size; i++)
213      for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
214        {
215 -#ifdef HAVE_DNSSEC
216         cache_blockdata_free(cache);
217 -#endif
218 +
219         tmp = cache->hash_next;
220         if (cache->flags & (F_HOSTS | F_CONFIG))
221           {
222 @@ -1381,7 +1385,7 @@ void cache_add_dhcp_entry(char *host_nam
223                           union all_addr *host_address, time_t ttd) 
224  {
225    struct crec *crec = NULL, *fail_crec = NULL;
226 -  unsigned short flags = F_IPV4;
227 +  unsigned int flags = F_IPV4;
228    int in_hosts = 0;
229    size_t addrlen = sizeof(struct in_addr);
230  
231 @@ -1682,9 +1686,8 @@ void dump_cache(time_t now)
232  #ifdef HAVE_AUTH
233    my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
234  #endif
235 -#ifdef HAVE_DNSSEC
236 +
237    blockdata_report();
238 -#endif
239  
240    /* sum counts from different records for same server */
241    for (serv = daemon->servers; serv; serv = serv->next)
242 @@ -1726,6 +1729,17 @@ void dump_cache(time_t now)
243             p += sprintf(p, "%-30.30s ", sanitise(n));
244             if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
245               a = sanitise(cache_get_cname_target(cache));
246 +           else if ((cache->flags & F_SRV) && !(cache->flags & F_NEG))
247 +             {
248 +               int targetlen = cache->addr.srv.targetlen;
249 +               ssize_t len = sprintf(a, "%u %u %u ", cache->addr.srv.priority,
250 +                                     cache->addr.srv.weight, cache->addr.srv.srvport);
251 +
252 +               if (targetlen > (40 - len))
253 +                 targetlen = 40 - len;
254 +               blockdata_retrieve(cache->addr.srv.target, targetlen, a + len);
255 +               a[len + targetlen] = 0;         
256 +             }
257  #ifdef HAVE_DNSSEC
258             else if (cache->flags & F_DS)
259               {
260 @@ -1752,6 +1766,8 @@ void dump_cache(time_t now)
261               t = "6";
262             else if (cache->flags & F_CNAME)
263               t = "C";
264 +           else if (cache->flags & F_SRV)
265 +             t = "V";
266  #ifdef HAVE_DNSSEC
267             else if (cache->flags & F_DS)
268               t = "S";
269 @@ -1913,6 +1929,8 @@ void log_query(unsigned int flags, char
270      }
271    else if (flags & F_CNAME)
272      dest = "<CNAME>";
273 +  else if (flags & F_SRV)
274 +    dest = "<SRV>";
275    else if (flags & F_RRNAME)
276      dest = arg;
277      
278 --- a/src/dnsmasq.c
279 +++ b/src/dnsmasq.c
280 @@ -366,9 +366,7 @@ int main (int argc, char **argv)
281      {
282        cache_init();
283  
284 -#ifdef HAVE_DNSSEC
285        blockdata_init();
286 -#endif
287      }
288  
289  #ifdef HAVE_INOTIFY
290 --- a/src/dnsmasq.h
291 +++ b/src/dnsmasq.h
292 @@ -299,6 +299,10 @@ union all_addr {
293      unsigned char algo;
294      unsigned char digest; 
295    } ds;
296 +  struct {
297 +    struct blockdata *target;
298 +    unsigned short targetlen, srvport, priority, weight;
299 +  } srv;
300    /* for log_query */
301    struct {
302      unsigned short keytag, algo, digest, rcode;
303 @@ -426,7 +430,7 @@ struct crec {
304    time_t ttd; /* time to die */
305    /* used as class if DNSKEY/DS, index to source for F_HOSTS */
306    unsigned int uid; 
307 -  unsigned short flags;
308 +  unsigned int flags;
309    union {
310      char sname[SMALLDNAME];
311      union bigname *bname;
312 @@ -470,6 +474,7 @@ struct crec {
313  #define F_NOEXTRA   (1u<<27)
314  #define F_SERVFAIL  (1u<<28)
315  #define F_RCODE     (1u<<29)
316 +#define F_SRV       (1u<<30)
317  
318  #define UID_NONE      0
319  /* Values of uid in crecs with F_CONFIG bit set. */
320 @@ -1142,7 +1147,7 @@ void cache_end_insert(void);
321  void cache_start_insert(void);
322  int cache_recv_insert(time_t now, int fd);
323  struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class, 
324 -                         time_t now, unsigned long ttl, unsigned short flags);
325 +                         time_t now, unsigned long ttl, unsigned int flags);
326  void cache_reload(void);
327  void cache_add_dhcp_entry(char *host_name, int prot, union all_addr *host_address, time_t ttd);
328  struct in_addr a_record_from_hosts(char *name, time_t now);
329 @@ -1158,7 +1163,6 @@ int read_hostsfile(char *filename, unsig
330                    struct crec **rhash, int hashsz);
331  
332  /* blockdata.c */
333 -#ifdef HAVE_DNSSEC
334  void blockdata_init(void);
335  void blockdata_report(void);
336  struct blockdata *blockdata_alloc(char *data, size_t len);
337 @@ -1166,7 +1170,6 @@ void *blockdata_retrieve(struct blockdat
338  struct blockdata *blockdata_read(int fd, size_t len);
339  void blockdata_write(struct blockdata *block, size_t len, int fd);
340  void blockdata_free(struct blockdata *blocks);
341 -#endif
342  
343  /* domain.c */
344  char *get_domain(struct in_addr addr);
345 --- a/src/rfc1035.c
346 +++ b/src/rfc1035.c
347 @@ -726,7 +726,7 @@ int extract_addresses(struct dns_header
348         {
349           /* everything other than PTR */
350           struct crec *newc;
351 -         int addrlen;
352 +         int addrlen = 0;
353  
354           if (qtype == T_A)
355             {
356 @@ -738,7 +738,9 @@ int extract_addresses(struct dns_header
357               addrlen = IN6ADDRSZ;
358               flags |= F_IPV6;
359             }
360 -         else 
361 +         else if (qtype == T_SRV)
362 +           flags |= F_SRV;
363 +         else
364             continue;
365             
366         cname_loop1:
367 @@ -799,39 +801,61 @@ int extract_addresses(struct dns_header
368                     {
369                       found = 1;
370                       
371 -                     /* copy address into aligned storage */
372 -                     if (!CHECK_LEN(header, p1, qlen, addrlen))
373 -                       return 0; /* bad packet */
374 -                     memcpy(&addr, p1, addrlen);
375 -                     
376 -                     /* check for returned address in private space */
377 -                     if (check_rebind)
378 +                     if (flags & F_SRV)
379                         {
380 -                         if ((flags & F_IPV4) &&
381 -                             private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
382 -                           return 1;
383 -                         
384 -                         if ((flags & F_IPV6) &&
385 -                             IN6_IS_ADDR_V4MAPPED(&addr.addr6))
386 +                          unsigned char *tmp = namep;
387 +
388 +                          if (!CHECK_LEN(header, p1, qlen, 6))
389 +                            return 0; /* bad packet */
390 +                          GETSHORT(addr.srv.priority, p1);
391 +                          GETSHORT(addr.srv.weight, p1);
392 +                          GETSHORT(addr.srv.srvport, p1);
393 +                          if (!extract_name(header, qlen, &p1, name, 1, 0))
394 +                            return 0;
395 +                          addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
396 +                          if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
397 +                            return 0;
398 +                          
399 +                          /* we overwrote the original name, so get it back here. */
400 +                          if (!extract_name(header, qlen, &tmp, name, 1, 0))
401 +                            return 0;
402 +                       }
403 +                     else
404 +                       {
405 +                         /* copy address into aligned storage */
406 +                         if (!CHECK_LEN(header, p1, qlen, addrlen))
407 +                           return 0; /* bad packet */
408 +                         memcpy(&addr, p1, addrlen);
409 +                     
410 +                         /* check for returned address in private space */
411 +                         if (check_rebind)
412                             {
413 -                             struct in_addr v4;
414 -                             v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
415 -                             if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
416 +                             if ((flags & F_IPV4) &&
417 +                                 private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
418                                 return 1;
419 +                             
420 +                             if ((flags & F_IPV6) &&
421 +                                 IN6_IS_ADDR_V4MAPPED(&addr.addr6))
422 +                               {
423 +                                 struct in_addr v4;
424 +                                 v4.s_addr = ((const uint32_t *) (&addr.addr6))[3];
425 +                                 if (private_net(v4, !option_bool(OPT_LOCAL_REBIND)))
426 +                                   return 1;
427 +                               }
428                             }
429 -                       }
430 -                     
431 +                         
432  #ifdef HAVE_IPSET
433 -                     if (ipsets && (flags & (F_IPV4 | F_IPV6)))
434 -                       {
435 -                         ipsets_cur = ipsets;
436 -                         while (*ipsets_cur)
437 +                         if (ipsets && (flags & (F_IPV4 | F_IPV6)))
438                             {
439 -                             log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
440 -                             add_to_ipset(*ipsets_cur++, &addr, flags, 0);
441 +                             ipsets_cur = ipsets;
442 +                             while (*ipsets_cur)
443 +                               {
444 +                                 log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
445 +                                 add_to_ipset(*ipsets_cur++, &addr, flags, 0);
446 +                               }
447                             }
448 -                       }
449  #endif
450 +                       }
451                       
452                       newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
453                       if (newc && cpp)
454 @@ -1844,7 +1868,68 @@ size_t answer_request(struct dns_header
455                   *up = move;
456                   move->next = NULL;
457                 }
458 -             
459 +
460 +             if (!found)
461 +               {
462 +               cname_srv_restart:
463 +                 if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | F_SRV | (dryrun ? F_NO_RR : 0))) &&
464 +                     (!do_bit || (option_bool(OPT_DNSSEC_VALID) && !(crecp->flags & F_DNSSECOK))))
465 +                   {
466 +                     if (!(crecp->flags & F_DNSSECOK))
467 +                       sec_data = 0;
468 +                     
469 +                     auth = 0;
470 +                     found = ans = 1;
471 +                     
472 +                     do {
473 +                       if (crecp->flags & F_CNAME)
474 +                         {
475 +                           char *cname_target = cache_get_cname_target(crecp);
476 +                           
477 +                           if (!dryrun)
478 +                             {
479 +                               log_query(crecp->flags, name, NULL, record_source(crecp->uid));
480 +                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
481 +                                                       crec_ttl(crecp, now), &nameoffset,
482 +                                                       T_CNAME, C_IN, "d", cname_target))
483 +                                 anscount++;
484 +                             }
485 +                           
486 +                           strcpy(name, cname_target);
487 +                           goto cname_srv_restart;
488 +                         }
489 +                       else if (crecp->flags & F_NEG)
490 +                         {
491 +                           if (crecp->flags & F_NXDOMAIN)
492 +                             nxdomain = 1;
493 +                           if (!dryrun)
494 +                             log_query(crecp->flags, name, NULL, NULL);
495 +                         }
496 +                       else 
497 +                         {
498 +                           unsigned char *p1 = ((unsigned char *)header) + nameoffset;
499 +                           
500 +                           if (!dryrun)
501 +                             {
502 +                               log_query(crecp->flags, name, NULL, 0);
503 +                               
504 +                               blockdata_retrieve(crecp->addr.srv.target, crecp->addr.srv.targetlen, name); 
505 +                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
506 +                                                       crec_ttl(crecp, now), NULL, T_SRV, C_IN, "sssd",
507 +                                                       crecp->addr.srv.priority, crecp->addr.srv.weight, crecp->addr.srv.srvport,
508 +                                                       name))
509 +                                 anscount++;
510 +                               
511 +                               
512 +                               /* restore name we overwrote */
513 +                               if (!extract_name(header, qlen, &p1, name, 1, 0))
514 +                                 return 0; /* bad packet */
515 +                             }
516 +                         }
517 +                     } while ((crecp = cache_find_by_name(crecp, name, now, F_SRV | F_CNAME)));
518 +                   }
519 +               }
520 +
521               if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
522                 {
523                   ans = 1;