Files
JavaTUN/src/org/kne/cloud/network/tun/WindowsDNSManager.java
T

492 lines
20 KiB
Java

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<String> nameServers;
private List<String> searchList;
private boolean registrationEnabled;
private boolean registerAdapterName;
private boolean enableLLMNR;
private boolean queryAdapterName;
private String profileNameServer;
private boolean disableUnconstrainedQueries;
private List<String> 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<String> getNameServers() { return nameServers; }
public void setNameServers(List<String> nameServers) { this.nameServers = nameServers; }
public List<String> getSearchList() { return searchList; }
public void setSearchList(List<String> 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<String> getSupplementalSearchList() { return supplementalSearchList; }
public void setSupplementalSearchList(List<String> 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;
}
}
}