package org.kne.cloud.network.tun; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.nio.charset.Charset; import java.util.*; import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID; /** * Windows DNS 设置管理类 * 支持获取、设置和修改网络接口的 DNS 配置 */ public class WindowsDNSManager { // 内存布局 public static final AddressLayout POINTER = ValueLayout.ADDRESS; public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; // DNS 设置常量 public static final int DNS_SETTINGS_VERSION1 = 0x0001; public static final int DNS_INTERFACE_SETTINGS_VERSION1 = 0x0001; public static final int DNS_INTERFACE_SETTINGS_VERSION2 = 0x0002; // DNS 设置标志 public static final long DNS_SETTING_IPV6 = 0x0001L; public static final long DNS_SETTING_NAMESERVER = 0x0002L; public static final long DNS_SETTING_SEARCHLIST = 0x0004L; public static final long DNS_SETTING_REGISTRATION_ENABLED = 0x0008L; public static final long DNS_SETTING_REGISTER_ADAPTER_NAME = 0x0010L; public static final long DNS_SETTING_DOMAIN = 0x0020L; public static final long DNS_SETTING_HOSTNAME = 0x0040L; public static final long DNS_SETTINGS_ENABLE_LLMNR = 0x0080L; public static final long DNS_SETTINGS_QUERY_ADAPTER_NAME = 0x0100L; public static final long DNS_SETTING_PROFILE_NAMESERVER = 0x0200L; public static final long DNS_SETTING_DISABLE_UNCONSTRAINED_QUERIES = 0x0400L; public static final long DNS_SETTING_SUPPLEMENTAL_SEARCH_LIST = 0x0800L; // DNS_SETTINGS 结构体布局 (通用 DNS 设置) public static final GroupLayout DNS_SETTINGS_LAYOUT = MemoryLayout.structLayout( DWORD.withName("Version"), // ULONG Version MemoryLayout.paddingLayout(4), DWORD64.withName("Flags"), // ULONG64 Flags POINTER.withName("Hostname"), // PWSTR Hostname POINTER.withName("Domain"), // PWSTR Domain POINTER.withName("SearchList") // PWSTR SearchList ); // DNS_INTERFACE_SETTINGS 结构体布局 (接口特定 DNS 设置 V1) public static final GroupLayout DNS_INTERFACE_SETTINGS_LAYOUT = MemoryLayout.structLayout( DWORD.withName("Version"), // ULONG Version MemoryLayout.paddingLayout(4), DWORD64.withName("Flags"), // ULONG64 Flags POINTER.withName("Domain"), // PWSTR Domain POINTER.withName("NameServer"), // PWSTR NameServer POINTER.withName("SearchList"), // PWSTR SearchList DWORD.withName("RegistrationEnabled"), // ULONG RegistrationEnabled DWORD.withName("RegisterAdapterName"), // ULONG RegisterAdapterName DWORD.withName("EnableLLMNR"), // ULONG EnableLLMNR DWORD.withName("QueryAdapterName"), // ULONG QueryAdapterName POINTER.withName("ProfileNameServer") // PWSTR ProfileNameServer ); // DNS_INTERFACE_SETTINGS_EX 结构体布局 (扩展版本) public static final GroupLayout DNS_INTERFACE_SETTINGS_EX_LAYOUT = MemoryLayout.structLayout( DNS_INTERFACE_SETTINGS_LAYOUT.withName("SettingsV1"), DWORD.withName("DisableUnconstrainedQueries"), // ULONG DisableUnconstrainedQueries MemoryLayout.paddingLayout(4), POINTER.withName("SupplementalSearchList") // PWSTR SupplementalSearchList ); private static final SymbolLookup LOOKUP; private static final Linker LINKER = Linker.nativeLinker(); // 方法句柄 private static final MethodHandle GET_INTERFACE_DNS_SETTINGS; private static final MethodHandle FREE_INTERFACE_DNS_SETTINGS; private static final MethodHandle SET_INTERFACE_DNS_SETTINGS; private static final MethodHandle FREE_DNS_SETTINGS; private static final MethodHandle GET_DNS_SETTINGS; static { try { // 加载 dnsapi.dll System.loadLibrary("dnsapi"); LOOKUP = SymbolLookup.loaderLookup(); // GetInterfaceDnsSettings 函数 // DWORD GetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings); FunctionDescriptor getInterfaceDnsSettingsDesc = FunctionDescriptor.of( DWORD, // 返回值: DWORD POINTER, // Interface: GUID* POINTER // Settings: DNS_INTERFACE_SETTINGS* ); // SetInterfaceDnsSettings 函数 // DWORD SetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings); FunctionDescriptor setInterfaceDnsSettingsDesc = FunctionDescriptor.of( DWORD, // 返回值: DWORD POINTER, // Interface: GUID* POINTER // Settings: DNS_INTERFACE_SETTINGS* ); FunctionDescriptor freeInterfaceDnsSettingsDesc = FunctionDescriptor.ofVoid( POINTER ); // FreeDnsSettings 函数 // VOID FreeDnsSettings(DNS_SETTINGS *Settings); FunctionDescriptor freeDnsSettingsDesc = FunctionDescriptor.ofVoid( POINTER // Settings: DNS_SETTINGS* ); // GetDnsSettings 函数 // DWORD GetDnsSettings(DNS_SETTINGS *Settings); FunctionDescriptor getDnsSettingsDesc = FunctionDescriptor.of( DWORD, // 返回值: DWORD POINTER // Settings: DNS_SETTINGS* ); // 获取方法句柄 GET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( LOOKUP.find("GetInterfaceDnsSettings").orElseThrow(), getInterfaceDnsSettingsDesc ); SET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( LOOKUP.find("SetInterfaceDnsSettings").orElseThrow(), setInterfaceDnsSettingsDesc ); FREE_DNS_SETTINGS = LINKER.downcallHandle( LOOKUP.find("FreeDnsSettings").orElseThrow(), freeInterfaceDnsSettingsDesc ); FREE_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( LOOKUP.find("FreeInterfaceDnsSettings").orElseThrow(), freeDnsSettingsDesc ); GET_DNS_SETTINGS = LINKER.downcallHandle( LOOKUP.find("GetDnsSettings").orElseThrow(), getDnsSettingsDesc ); } catch (Throwable e) { throw new RuntimeException("Failed to initialize Windows DNS manager", e); } } /** * DNS 设置信息类 (Java 对象版本) */ public static class DnsInterfaceSettings { private int version; private long flags; private String domain; private List nameServers; private List searchList; private boolean registrationEnabled; private boolean registerAdapterName; private boolean enableLLMNR; private boolean queryAdapterName; private String profileNameServer; private boolean disableUnconstrainedQueries; private List supplementalSearchList; public DnsInterfaceSettings() { this.version = DNS_INTERFACE_SETTINGS_VERSION1; this.flags = 0; } // Getters and Setters public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } public long getFlags() { return flags; } public void setFlags(long flags) { this.flags = flags; } public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } public List getNameServers() { return nameServers; } public void setNameServers(List nameServers) { this.nameServers = nameServers; } public List getSearchList() { return searchList; } public void setSearchList(List searchList) { this.searchList = searchList; } public boolean isRegistrationEnabled() { return registrationEnabled; } public void setRegistrationEnabled(boolean registrationEnabled) { this.registrationEnabled = registrationEnabled; } public boolean isRegisterAdapterName() { return registerAdapterName; } public void setRegisterAdapterName(boolean registerAdapterName) { this.registerAdapterName = registerAdapterName; } public boolean isEnableLLMNR() { return enableLLMNR; } public void setEnableLLMNR(boolean enableLLMNR) { this.enableLLMNR = enableLLMNR; } public boolean isQueryAdapterName() { return queryAdapterName; } public void setQueryAdapterName(boolean queryAdapterName) { this.queryAdapterName = queryAdapterName; } public String getProfileNameServer() { return profileNameServer; } public void setProfileNameServer(String profileNameServer) { this.profileNameServer = profileNameServer; } public boolean isDisableUnconstrainedQueries() { return disableUnconstrainedQueries; } public void setDisableUnconstrainedQueries(boolean disableUnconstrainedQueries) { this.disableUnconstrainedQueries = disableUnconstrainedQueries; } public List getSupplementalSearchList() { return supplementalSearchList; } public void setSupplementalSearchList(List supplementalSearchList) { this.supplementalSearchList = supplementalSearchList; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("DNS Interface Settings:\n"); sb.append(" Version: ").append(version).append("\n"); sb.append(" Flags: 0x").append(Long.toHexString(flags)).append("\n"); sb.append(" Domain: ").append(domain != null ? domain : "(null)").append("\n"); sb.append(" Name Servers: ").append(nameServers).append("\n"); sb.append(" Search List: ").append(searchList).append("\n"); sb.append(" Registration Enabled: ").append(registrationEnabled).append("\n"); sb.append(" Register Adapter Name: ").append(registerAdapterName).append("\n"); sb.append(" Enable LLMNR: ").append(enableLLMNR).append("\n"); sb.append(" Query Adapter Name: ").append(queryAdapterName).append("\n"); sb.append(" Profile Name Server: ").append(profileNameServer).append("\n"); sb.append(" Disable Unconstrained Queries: ").append(disableUnconstrainedQueries).append("\n"); sb.append(" Supplemental Search List: ").append(supplementalSearchList); return sb.toString(); } } /** * 从网卡获取 DNS 设置 * @param interfaceGuid 网卡的 GUID * @return DNS 设置对象 */ public static DnsInterfaceSettings getInterfaceDnsSettings(GUID interfaceGuid,DnsInterfaceSettings oldSettings) { try (Arena arena = Arena.ofConfined()) { // 创建 GUID 内存段 MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena); // 分配 DNS_INTERFACE_SETTINGS 结构体 (版本1) MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(oldSettings, arena); // 设置版本 dnsSettings.set(DWORD, 0, DNS_INTERFACE_SETTINGS_VERSION1); // 调用 GetInterfaceDnsSettings int result = (int) GET_INTERFACE_DNS_SETTINGS.invokeExact( guidSegment, dnsSettings ); if (result != 0) { // NO_ERROR = 0 throw new WindowsAPIException("GetInterfaceDnsSettings failed", result); } DnsInterfaceSettings resultv=parseDnsInterfaceSettings(dnsSettings, arena); FREE_INTERFACE_DNS_SETTINGS.invokeExact(dnsSettings); // 解析结构体为 Java 对象 return resultv; } catch (Throwable e) { throw new RuntimeException("Error getting interface DNS settings", e); } } /** * 应用 DNS 设置到网卡 * @param interfaceGuid 网卡的 GUID * @param settings DNS 设置对象 * @return 操作结果 (0 = 成功) */ public static void setInterfaceDnsSettings(GUID interfaceGuid, DnsInterfaceSettings settings) { try (Arena arena = Arena.ofConfined()) { // 创建 GUID 内存段 MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena); // 创建 DNS_INTERFACE_SETTINGS 结构体 MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(settings, arena); // 调用 SetInterfaceDnsSettings int result= (int) SET_INTERFACE_DNS_SETTINGS.invokeExact( guidSegment, dnsSettings ); if (result != 0) { // NO_ERROR = 0 throw new WindowsAPIException("SetInterfaceDnsSettings failed", result); } } catch (Throwable e) { throw new RuntimeException("Error setting interface DNS settings", e); } } /** * 解析 DNS_INTERFACE_SETTINGS 结构体为 Java 对象 */ private static DnsInterfaceSettings parseDnsInterfaceSettings(MemorySegment dnsSettings, Arena arena) { DnsInterfaceSettings settings = new DnsInterfaceSettings(); // 读取基本字段 int version = dnsSettings.get(DWORD, 0); settings.setVersion(version); long flags = dnsSettings.get(DWORD64, 8); settings.setFlags(flags); // 读取字符串字段 MemorySegment domainPtr = dnsSettings.get(POINTER, 16); if (!domainPtr.equals(MemorySegment.NULL)) { String domain = readWideString(domainPtr, arena); settings.setDomain(domain); }else { settings.setDomain(null); } // 读取 NameServer (可能是多个服务器,用空格分隔) MemorySegment nameServerPtr = dnsSettings.get(POINTER, 24); if (!nameServerPtr.equals(MemorySegment.NULL)) { String nameServerStr = readWideString(nameServerPtr, arena); if (nameServerStr != null && !nameServerStr.isEmpty()) { String[] servers = nameServerStr.split("\\s+"); settings.setNameServers(Arrays.asList(servers)); } }else { settings.setNameServers(null); } // 读取 SearchList (可能是多个域名,用空格分隔) MemorySegment searchListPtr = dnsSettings.get(POINTER, 32); if (!searchListPtr.equals(MemorySegment.NULL)) { String searchListStr = readWideString(searchListPtr, arena); if (searchListStr != null && !searchListStr.isEmpty()) { String[] domains = searchListStr.split("\\s+"); settings.setSearchList(Arrays.asList(domains)); } }else { settings.setSearchList(null); } // 读取布尔值字段 int registrationEnabled = dnsSettings.get(DWORD, 40); settings.setRegistrationEnabled(registrationEnabled != 0); int registerAdapterName = dnsSettings.get(DWORD, 44); settings.setRegisterAdapterName(registerAdapterName != 0); int enableLLMNR = dnsSettings.get(DWORD, 48); settings.setEnableLLMNR(enableLLMNR != 0); int queryAdapterName = dnsSettings.get(DWORD, 52); settings.setQueryAdapterName(queryAdapterName != 0); // 读取 ProfileNameServer MemorySegment profileNameServerPtr = dnsSettings.get(POINTER, 56); if (!profileNameServerPtr.equals(MemorySegment.NULL)) { String profileNameServer = readWideString(profileNameServerPtr, arena); settings.setProfileNameServer(profileNameServer); }else { settings.setProfileNameServer(null); } return settings; } /** * 创建 DNS_INTERFACE_SETTINGS 结构体的内存表示 */ private static MemorySegment createDnsInterfaceSettingsMemory(DnsInterfaceSettings settings, Arena arena) { // 分配结构体内存 MemorySegment dnsSettings = arena.allocate(DNS_INTERFACE_SETTINGS_LAYOUT); // 设置版本和标志 dnsSettings.set(DWORD, 0, settings.getVersion()); dnsSettings.set(DWORD64, 8, settings.getFlags()); // 设置 Domain if (settings.getDomain() != null) { MemorySegment domainStr = toWideString(settings.getDomain(), arena); dnsSettings.set(POINTER, 16, domainStr); } else { dnsSettings.set(POINTER, 16, MemorySegment.NULL); } // 设置 NameServer (多个服务器用空格分隔) if (settings.getNameServers()!=null) { String nameServerStr = String.join(" ", settings.getNameServers()); MemorySegment nameServerSegment = toWideString(nameServerStr, arena); dnsSettings.set(POINTER, 24, nameServerSegment); } else { dnsSettings.set(POINTER, 24, MemorySegment.NULL); } // 设置 SearchList (多个域名用空格分隔) if (settings.getSearchList()!=null) { String searchListStr = String.join(" ", settings.getSearchList()); MemorySegment searchListSegment = toWideString(searchListStr, arena); dnsSettings.set(POINTER, 32, searchListSegment); } else { dnsSettings.set(POINTER, 32, MemorySegment.NULL); } // 设置布尔值字段 dnsSettings.set(DWORD, 40, settings.isRegistrationEnabled() ? 1 : 0); dnsSettings.set(DWORD, 44, settings.isRegisterAdapterName() ? 1 : 0); dnsSettings.set(DWORD, 48, settings.isEnableLLMNR() ? 1 : 0); dnsSettings.set(DWORD, 52, settings.isQueryAdapterName() ? 1 : 0); // 设置 ProfileNameServer if (settings.getProfileNameServer() != null ) { MemorySegment profileNameServerSegment = toWideString(settings.getProfileNameServer(), arena); dnsSettings.set(POINTER, 56, profileNameServerSegment); } else { dnsSettings.set(POINTER, 56, MemorySegment.NULL); } return dnsSettings; } /** * 将 Java String 转换为 Windows 宽字符串 (UTF-16 LE) */ private static MemorySegment toWideString(String str, Arena arena) { if (str == null ) { return MemorySegment.NULL; } byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); MemorySegment segment = arena.allocate(utf16Bytes.length + 2); segment.copyFrom(MemorySegment.ofArray(utf16Bytes)); // 添加 null 终止符 segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length, (byte) 0); segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length + 1, (byte) 0); return segment; } /** * 读取 Windows 宽字符串 */ private static String readWideString(MemorySegment strPtr, Arena arena) { if (strPtr.equals(MemorySegment.NULL)) { return null; } strPtr= strPtr.reinterpret(0xffffffffL); //return strPtr.getString(0,Charset.forName("UTF-16")); try { // 计算字符串长度 (找到 null 终止符) long length = 0; while (true) { short ch = strPtr.get(ValueLayout.JAVA_SHORT, length * 2); if (ch == 0) { break; } length++; } // 读取字符串数据 byte[] bytes = new byte[(int) (length * 2)]; MemorySegment.copy(strPtr, 0, MemorySegment.ofArray(bytes), 0, bytes.length); return new String(bytes, java.nio.charset.StandardCharsets.UTF_16LE); } catch (Exception e) { return null; } } }