summaryrefslogtreecommitdiff
path: root/src/loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/loop.c')
-rw-r--r--src/loop.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/src/loop.c b/src/loop.c
new file mode 100644
index 0000000..bb377ad
--- /dev/null
+++ b/src/loop.c
@@ -0,0 +1,116 @@
+/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_LOOP
+static ssize_t loop_make_probe(u32 uid);
+
+void loop_send_probes()
+{
+ struct server *serv;
+
+ if (!option_bool(OPT_LOOP_DETECT))
+ return;
+
+ /* Loop through all upstream servers not for particular domains, and send a query to that server which is
+ identifiable, via the uid. If we see that query back again, then the server is looping, and we should not use it. */
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if (!(serv->flags &
+ (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)))
+ {
+ ssize_t len = loop_make_probe(serv->uid);
+ int fd;
+ struct randfd *rfd = NULL;
+
+ if (serv->sfd)
+ fd = serv->sfd->fd;
+ else
+ {
+ if (!(rfd = allocate_rfd(serv->addr.sa.sa_family)))
+ continue;
+ fd = rfd->fd;
+ }
+
+ while (sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)) == -1 && retry_send());
+
+ free_rfd(rfd);
+ }
+}
+
+static ssize_t loop_make_probe(u32 uid)
+{
+ struct dns_header *header = (struct dns_header *)daemon->packet;
+ unsigned char *p = (unsigned char *)(header+1);
+
+ /* packet buffer overwritten */
+ daemon->srv_save = NULL;
+
+ header->id = rand16();
+ header->ancount = header->nscount = header->arcount = htons(0);
+ header->qdcount = htons(1);
+ header->hb3 = HB3_RD;
+ header->hb4 = 0;
+ SET_OPCODE(header, QUERY);
+
+ *p++ = 8;
+ sprintf((char *)p, "%.8x", uid);
+ p += 8;
+ *p++ = strlen(LOOP_TEST_DOMAIN);
+ strcpy((char *)p, LOOP_TEST_DOMAIN); /* Add terminating zero */
+ p += strlen(LOOP_TEST_DOMAIN) + 1;
+
+ PUTSHORT(LOOP_TEST_TYPE, p);
+ PUTSHORT(C_IN, p);
+
+ return p - (unsigned char *)header;
+}
+
+
+int detect_loop(char *query, int type)
+{
+ int i;
+ u32 uid;
+ struct server *serv;
+
+ if (!option_bool(OPT_LOOP_DETECT))
+ return 0;
+
+ if (type != LOOP_TEST_TYPE ||
+ strlen(LOOP_TEST_DOMAIN) + 9 != strlen(query) ||
+ strstr(query, LOOP_TEST_DOMAIN) != query + 9)
+ return 0;
+
+ for (i = 0; i < 8; i++)
+ if (!isxdigit(query[i]))
+ return 0;
+
+ uid = strtol(query, NULL, 16);
+
+ for (serv = daemon->servers; serv; serv = serv->next)
+ if (!(serv->flags &
+ (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_LOOP)) &&
+ uid == serv->uid)
+ {
+ serv->flags |= SERV_LOOP;
+ check_servers(); /* log new state */
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif