diff options
| author | Felix Fietkau | 2025-05-28 21:48:18 +0000 |
|---|---|---|
| committer | Felix Fietkau | 2025-05-28 21:48:19 +0000 |
| commit | 0ce73d80dc0c3996eca0b73c76bf513a33c1dab4 (patch) | |
| tree | ef02d322e0c4ec45e102104b89068bb411949380 | |
| parent | d62813727e53c42d48f7c17e042f4cef2a1590d8 (diff) | |
| download | mdnsd-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.c | 27 | ||||
| -rw-r--r-- | dns.c | 64 | ||||
| -rw-r--r-- | dns.h | 3 |
3 files changed, 73 insertions, 21 deletions
@@ -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; } @@ -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) { @@ -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); |