dns: add support for reverse address mapping queries
authorRahul Thakur <[email protected]>
Tue, 20 Feb 2024 07:30:17 +0000 (13:00 +0530)
committerFelix Fietkau <[email protected]>
Wed, 28 May 2025 22:40:37 +0000 (00:40 +0200)
The mdnsd currently does not have the capability of responding to
reverse address mapping dns requests for either IPv4 or IPv6.

This commit adds support to handle reverse address mapping and
respond with hostname if the address matches that of the DUT.

Signed-off-by: Rahul Thakur <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]> [cleanup]
dns.c

diff --git a/dns.c b/dns.c
index a20fb3ee17c22b724f6bbf5a710f42d14b23ec34..e2db85100e348d7abe50657680a7d99e45578b8e 100644 (file)
--- a/dns.c
+++ b/dns.c
@@ -440,13 +440,144 @@ static int parse_answer(struct interface *iface, struct sockaddr *from,
        return 0;
 }
 
+static int
+match_ipv6_addresses(char *reverse_ip, struct in6_addr *intf_ip)
+{
+       int i = 0, j = 0, idx = 0;
+       char temp_ip[INET6_ADDRSTRLEN] = "";
+       struct in6_addr buf;
+
+       for (i = strlen(reverse_ip) - 1; i >= 0; i--) {
+               if (reverse_ip[i] == '.')
+                       continue;
+
+               if (j == 4) {
+                       temp_ip[idx] = ':';
+                       idx++;
+                       j = 0;
+               }
+               temp_ip[idx] = reverse_ip[i];
+               idx++;
+               j++;
+       }
+
+       if (inet_pton(AF_INET6, temp_ip, &buf) <= 0)
+               return 0;
+
+       return !memcmp(&buf, intf_ip, sizeof(buf));
+}
+
+static int
+match_ip_addresses(char *reverse_ip, char *intf_ip)
+{
+       int ip1[4], ip2[4];
+
+       sscanf(reverse_ip, "%d.%d.%d.%d", &ip1[3], &ip1[2], &ip1[1], &ip1[0]);
+       sscanf(intf_ip, "%d.%d.%d.%d", &ip2[0], &ip2[1], &ip2[2], &ip2[3]);
+
+       int i;
+       for (i = 0; i < 4; i++) {
+               if (ip1[i] != ip2[i])
+                       return 0;
+       }
+       return 1;
+}
+
+static void
+dns_reply_reverse_ip6_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip)
+{
+       struct ifaddrs *ifap, *ifa;
+       struct sockaddr_in6 *sa6;
+
+       char intf_ip[INET6_ADDRSTRLEN] = "";
+       uint8_t buffer[256];
+       int len;
+
+       getifaddrs(&ifap);
+       dns_packet_init();
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               if (strcmp(ifa->ifa_name, iface->name))
+                       continue;
+               if (ifa->ifa_addr->sa_family == AF_INET6) {
+                       sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+                       if (inet_ntop(AF_INET6, &sa6->sin6_addr, intf_ip, INET6_ADDRSTRLEN) == NULL)
+                               continue;
+
+                       if (match_ipv6_addresses(reverse_ip, &sa6->sin6_addr)) {
+                               len = dn_comp(mdns_hostname_local, buffer, sizeof(buffer), NULL, NULL);
+
+                               if (len < 1)
+                                       continue;
+
+                               dns_packet_answer(name, TYPE_PTR, buffer, len, ttl);
+                       }
+               }
+       }
+       dns_packet_send(iface, to, 0, 0);
+
+       freeifaddrs(ifap);
+}
+
+static void
+dns_reply_reverse_ip4_mapping(struct interface *iface, struct sockaddr *to, int ttl, char *name, char *reverse_ip)
+{
+       struct ifaddrs *ifap, *ifa;
+       struct sockaddr_in *sa;
+
+       char intf_ip[INET_ADDRSTRLEN] = "";
+       uint8_t buffer[256];
+       int len;
+
+       getifaddrs(&ifap);
+       dns_packet_init();
+       for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+               if (strcmp(ifa->ifa_name, iface->name))
+                       continue;
+               if (ifa->ifa_addr->sa_family == AF_INET) {
+                       sa = (struct sockaddr_in *) ifa->ifa_addr;
+                       if (inet_ntop(AF_INET, &sa->sin_addr, intf_ip, INET_ADDRSTRLEN) == NULL)
+                               continue;
+
+                       if (match_ip_addresses(reverse_ip, intf_ip)) {
+                               len = dn_comp(mdns_hostname_local, buffer, sizeof(buffer), NULL, NULL);
+
+                               if (len < 1)
+                                       continue;
+
+                               dns_packet_answer(name, TYPE_PTR, buffer, len, ttl);
+                       }
+               }
+       }
+       dns_packet_send(iface, to, 0, 0);
+
+       freeifaddrs(ifap);
+}
+
+static bool
+is_reverse_dns_query(const char *name, const char *suffix)
+{
+       if (!name || !suffix)
+               return false;
+
+       size_t name_len = strlen(name);
+       size_t suffix_len = strlen(suffix);
+
+       if (suffix_len > name_len)
+               return false;
+
+       if (strncmp(name + (name_len - suffix_len), suffix, suffix_len) == 0)
+               return true;
+
+       return false;
+}
+
 static void
 parse_question(struct interface *iface, struct sockaddr *from, char *name, struct dns_question *q)
 {
        int is_unicast = (q->class & CLASS_UNICAST) != 0;
        struct sockaddr *to = NULL;
        struct hostname *h;
-       char *host;
+       char *host, *host6;
 
        /* TODO: Multicast if more than one quarter of TTL has passed */
        if (is_unicast) {
@@ -467,6 +598,24 @@ parse_question(struct interface *iface, struct sockaddr *from, char *name, struc
                break;
 
        case TYPE_PTR:
+               if (is_reverse_dns_query(name, ".in-addr.arpa")) {
+                       host = strstr(name, ".in-addr.arpa");
+                       char name_buf[256];
+                       strcpy(name_buf, name);
+                       *host = '\0';
+                       dns_reply_reverse_ip4_mapping(iface, to, announce_ttl, name_buf, name);
+                       break;
+               }
+
+               if (is_reverse_dns_query(name, ".ip6.arpa")) {
+                       host6 = strstr(name, ".ip6.arpa");
+                       char name_buf6[256];
+                       strcpy(name_buf6, name);
+                       *host6 = '\0';
+                       dns_reply_reverse_ip6_mapping(iface, to, announce_ttl, name_buf6, name);
+                       break;
+               }
+
                if (!strcasecmp(name, C_DNS_SD)) {
                        service_announce_services(iface, to, announce_ttl);
                } else {