我正在尝试通过WMI对静态(由用户放置)的网络接口的DNS服务器进行连接。我有这个可以工作的脚本,当然静态部分除外:
Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object {$_.DNSServerSearchOrder -ne $null} | Select DnsServerSearchOrder,Index,InterfaceIndex
这会导致如下输出:
DnsServerSearchOrder Index InterfaceIndex
-------------------- ----- --------------
{192.168.122.1} 1 6
{1.1.1.1} 2 10
与192.168.122.1
的接口具有DHCP的DNS设置,因此该值对我不利。如何过滤掉 dns 不是静态的接口?有什么想法吗?
netsh interface ip show config
:
Configuration for interface "Ethernet"
DHCP enabled: Yes
IP Address: 192.168.122.130
Subnet Prefix: 192.168.122.0/24 (mask 255.255.255.0)
Default Gateway: 192.168.122.1
Gateway Metric: 0
InterfaceMetric: 35
DNS servers configured through DHCP: 192.168.122.1
Register with which suffix: Primary only
WINS servers configured through DHCP: None
Configuration for interface "Ethernet 2"
DHCP enabled: Yes
IP Address: 10.0.0.17
Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0)
Default Gateway: 10.0.0.1
Gateway Metric: 0
InterfaceMetric: 35
Statically Configured DNS Servers: 1.1.1.1
Register with which suffix: Primary only
WINS servers configured through DHCP: None
请注意Statically Configured DNS Servers
和DNS servers configured through DHCP
之间的区别。我想我可能会解析此输出,但我不确定如果 Windows 语言/区域设置发生变化,我是否可以依赖此文本,如果可能的话,我宁愿使用 WMI 接口。
我认为这可能在System.Net.NetworkInformation
命名空间中可用,但显然不是。 我浏览了一些较低级别的Windows网络API,认为它肯定存在于其中的某个地方,但没有这样的运气。 在netsh.exe
上运行dumpbin
以查看它正在使用的库/函数类型后,我确实想到了另一个要查看的地方:注册表。
碰巧的是,如果在注册表中查看HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces
项下的注册表项,您将看到每个网络接口的密钥,其名称采用 GUID 格式。 在接口的键中,您将找到两个感兴趣的值:DhcpNameServer
和NameServer
。 在我的客户端系统上,DNS 服务器由 DHCP 设置,DhcpNameServer
包含该 DNS 服务器的 IP 地址,NameServer
包含一个空[String]
。 如果我手动设置 DNS 服务器,同时保留接口本身的自动分配地址,则NameServer
将包含该手动设置的 DNS 服务器地址,而DhcpNameServer
仍然包含 DHCP 指定的相同 DNS 服务器。
根据这些观察,似乎...
DhcpNameServer
值始终包含 DHCP 指定的 DNS 服务器。NameServer
值始终包含手动指定的 DNS 服务器。- 当您向系统查询其名称服务器时(例如,通过
Win32_NetworkAdapterConfiguration.DNSServerSearchOrder
属性),结果将包含NameServer
的值(如果提供),否则将包含DhcpNameServer
的值(如果提供)。 换句话说,系统会告诉您当前正在使用哪些 DNS 服务器,但不会告诉您它们的地址是如何指定的。 - 要确定接口是否具有手动分配的 DNS 服务器,请检查
NameServer
是否具有非空值。
因此,给定一个带有 ID$interfaceID
的接口,您可以像这样构建其注册表项的路径......
$interfaceKeyPath = "HKLM:SYSTEMCurrentControlSetServicesTcpipParametersInterfaces$interfaceID"
。然后检索动态分配和手动分配的名称服务器值,如下所示...
Get-ItemProperty -Path $interfaceKeyPath -Name 'DhcpNameServer', 'NameServer'
这只是留下了从哪里获得$interfaceID
值的问题,并且有许多来源,尽管诀窍是排除不需要的接口(例如,在我的 Windows 10 系统上,我有一个数据包捕获环回适配器和一个虚拟机管理程序适配器,我希望从此类查询中排除)。 最兼容的方式(可以追溯到 .NET 2.0)是NetworkInterface
类的Id
属性...
[System.Net.NetworkInformation.NetworkInterface]::GetAllNetworkInterfaces() `
| Select-Object -ExpandProperty 'Id'
。尽管要过滤的唯一有用属性是Name
和NetworkInterfaceType
.
在 Windows Vista 及更高版本上,Win32_NetworkAdapter
类提供了一个GUID
属性...
Get-WmiObject -Class 'Win32_NetworkAdapter' -Property 'GUID' -Filter 'PhysicalAdapter = true'
。尽管即使在PhysicalAdapter
过滤时,它仍然返回环回和虚拟机管理程序适配器,并且我没有看到任何可用于仅选择硬件适配器的明确属性或类关系。
Win32_NetworkAdapterConfiguration
类大致相同...
Get-WmiObject -Class 'Win32_NetworkAdapterConfiguration' -Property 'SettingID'
。没有属性来筛选出非硬件甚至非物理适配器。
在(我认为)Windows 8及更高版本上,有Get-NetConnectionProfile
cmdlet...
Get-NetConnectionProfile | Select-Object -ExpandProperty 'InstanceID'
它被记录为获取"与一个或多个物理网络适配器关联的连接配置文件",并且在我的系统上,它只返回我的物理适配器。
还有Get-NetAdapter
cmdlet...
Get-NetAdapter -Physical `
| Where-Object -Property 'EndPointInterface' -NE -Value $true `
| Select-Object -ExpandProperty 'InterfaceGuid'
我发现传递-Physical
参数会排除虚拟机管理程序适配器,但不会排除环回适配器,因此过滤掉EndPointInterface
$true
的位置是消除这种情况的必要条件。HardwareInterface
和Virtual
属性也可能令人感兴趣。
另一种选择是调用Get-NetAdapterHardwareInfo
cmdlet,它似乎知道如何区分真正的硬件适配器,并让它确定哪些适配器被Get-NetAdapter
检索......
Get-NetAdapterHardwareInfo `
| Get-NetAdapter `
| Select-Object -ExpandProperty 'InterfaceGuid'
上面的Get-Net*
cmdlet 返回 CIM 实例,因此,例如,您可以使用类似以下内容的内容,而不是Get-NetAdapter -Physical
...
Get-WmiObject -Namespace 'RootStandardCimv2' -Class 'MSFT_NetAdapter' `
-Property 'InterfaceGuid' -Filter 'HardwareInterface = true AND EndPointInterface = false'
以同样的方式检索MSFT_NetAdapter
实例。 我不太确定使用一个与另一个的指导是什么。 似乎人们应该更喜欢 cmdlet,但是,与 WMI/CIM 不同,它们提供有限/没有参数来有效地筛选输出或指定所需的属性,因此您必须在管道中执行此操作。 不过,我认为值得注意的是,我无法找到这些MSFT_*
类的任何当前文档;他们都说他们不再更新,除了我根本找不到任何文档页面的MSFT_NetConnectionProfile
类。 这对我来说Microsoft不希望您依赖这些类的任何明确结构,但是如果 cmdlet 只是传递这些类实例......我不确定如果没有记录,您如何与他们进行有意义和可靠的互动。
另外,请记住,在可能的情况下,您会更喜欢Get-CimInstance
及其同类而不是Get-WmiObject
。 我认为我还没有遇到过比将Get-WmiObject
更改为Get-CimInstance
更复杂的实例,尽管差异(不一定是坏的)比名称更多。
经过足够的抨击,我找到了解决方案,但我不是 100% 肯定是正确的道路。在所有 DHCP DNS 服务器上,DNS 值都包含一个 IP 地址,并且该 IP 地址等于Default Gateway
值。当这些值匹配时,我们将处理 DHCP DNS 服务器,而不是静态配置。
既然Netsh都知道了,为什么不走这条路呢?喜欢
get-netadapter | % { $adapter=$_ | Add-Member -PassThru -Name staticDNS -Type NoteProperty -Value "" | Add-Member -PassThru -Name dhcpDNS -Type NoteProperty -Value ""; $dnsInfo=netsh interface ipv4 show dns name="$($_.Name)"; $dnsInfo -split '`n' | % { if ($_ -like '*stat*10*' ) { $dummy=$_ -split ' '; $adapter.staticDNS+=@($dummy[$dummy.count-1]) }; if ($_ -like '*dhcp*' ) { $dummy=$_ -split ' '; $adapter.dhcpDNS+=@($dummy[$dummy.count-1]) } }; $adapter }