492 lines
20 KiB
Java
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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} |