[英]How do I get the NetBIOS name of a machine from IP in C#?
给定机器的 IP 地址,我如何在 C# 中以编程方式获取其 NetBIOS 名称? 我知道我可以通过“nbtstat -A”从命令行获取它,但我正在寻找更好的解决方案。
我使用的代码基于我曾经在微软找到的一个例子。 当它在端口 137 获得 UDP 请求的结果时,它会从答案中删除名称。 它不检查名称是否为有效的 NetBIOS 名称。 可以添加它以使其更安全。
public class NetBIOSHelper
{
/// <summary>
/// Get the NetBIOS machine name and domain / workgroup name by ip address using UPD datagram at port 137. Based on code I found at microsoft
/// </summary>
/// <returns>True if getting an answer on port 137 with a result</returns>
public static bool GetRemoteNetBiosName(IPAddress targetAddress, out string nbName, out string nbDomainOrWorkgroupName, int receiveTimeOut = 5000, int retries = 1)
{
// The following byte stream contains the necessary message
// to request a NetBios name from a machine
byte[] nameRequest = new byte[]{
0x80, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x43, 0x4b, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x21,
0x00, 0x01 };
do
{
byte[] receiveBuffer = new byte[1024];
Socket requestSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
requestSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeOut);
nbName = null;
nbDomainOrWorkgroupName = null;
EndPoint remoteEndpoint = new IPEndPoint(targetAddress, 137);
IPEndPoint originEndpoint = new IPEndPoint(IPAddress.Any, 0);
requestSocket.Bind(originEndpoint);
requestSocket.SendTo(nameRequest, remoteEndpoint);
try
{
int receivedByteCount = requestSocket.ReceiveFrom(receiveBuffer, ref remoteEndpoint);
// Is the answer long enough?
if (receivedByteCount >= 90)
{
Encoding enc = new ASCIIEncoding();
nbName = enc.GetString(receiveBuffer, 57, 15).Trim();
nbDomainOrWorkgroupName = enc.GetString(receiveBuffer, 75, 15).Trim();
// the names may be checked if they are really valid NetBIOS names, but I don't care ....
return true;
//<----------
}
}
catch (SocketException)
{
// We get this Exception when the target is not reachable
}
retries--;
} while (retries >= 0);
return false;
//< ----------
}
}
您可以使用类型为AF_NETBIOS
winapi gethostbyaddr 。
作为旁白; 在尝试将接收到的 MAC 地址 map 发送到组织名称 (/vendor) 方面,至少作为指南,这可能会有所帮助:
IDictionary<string, string> ouiToOrgName = null;
using (var httpClnt = new System.Net.Http.HttpClient())
{
var ouiTxt =
await httpClnt.GetStringAsync(@"https://standards-oui.ieee.org/oui/oui.txt");
var ouiTxtList =
ouiTxt
.Split('\n')
.Where(x => x.Contains(@"(hex)"))
.ToList();
ouiToOrgName =
new SortedDictionary<string, string>();
foreach (var line in ouiTxtList)
{
var match = OuiTxtOuiOrgNameRe.Match(line);
if (match.Success &&
// Ignore duplicates
! ouiToOrgName.ContainsKey(match.Groups[1].Value))
{
ouiToOrgName.Add(
match.Groups[1].Value,
match.Groups[2].Value);
}
}
}
return ouiToOrgName;
...
private static readonly Regex OuiTxtOuiOrgNameRe =
new Regex(
@"((?:[0-9A-F]{2}-){2}[0-9A-F]{2})(?: *)(?:\(hex\))(?:\t*)(.*)",
// RegexOptions.Multiline |
RegexOptions.Compiled);
我稍微完成了上面介绍的class(NetBIOSHelper)。 它错误地给出了 Windows 7 的工作组,但正确地给出了 Windows 10。因为 win7 的 nbtstat 首先给出所有名称,然后给出所有组,win10 名称,组,名称,组。 好吧,至少在 my.network 中。 还增加了另一个 boolean 返回参数,它表示给定的计算机是否是该组的主浏览器(MasterBrowser)。 那么,function 现在只返回 object[], [string computername,string workgroup,bool isMasterBrowser]
public class NetBIOSHelper{
public static object[] GetRemoteNetBiosName(IPAddress targetAddress, int receiveTimeOut = 5000, int retries = 1){
byte[] nameRequest = new byte[]{
0x80, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x43, 0x4b, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x21,
0x00, 0x01 };
string workgroup = "";
string compname = "";
bool isMasterBrowser = false;
do
{
byte[] receiveBuffer = new byte[1024];
Socket requestSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
requestSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeOut);
EndPoint remoteEndpoint = new IPEndPoint(targetAddress, 137);
IPEndPoint originEndpoint = new IPEndPoint(IPAddress.Any, 0);
requestSocket.Bind(originEndpoint);
requestSocket.SendTo(nameRequest, remoteEndpoint);
try
{
int receivedByteCount = requestSocket.ReceiveFrom(receiveBuffer, ref remoteEndpoint);
if (receivedByteCount >= 90)
{
Encoding enc = new ASCIIEncoding();
for(int i = 0; i < 5; i++){
var name = enc.GetString(receiveBuffer, 57+i*18, 15).Trim();
var b1 = receiveBuffer[57 + i * 18 + 15];
var b2 = receiveBuffer[57 + i * 18 + 16];
if ((b2 == 0x84 || b2 == 0x44) && b1 == 0x00)
workgroup = name;
if ((b2 == 0x04 || b2 == 0xC4) && b1 == 0x00)
compname = name;
if (b2 == 4 && b1 == 0x1D)
isMasterBrowser = true;
}
return new object[]{
compname,
workgroup,
isMasterBrowser
};
}
}
catch (SocketException e)
{
}
retries--;
} while (retries > 0);
return null;
}
}
PS 还将循环条件更改为 while (retries > 0) 因为参数 1 它被调用了 2 次
PPS 至于第 16 个字节(变量 b1),我找到了它是什么,这是它的值:
对于组资源:
您可以在此处阅读更多关于包含第 17 个字节(变量 b2)和第 18 个字节的内容,您已经需要将它们分成几位。 但在我的示例中,我设置了出现在 my.network 中的值。 通常,您可以在RFC 1001和RFC 1002中阅读有关 NetBIOS 的更多信息
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.