Imported Upstream version 2.88
[platform/upstream/dnsmasq.git] / src / loop.c
1 /* dnsmasq is Copyright (c) 2000-2022 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()
23 {
24    struct server *serv;
25    struct randfd_list *rfds = NULL;
26    
27    if (!option_bool(OPT_LOOP_DETECT))
28      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    for (serv = daemon->servers; serv; serv = serv->next)
33      if (strlen(serv->domain) == 0 &&
34          !(serv->flags & (SERV_FOR_NODOTS)))
35        {
36          ssize_t len = loop_make_probe(serv->uid);
37          int fd;
38          
39          serv->flags &= ~SERV_LOOP;
40
41          if ((fd = allocate_rfd(&rfds, serv)) == -1)
42            continue;
43          
44          while (retry_send(sendto(fd, daemon->packet, len, 0, 
45                                   &serv->addr.sa, sa_len(&serv->addr))));
46        }
47
48    free_rfds(&rfds);
49 }
50   
51 static ssize_t loop_make_probe(u32 uid)
52 {
53   struct dns_header *header = (struct dns_header *)daemon->packet;
54   unsigned char *p = (unsigned char *)(header+1);
55   
56   /* packet buffer overwritten */
57   daemon->srv_save = NULL;
58   
59   header->id = rand16();
60   header->ancount = header->nscount = header->arcount = htons(0);
61   header->qdcount = htons(1);
62   header->hb3 = HB3_RD;
63   header->hb4 = 0;
64   SET_OPCODE(header, QUERY);
65
66   *p++ = 8;
67   sprintf((char *)p, "%.8x", uid);
68   p += 8;
69   *p++ = strlen(LOOP_TEST_DOMAIN);
70   strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
71   p += strlen(LOOP_TEST_DOMAIN) + 1;
72
73   PUTSHORT(LOOP_TEST_TYPE, p);
74   PUTSHORT(C_IN, p);
75
76   return p - (unsigned char *)header;
77 }
78   
79
80 int detect_loop(char *query, int type)
81 {
82   int i;
83   u32 uid;
84   struct server *serv;
85   
86   if (!option_bool(OPT_LOOP_DETECT))
87     return 0;
88
89   if (type != LOOP_TEST_TYPE ||
90       strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
91       strstr(query, LOOP_TEST_DOMAIN) != query + 9)
92     return 0;
93
94   for (i = 0; i < 8; i++)
95     if (!isxdigit(query[i]))
96       return 0;
97
98   uid = strtol(query, NULL, 16);
99
100   for (serv = daemon->servers; serv; serv = serv->next)
101     if (strlen(serv->domain) == 0 &&
102         !(serv->flags & SERV_LOOP) &&
103         uid == serv->uid)
104       {
105         serv->flags |= SERV_LOOP;
106         check_servers(1); /* log new state - don't send more probes. */
107         return 1;
108       }
109   
110   return 0;
111 }
112
113 #endif