114 lines
2.9 KiB
C
114 lines
2.9 KiB
C
/* 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 <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;
|
|
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
|