Coverage Report

Created: 2025-07-09 06:38

/src/dnsmasq/src/loop.c
Line
Count
Source (jump to first uncovered line)
1
/* dnsmasq is Copyright (c) 2000-2025 Simon Kelley
2
3
   This program is free software; you can redistribute it and/or modify
4
   it under the terms of the GNU General Public License as published by
5
   the Free Software Foundation; version 2 dated June, 1991, or
6
   (at your option) version 3 dated 29 June, 2007.
7
 
8
   This program is distributed in the hope that it will be useful,
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
10
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
   GNU General Public License for more details.
12
     
13
   You should have received a copy of the GNU General Public License
14
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
*/
16
17
#include "dnsmasq.h"
18
19
#ifdef HAVE_LOOP
20
static ssize_t loop_make_probe(u32 uid);
21
22
void loop_send_probes(void)
23
0
{
24
0
   struct server *serv;
25
0
   struct randfd_list *rfds = NULL;
26
   
27
0
   if (!option_bool(OPT_LOOP_DETECT))
28
0
     return;
29
30
   /* Loop through all upstream servers not for particular domains, and send a query to that server which is
31
      identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
32
0
   for (serv = daemon->servers; serv; serv = serv->next)
33
0
     if (strlen(serv->domain) == 0 &&
34
0
   !(serv->flags & (SERV_FOR_NODOTS)))
35
0
       {
36
0
   ssize_t len = loop_make_probe(serv->uid);
37
0
   int fd;
38
   
39
0
   serv->flags &= ~SERV_LOOP;
40
41
0
   if ((fd = allocate_rfd(&rfds, serv)) == -1)
42
0
     continue;
43
   
44
0
   while (retry_send(sendto(fd, daemon->packet, len, 0, 
45
0
          &serv->addr.sa, sa_len(&serv->addr))));
46
0
       }
47
48
0
   free_rfds(&rfds);
49
0
}
50
  
51
static ssize_t loop_make_probe(u32 uid)
52
0
{
53
0
  struct dns_header *header = (struct dns_header *)daemon->packet;
54
0
  unsigned char *p = (unsigned char *)(header+1);
55
  
56
  /* packet buffer overwritten */
57
0
  daemon->srv_save = NULL;
58
  
59
0
  header->id = rand16();
60
0
  header->ancount = header->nscount = header->arcount = htons(0);
61
0
  header->qdcount = htons(1);
62
0
  header->hb3 = HB3_RD;
63
0
  header->hb4 = 0;
64
0
  SET_OPCODE(header, QUERY);
65
66
0
  *p++ = 8;
67
0
  sprintf((char *)p, "%.8x", uid);
68
0
  p += 8;
69
0
  *p++ = strlen(LOOP_TEST_DOMAIN);
70
0
  strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
71
0
  p += strlen(LOOP_TEST_DOMAIN) + 1;
72
73
0
  PUTSHORT(LOOP_TEST_TYPE, p);
74
0
  PUTSHORT(C_IN, p);
75
76
0
  return p - (unsigned char *)header;
77
0
}
78
  
79
80
int detect_loop(char *query, int type)
81
0
{
82
0
  int i;
83
0
  u32 uid;
84
0
  struct server *serv;
85
  
86
0
  if (!option_bool(OPT_LOOP_DETECT))
87
0
    return 0;
88
89
0
  if (type != LOOP_TEST_TYPE ||
90
0
      strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
91
0
      strstr(query, LOOP_TEST_DOMAIN) != query + 9)
92
0
    return 0;
93
94
0
  for (i = 0; i < 8; i++)
95
0
    if (!isxdigit((unsigned char)query[i]))
96
0
      return 0;
97
98
0
  uid = strtol(query, NULL, 16);
99
100
0
  for (serv = daemon->servers; serv; serv = serv->next)
101
0
    if (strlen(serv->domain) == 0 &&
102
0
  !(serv->flags & SERV_LOOP) &&
103
0
  uid == serv->uid)
104
0
      {
105
0
  serv->flags |= SERV_LOOP;
106
0
  check_servers(1); /* log new state - don't send more probes. */
107
0
  return 1;
108
0
      }
109
  
110
0
  return 0;
111
0
}
112
113
#endif