/* dnsmasq is Copyright (c) 2000-2018 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 . */ #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 (retry_send(sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)))); 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