summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2025-05-28 21:48:18 +0000
committerFelix Fietkau2025-05-28 21:48:19 +0000
commit0ce73d80dc0c3996eca0b73c76bf513a33c1dab4 (patch)
treeef02d322e0c4ec45e102104b89068bb411949380
parentd62813727e53c42d48f7c17e042f4cef2a1590d8 (diff)
downloadmdnsd-0ce73d80dc0c3996eca0b73c76bf513a33c1dab4.tar.gz
dns: add cache/queue for outgoing queries
Batch outgoing questions and drop redundant ones. Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--cache.c27
-rw-r--r--dns.c64
-rw-r--r--dns.h3
3 files changed, 73 insertions, 21 deletions
diff --git a/cache.c b/cache.c
index 51eb707..0ccac49 100644
--- a/cache.c
+++ b/cache.c
@@ -143,26 +143,17 @@ void
cache_update(void)
{
struct cache_service *s;
- int count = 2;
- dns_packet_init();
- dns_packet_question(C_DNS_SD, TYPE_ANY);
- dns_packet_question(C_DNS_SD, TYPE_PTR);
+ dns_query(C_DNS_SD, TYPE_ANY);
+ dns_query(C_DNS_SD, TYPE_PTR);
avl_for_each_element(&services, s, avl) {
if (s->host) {
- dns_packet_question(s->host, TYPE_A);
- dns_packet_question(s->host, TYPE_AAAA);
+ dns_query(s->host, TYPE_A);
+ dns_query(s->host, TYPE_AAAA);
} else {
- dns_packet_question(s->entry, TYPE_PTR);
+ dns_query(s->entry, TYPE_PTR);
}
- if (++count < 16)
- continue;
- dns_packet_broadcast();
- count = 0;
}
-
- if (count)
- dns_packet_broadcast();
}
static struct cache_service*
@@ -203,14 +194,12 @@ cache_service(struct interface *iface, char *entry, int hlen, int ttl)
s->avl.key = type;
avl_insert(&services, &s->avl);
- dns_packet_init();
if (hlen) {
- dns_packet_question(s->host, TYPE_A);
- dns_packet_question(s->host, TYPE_AAAA);
+ dns_query(s->host, TYPE_A);
+ dns_query(s->host, TYPE_AAAA);
} else {
- dns_packet_question(entry, TYPE_PTR);
+ dns_query(entry, TYPE_PTR);
}
- dns_packet_broadcast();
return s;
}
diff --git a/dns.c b/dns.c
index 715125b..7c24ddc 100644
--- a/dns.c
+++ b/dns.c
@@ -31,6 +31,7 @@
#include <libubox/uloop.h>
#include <libubox/usock.h>
#include <libubox/utils.h>
+#include <libubox/avl-cmp.h>
#include "announce.h"
#include "util.h"
@@ -39,6 +40,15 @@
#include "service.h"
#include "interface.h"
+#define QUERY_BATCH_SIZE 16
+
+struct query_entry {
+ struct avl_node node;
+ uint16_t type;
+ char name[];
+};
+
+static AVL_TREE(queries, avl_strcmp, true, NULL);
static char name_buffer[MAX_NAME_LEN + 1];
static struct {
@@ -185,7 +195,7 @@ void dns_packet_send(struct interface *iface, struct sockaddr *to, bool query, i
perror("failed to send answer");
}
-void dns_packet_broadcast(void)
+static void dns_packet_broadcast(void)
{
struct interface *iface;
@@ -202,6 +212,58 @@ dns_send_question(struct interface *iface, struct sockaddr *to,
dns_packet_send(iface, to, true, multicast);
}
+static void
+dns_query_pending(struct uloop_timeout *t)
+{
+ struct query_entry *e, *tmp;
+ int count = 0;
+
+ dns_packet_init();
+ avl_remove_all_elements(&queries, e, node, tmp) {
+ dns_packet_question(e->name, e->type);
+ free(e);
+
+ if (++count < QUERY_BATCH_SIZE)
+ continue;
+
+ count = 0;
+ dns_packet_broadcast();
+ }
+
+ if (count)
+ dns_packet_broadcast();
+}
+
+void dns_query(const char *name, uint16_t type)
+{
+ static struct uloop_timeout timer = {
+ .cb = dns_query_pending
+ };
+ struct query_entry *e;
+
+ e = avl_find_element(&queries, name, e, node);
+ while (e) {
+ if (e->type == type)
+ return;
+
+ e = avl_next_element(e, node);
+ if (strcmp(e->name, name) != 0)
+ break;
+ }
+
+ e = calloc(1, sizeof(*e) + strlen(name) + 1);
+ e->type = type;
+ e->node.key = e->name;
+ strcpy(e->name, name);
+ avl_insert(&queries, &e->node);
+
+ if (queries.count > QUERY_BATCH_SIZE)
+ timer.cb(&timer);
+
+ if (!timer.pending)
+ uloop_timeout_set(&timer, 100);
+}
+
void
dns_reply_a(struct interface *iface, struct sockaddr *to, int ttl, const char *hostname)
{
diff --git a/dns.h b/dns.h
index 65efcf7..fde049f 100644
--- a/dns.h
+++ b/dns.h
@@ -77,7 +77,8 @@ void dns_packet_init(void);
bool dns_packet_question(const char *name, int type);
void dns_packet_answer(const char *name, int type, const uint8_t *rdata, uint16_t rdlength, int ttl);
void dns_packet_send(struct interface *iface, struct sockaddr *to, bool query, int multicast);
-void dns_packet_broadcast(void);
+
+void dns_query(const char *name, uint16_t type);
void dns_send_question(struct interface *iface, struct sockaddr *to,
const char *question, int type, int multicast);