]> git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/loop.c
Add --dns-loop-detect feature.
[people/ms/dnsmasq.git] / src / loop.c
1 /* dnsmasq is Copyright (c) 2000-2014 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
26 if (!option_bool(OPT_LOOP_DETECT))
27 return;
28
29 /* Loop through all upstream servers not for particular domains, and send a query to that server which is
30 identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
31 for (serv = daemon->servers; serv; serv = serv->next)
32 if (!(serv->flags &
33 (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
34 {
35 ssize_t len = loop_make_probe(serv->uid);
36 int fd;
37 struct randfd *rfd = NULL;
38
39 if (serv->sfd)
40 fd = serv->sfd->fd;
41 else
42 {
43 if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
44 continue;
45 fd = rfd->fd;
46 }
47
48 while (sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)) == -1 && retry_send());
49
50 free_rfd(rfd);
51 }
52 }
53
54 static ssize_t loop_make_probe(u32 uid)
55 {
56 struct dns_header *header = (struct dns_header *)daemon->packet;
57 unsigned char *p = (unsigned char *)(header+1);
58
59 /* packet buffer overwritten */
60 daemon->srv_save = NULL;
61
62 header->id = rand16();
63 header->ancount = header->nscount = header->arcount = htons(0);
64 header->qdcount = htons(1);
65 header->hb3 = HB3_RD;
66 header->hb4 = 0;
67 SET_OPCODE(header, QUERY);
68
69 *p++ = 8;
70 sprintf((char *)p, "%.8x", uid);
71 p += 8;
72 *p++ = strlen(LOOP_TEST_DOMAIN);
73 strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
74 p += strlen(LOOP_TEST_DOMAIN) + 1;
75
76 PUTSHORT(LOOP_TEST_TYPE, p);
77 PUTSHORT(C_IN, p);
78
79 return p - (unsigned char *)header;
80 }
81
82
83 int detect_loop(char *query, int type)
84 {
85 int i;
86 u32 uid;
87 struct server *serv;
88
89 if (!option_bool(OPT_LOOP_DETECT))
90 return 0;
91
92 if (type != LOOP_TEST_TYPE ||
93 strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
94 strstr(query, LOOP_TEST_DOMAIN) != query + 9)
95 return 0;
96
97 for (i = 0; i < 8; i++)
98 if (!isxdigit(query[i]))
99 return 0;
100
101 uid = strtol(query, NULL, 16);
102
103 for (serv = daemon->servers; serv; serv = serv->next)
104 if (!(serv->flags &
105 (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
106 uid == serv->uid)
107 {
108 serv->flags |= SERV_LOOP;
109 check_servers(); /* log new state */
110 return 1;
111 }
112
113 return 0;
114 }
115
116 #endif