blob: 758674bf26bed5b497f12a89ae0138de34add5c1 [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +01002
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
20static ssize_t loop_make_probe(u32 uid);
21
22void loop_send_probes()
23{
24 struct server *serv;
Simon Kelley74d4fcd2021-03-15 21:59:51 +000025 struct randfd_list *rfds = NULL;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +010026
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 (!(serv->flags &
34 (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
35 {
36 ssize_t len = loop_make_probe(serv->uid);
37 int fd;
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +010038
Simon Kelley74d4fcd2021-03-15 21:59:51 +000039 if ((fd = allocate_rfd(&rfds, serv)) == -1)
40 continue;
41
Simon Kelleyff841eb2015-03-11 21:36:30 +000042 while (retry_send(sendto(fd, daemon->packet, len, 0,
43 &serv->addr.sa, sa_len(&serv->addr))));
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +010044 }
Simon Kelley74d4fcd2021-03-15 21:59:51 +000045
46 free_rfds(&rfds);
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +010047}
48
49static ssize_t loop_make_probe(u32 uid)
50{
51 struct dns_header *header = (struct dns_header *)daemon->packet;
52 unsigned char *p = (unsigned char *)(header+1);
Simon Kelley74d4fcd2021-03-15 21:59:51 +000053
Simon Kelleyb5ea1cc2014-07-29 16:34:14 +010054 /* packet buffer overwritten */
55 daemon->srv_save = NULL;
56
57 header->id = rand16();
58 header->ancount = header->nscount = header->arcount = htons(0);
59 header->qdcount = htons(1);
60 header->hb3 = HB3_RD;
61 header->hb4 = 0;
62 SET_OPCODE(header, QUERY);
63
64 *p++ = 8;
65 sprintf((char *)p, "%.8x", uid);
66 p += 8;
67 *p++ = strlen(LOOP_TEST_DOMAIN);
68 strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
69 p += strlen(LOOP_TEST_DOMAIN) + 1;
70
71 PUTSHORT(LOOP_TEST_TYPE, p);
72 PUTSHORT(C_IN, p);
73
74 return p - (unsigned char *)header;
75}
76
77
78int detect_loop(char *query, int type)
79{
80 int i;
81 u32 uid;
82 struct server *serv;
83
84 if (!option_bool(OPT_LOOP_DETECT))
85 return 0;
86
87 if (type != LOOP_TEST_TYPE ||
88 strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
89 strstr(query, LOOP_TEST_DOMAIN) != query + 9)
90 return 0;
91
92 for (i = 0; i < 8; i++)
93 if (!isxdigit(query[i]))
94 return 0;
95
96 uid = strtol(query, NULL, 16);
97
98 for (serv = daemon->servers; serv; serv = serv->next)
99 if (!(serv->flags &
100 (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
101 uid == serv->uid)
102 {
103 serv->flags |= SERV_LOOP;
104 check_servers(); /* log new state */
105 return 1;
106 }
107
108 return 0;
109}
110
111#endif