-- SPDX-License-Identifier: GPL-3.0-or-later -- Module interface local ffi = require('ffi') local basexx = require('basexx') local C = ffi.C -- Export module interface local M = {} M.layer = {} local base32 = {} local str = {} local AF_INET = 2 local AF_INET6 = 10 local INET_ADDRSTRLEN = 16 local INET6_ADDRSTRLEN = 46 ffi.cdef[[ /* * Data structures */ typedef int socklen_t; struct sockaddr_storage{ unsigned short int ss_family; unsigned long int __ss_align; char __ss_padding[128 - (2 *sizeof(unsigned long int))]; }; struct in_addr{ unsigned char s_addr[4]; }; struct in6_addr{ unsigned char s6_addr[16]; }; struct sockaddr_in{ short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; } __attribute__ ((__packed__)); struct sockaddr_in6{ unsigned short sin6_family; unsigned short sin6_port; unsigned int sin6_flowinfo; struct in6_addr sin6_addr; unsigned int sin6_scope_id; }; typedef unsigned short sa_family_t; struct sockaddr_un { sa_family_t sun_family; char sun_path[108]; }; const char *inet_ntop( int af, const void *cp, char *buf, socklen_t len); ]] function base32.pad(b32) local m = #b32 % 8 if m ~= 0 then b32 = b32 .. string.rep("=", 8 - m) end return b32 end function str.starts(String,Start) return string.sub(String,1,string.len(Start))==Start end -- Handle DoT signalling NS domains. function M.layer.consume(state, _, pkt) -- Only successful answers if state == kres.FAIL then return state end -- log_debug(ffi.C.LOG_GRP_DOTAUTH, "%s", pkt:tostring()) local authority = pkt:section(kres.section.AUTHORITY) local additional = pkt:section(kres.section.ADDITIONAL) for _, rr in ipairs(authority) do --log_debug(ffi.C.LOG_GRP_DOTAUTH, "%d %s", rr.type, kres.dname2str(rr.rdata)) if rr.type == kres.type.NS then local name = kres.dname2str(rr.rdata):upper() -- log_debug(ffi.C.LOG_GRP_DOTAUTH, "NS %d", name:len()) if name:len() > 56 and str.starts(name, "DOT-") then local k = basexx.to_base64( basexx.from_base32( base32.pad(string.sub(name, 5, string.find(name, '[.]') - 1)) ) ) for _, rr_add in ipairs(additional) do if rr_add.type == kres.type.A or rr_add.type == kres.type.AAAA then local name_add = kres.dname2str(rr_add.owner):upper() if name == name_add then local addrbuf if rr_add.type == kres.type.A then local ns_addr = ffi.new("struct sockaddr_in") ns_addr.sin_family = AF_INET ns_addr.sin_addr.s_addr = rr_add.rdata addrbuf = ffi.new("char[?]", INET_ADDRSTRLEN) C.inet_ntop(AF_INET, ns_addr.sin_addr, addrbuf, INET_ADDRSTRLEN) else local ns_addr = ffi.new("struct sockaddr_in6") ns_addr.sin6_family = AF_INET6 ns_addr.sin6_addr.s6_addr = rr_add.rdata addrbuf = ffi.new("char[?]", INET6_ADDRSTRLEN) C.inet_ntop(AF_INET6, ns_addr.sin6_addr, addrbuf, INET6_ADDRSTRLEN) end net.tls_client(ffi.string(addrbuf).."@853", {k}) log_info(ffi.C.LOG_GRP_DOTAUTH, "Adding %s IP %s %s", name_add, ffi.string(addrbuf).."@853", k) end end end end end end return state end return M