From d8b33dad0bb761a2d8651a4d10cff114879eb358 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 8 Nov 2021 14:48:36 +0100 Subject: [PATCH] dnsmasq: add support for monitoring and modifying dns lookup results via ubus The monitoring functionality will be used for dns rule support in qosify Signed-off-by: Felix Fietkau --- .../dnsmasq/patches/200-ubus_dns.patch | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 package/network/services/dnsmasq/patches/200-ubus_dns.patch diff --git a/package/network/services/dnsmasq/patches/200-ubus_dns.patch b/package/network/services/dnsmasq/patches/200-ubus_dns.patch new file mode 100644 index 0000000000..506a413ae2 --- /dev/null +++ b/package/network/services/dnsmasq/patches/200-ubus_dns.patch @@ -0,0 +1,270 @@ +--- a/src/dnsmasq.h ++++ b/src/dnsmasq.h +@@ -1564,14 +1564,26 @@ void emit_dbus_signal(int action, struct + + /* ubus.c */ + #ifdef HAVE_UBUS ++struct blob_attr; ++typedef void (*ubus_dns_notify_cb)(struct blob_attr *msg, void *priv); ++ + char *ubus_init(void); + void set_ubus_listeners(void); + void check_ubus_listeners(void); ++void drop_ubus_listeners(void); ++struct blob_buf *ubus_dns_notify_prepare(void); ++int ubus_dns_notify(const char *type, ubus_dns_notify_cb cb, void *priv); + void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface); + # ifdef HAVE_CONNTRACK + void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name); + void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *pattern, const char *ip, u32 ttl); + # endif ++#else ++struct blob_buf; ++static inline struct blob_buf *ubus_dns_notify_prepare(void) ++{ ++ return NULL; ++} + #endif + + /* ipset.c */ +--- a/src/rfc1035.c ++++ b/src/rfc1035.c +@@ -13,8 +13,10 @@ + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +- + #include "dnsmasq.h" ++#ifdef HAVE_UBUS ++#include ++#endif + + int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, + char *name, int isExtract, int extrabytes) +@@ -394,9 +396,64 @@ static int private_net6(struct in6_addr + ((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */ + } + ++#ifdef HAVE_UBUS ++static void ubus_dns_doctor_cb(struct blob_attr *msg, void *priv) ++{ ++ static const struct blobmsg_policy policy = { ++ .name = "address", ++ .type = BLOBMSG_TYPE_STRING, ++ }; ++ struct blob_attr *val; ++ char **dest = priv; ++ ++ blobmsg_parse(&policy, 1, &val, blobmsg_data(msg), blobmsg_data_len(msg)); ++ if (val) ++ *dest = blobmsg_get_string(val); ++} ++ ++static bool ubus_dns_doctor(const char *name, int ttl, void *p, int af) ++{ ++ struct blob_buf *b; ++ char *addr; ++ ++ if (!name) ++ return false; ++ ++ b = ubus_dns_notify_prepare(); ++ if (!b) ++ return false; ++ ++ blobmsg_add_string(b, "name", name); ++ ++ blobmsg_add_u32(b, "ttl", ttl); ++ ++ blobmsg_add_string(b, "type", af == AF_INET6 ? "AAAA" : "A"); ++ ++ addr = blobmsg_alloc_string_buffer(b, "address", INET6_ADDRSTRLEN); ++ if (!addr) ++ return false; ++ ++ inet_ntop(af, p, addr, INET6_ADDRSTRLEN); ++ blobmsg_add_string_buffer(b); ++ ++ addr = NULL; ++ ubus_dns_notify("dns_result", ubus_dns_doctor_cb, &addr); ++ ++ if (!addr) ++ return false; ++ ++ return inet_pton(af, addr, p) == 1; ++} ++#else ++static bool ubus_dns_doctor(const char *name, int ttl, void *p, int af) ++{ ++ return false; ++} ++#endif ++ + static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored) + { +- int i, qtype, qclass, rdlen; ++ int i, qtype, qclass, rdlen, ttl; + + for (i = count; i != 0; i--) + { +@@ -405,7 +462,7 @@ static unsigned char *do_doctor(unsigned + + GETSHORT(qtype, p); + GETSHORT(qclass, p); +- p += 4; /* ttl */ ++ GETLONG(ttl, p); /* ttl */ + GETSHORT(rdlen, p); + + if (qclass == C_IN && qtype == T_A) +@@ -416,6 +473,9 @@ static unsigned char *do_doctor(unsigned + if (!CHECK_LEN(header, p, qlen, INADDRSZ)) + return 0; + ++ if (ubus_dns_doctor(daemon->namebuff, ttl, p, AF_INET)) ++ *doctored = 1; ++ + /* alignment */ + memcpy(&addr, p, INADDRSZ); + +@@ -433,13 +493,22 @@ static unsigned char *do_doctor(unsigned + addr.s_addr &= ~doctor->mask.s_addr; + addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr); + /* Since we munged the data, the server it came from is no longer authoritative */ +- header->hb3 &= ~HB3_AA; + *doctored = 1; + memcpy(p, &addr, INADDRSZ); + break; + } + } +- ++ else if (qclass == C_IN && qtype == T_AAAA) ++ { ++ if (!CHECK_LEN(header, p, qlen, IN6ADDRSZ)) ++ return 0; ++ ++ if (ubus_dns_doctor(daemon->namebuff, ttl, p, AF_INET6)) ++ *doctored = 1; ++ } ++ ++ if (*doctored) ++ header->hb3 &= ~HB3_AA; + if (!ADD_RDLEN(header, p, qlen, rdlen)) + return 0; /* bad packet */ + } +@@ -563,7 +632,7 @@ int extract_addresses(struct dns_header + cache_start_insert(); + + /* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */ +- if (daemon->doctors || option_bool(OPT_DNSSEC_VALID)) ++ if (daemon->doctors || option_bool(OPT_DNSSEC_VALID) || ubus_dns_notify_prepare()) + { + searched_soa = 1; + ttl = find_soa(header, qlen, doctored); +--- a/src/ubus.c ++++ b/src/ubus.c +@@ -72,6 +72,14 @@ static struct ubus_object ubus_object = + .subscribe_cb = ubus_subscribe_cb, + }; + ++static struct ubus_object_type ubus_dns_object_type = ++ { .name = "dnsmasq.dns" }; ++ ++static struct ubus_object ubus_dns_object = { ++ .name = "dnsmasq.dns", ++ .type = &ubus_dns_object_type, ++}; ++ + static void ubus_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) + { + (void)ctx; +@@ -112,6 +120,8 @@ char *ubus_init() + + ubus_object.name = daemon->ubus_name; + ret = ubus_add_object(ubus, &ubus_object); ++ if (!ret) ++ ret = ubus_add_object(ubus, &ubus_dns_object); + if (ret) + { + ubus_destroy(ubus); +@@ -181,6 +191,17 @@ void check_ubus_listeners() + } \ + } while (0) + ++void drop_ubus_listeners() ++{ ++ struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; ++ ++ if (!ubus) ++ return; ++ ++ ubus_free(ubus); ++ ubus = NULL; ++} ++ + static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +@@ -328,6 +349,50 @@ fail: + } \ + } while (0) + ++struct blob_buf *ubus_dns_notify_prepare(void) ++{ ++ struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; ++ ++ if (!ubus || !ubus_dns_object.has_subscribers) ++ return NULL; ++ ++ blob_buf_init(&b, 0); ++ return &b; ++} ++ ++struct ubus_dns_notify_req { ++ struct ubus_notify_request req; ++ ubus_dns_notify_cb cb; ++ void *priv; ++}; ++ ++static void dns_notify_cb(struct ubus_notify_request *req, int type, struct blob_attr *msg) ++{ ++ struct ubus_dns_notify_req *dreq = container_of(req, struct ubus_dns_notify_req, req); ++ ++ dreq->cb(msg, dreq->priv); ++} ++ ++int ubus_dns_notify(const char *type, ubus_dns_notify_cb cb, void *priv) ++{ ++ struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; ++ struct ubus_dns_notify_req dreq; ++ int ret; ++ ++ if (!ubus || !ubus_dns_object.has_subscribers) ++ return 0; ++ ++ ret = ubus_notify_async(ubus, &ubus_dns_object, type, b.head, &dreq.req); ++ if (ret) ++ return ret; ++ ++ dreq.req.data_cb = dns_notify_cb; ++ dreq.cb = cb; ++ dreq.priv = priv; ++ ++ return ubus_complete_request(ubus, &dreq.req.req, 100); ++} ++ + void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface) + { + struct ubus_context *ubus = (struct ubus_context *)daemon->ubus; +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -1972,6 +1972,10 @@ static void check_dns_listeners(time_t n + daemon->pipe_to_parent = pipefd[1]; + } + ++#ifdef HAVE_UBUS ++ drop_ubus_listeners(); ++#endif ++ + /* start with no upstream connections. */ + for (s = daemon->servers; s; s = s->next) + s->tcpfd = -1; -- 2.30.2