/* dnsmasq is Copyright (c) 2000-2024 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; struct randfd_list *rfds = NULL; 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 (strlen(serv->domain) == 0 && !(serv->flags & (SERV_FOR_NODOTS))) { ssize_t len = loop_make_probe(serv->uid); int fd; serv->flags &= ~SERV_LOOP; if ((fd = allocate_rfd(&rfds, serv)) == -1) continue; while (retry_send(sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)))); } free_rfds(&rfds); } 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((unsigned char)query[i])) return 0; uid = strtol(query, NULL, 16); for (serv = daemon->servers; serv; serv = serv->next) if (strlen(serv->domain) == 0 && !(serv->flags & SERV_LOOP) && uid == serv->uid) { serv->flags |= SERV_LOOP; check_servers(1); /* log new state - don't send more probes. */ return 1; } return 0; } #endif