如果我有一个机器的IP地址,如何在C#中以编程方式获取其NetBIOS名称? 我知道我可以通过命令行使用“nbtstat -A”获取它,但我正在寻找更好的解决方案。
如果我有一个机器的IP地址,如何在C#中以编程方式获取其NetBIOS名称? 我知道我可以通过命令行使用“nbtstat -A”获取它,但我正在寻找更好的解决方案。
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;
//< ----------
}
}
nameRequest
的值的吗? - Tib Schott顺便提一下,在尝试将接收到的MAC地址映射到组织名称(/供应商)方面,至少作为指南,这可能会有所帮助:
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);
我稍微修改了上面介绍的类(NetBIOSHelper)。 它在Windows 7中错误地给出了工作组,但在Windows 10中是正确的。 因为win7的nbtstat首先给出所有名称,然后是所有组,而win10则是名称、组、名称、组。至少在我的网络中是这样的。 此外,我添加了另一个布尔返回参数, 它指示给定的计算机是否是该组的主浏览器(MasterBrowser)。 现在,该函数简单地返回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 < receiveBuffer[56]; i++){
var name = enc.GetString(receiveBuffer, 57+i*18, 15).Trim();
byte b1 = receiveBuffer[57 + i * 18 + 15];
byte b2 = receiveBuffer[57 + i * 18 + 16];
if ((b2 & (1 << 7)) != 0 && (b2 & (1 << 2)) != 0 && b1 == 0x00)
workgroup = name;
if ((b2 & (1 << 7)) == 0 && (b2 & (1 << 2)) != 0 && b1 == 0x00)
compname = name;
if ((b2 & (1 << 2)) != 0 && b1 == 0x1D)
isMasterBrowser = true;
}
return new object[]{
compname,
workgroup,
isMasterBrowser
};
}
}
catch (SocketException e)
{
}
retries--;
} while (retries > 0);
return null;
}
}
附言:还将循环条件更改为 while (retries > 0),因为使用参数1时会调用2次
另外关于第16个字节(变量b1),我找到了它的值,以下是它们的含义:
对于组资源:
您可以阅读更多关于第17个字节(变量b2)和第18个字节包含的内容这里, 在那里您需要将它们分割为位。 总的来说,您可以在RFC 1001和RFC 1002以及这里中了解更多NetBIOS知识。