12

Given the IP address of a machine how do I get its NetBIOS name programmatically in C#? I know I can get it from the command line through "nbtstat -A ', but I'm looking for a better solution.

Jonathan Nixon
  • 4,940
  • 5
  • 39
  • 53
Rohit
  • 7,449
  • 9
  • 45
  • 55
  • Interesting question. I suspect you could query DNS to map from the IP to the hostname, which may or may not be the same as the NetBIOS name. I am unsure what APIs or libraries exist that specifically target NetBIOS. – Bruce Feb 01 '10 at 05:46

5 Answers5

4

Check Using the Socket class to request the NetBios name of a device over UDP (scroll down).

EDIT

Community has edited the URL due to 404 on original page, and changed link to pull from web.archive.org

G. Stoynev
  • 7,389
  • 6
  • 38
  • 49
Carlos Gutiérrez
  • 13,972
  • 5
  • 37
  • 47
2

I use this code which is based on an example I found once at microsoft. When it gets a result to a UDP request at port 137 it cuts of the names from the answer. It does not check if the names are valid NetBIOS names. This may be added to make it safer.

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;
        //< ----------
    }
}
marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
  • 1
    I cant believe why but this script is one of the only which is working. can you explain how you found out the values of ```nameRequest```? – Tib Schott Oct 27 '21 at 09:01
  • @TibSchott These values are from the example I found once at microsoft. When I search for the first for bytes "0x80, 0x94, 0x00, 0x00" I get several hits, may be one has an explanation. – marsh-wiggle Oct 27 '21 at 09:25
  • @TibSchott in this thread a similar code is used but not explained either: https://social.msdn.microsoft.com/Forums/vstudio/en-US/6ccac955-b6c4-4173-8390-1ce0b2fb82c8/urgent-pls-how-to-get-computer-name-from-given-fqdn-and-it-username-password. I remember that I was looking in the RFCs for the defnition of the UDP message and accidentially found that example and use it now for years. – marsh-wiggle Oct 27 '21 at 10:29
  • Didn't seem to work for me - in my case. – DennisVM-D2i May 04 '22 at 11:23
  • @DennisVM-D2i The network discovery needs to be active on the remote machine. Have you checked that? – marsh-wiggle May 04 '22 at 15:05
  • I do get results, but most (if not all of it) was garbage/non-printable characters; however, for the benefit of all, it seems like the MAC address is the item that I seem to have more success with - via code similar to this. (Maybe I'm just unlucky - for all of the hosts/devices that I've targeted.) – DennisVM-D2i May 05 '22 at 15:56
  • @DennisVM-D2i If the code is public, I would appreciate a link, thanks! :-) – marsh-wiggle May 05 '22 at 16:04
  • @marsh-wiggle It might be the same code as here - as far as I know. I found it after wasting much time trying to PInvoke 'gethostbyaddr()' instead. It left me feeling that maybe it's only the MAC address that you're likely to have more consistent luck/success with - here's the code: https://humpreyonsecurity.blogspot.com/2011/08/using-c-and-netbios-ns-to-get.html – DennisVM-D2i May 05 '22 at 16:31
  • @DennisVM-D2i The code is doing nearly the same (specially the encoding). The differnce I see: the second byte in my nameRequest is 0x94 and 0xff in your code. – marsh-wiggle May 06 '22 at 02:13
1

You could use winapi gethostbyaddr with type AF_NETBIOS.

Carlos Gutiérrez
  • 13,972
  • 5
  • 37
  • 47
0

As an aside; in terms of attempting to map the received MAC Address to an Organisation Name (/vendor), at least as a guideline this might help :

            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);

DennisVM-D2i
  • 416
  • 3
  • 8
0

I slightly finished the class presented above (NetBIOSHelper). It incorrectly gives Workgroup for Windows 7, but correctly for Windows 10. Because nbtstat for win7 first gives all names and then all groups, and win10 name, group, name, group. Well, at least in my network. Also added another boolean return parameter, it indicates whether the given computer is the master browser of this group (MasterBrowser). Well, the function now simply returns 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;
}
}

P.S. Also changed the loop condition to while (retries > 0) Because with parameter 1 it is called 2 times

P.P.S. As for the 16th byte (variable b1), I found what it is, here are its values:

  • 00 Standard Workstation Service
  • 03 Messenger Service (WinPopup)
  • 06 RAS Server Service
  • 1B Domain Master Browser Service (associated with primary domain controller)
  • 1D Master Browser name
  • 1F NetDDE Service
  • 20 Fileserver (including printer server)
  • 21 RAS Client Service
  • BE Network Monitor Agent
  • BF Network Monitor Utility

for group resources:

  • 00 Standard Workstation group
  • 1C Logon Server
  • 1D Master Browser name
  • 1E Normal Group name (used in browser elections)
  • 20 Internet Group name (administrative)
  • 01-MSBROWSE (alert about others Master Browser)

You can read more about what contains the 17th byte (variable b2) and the 18th byte here, there you already need to divide them into bits. In general, you can read more about NetBIOS in RFC 1001 and RFC 1002 and this

twobomb
  • 1
  • 1