How to find Windows PCs in an IP range

313 Views Asked by At

I want to scan a network and enumerate hostname of all windows machines. There is an interface method that takes an ip range as input and returns hostnames. I have to implement it. So, here is my code:

public ICollection<string> EnumerateWindowsComputers(ICollection<string> ipList)
{
    ICollection<string> hostNames = new List<string>();

    foreach (var ip in ipList)
    {
        var hostName = GetHostName(ip);

        if (string.IsNullOrEmpty(hostName) == false)
        {
            hostNames.Add(hostName)
        }
    }

    return hostNames;
}

private static string GetHostName(string ipAddress)
{
    try
    {
        IPHostEntry entry = Dns.GetHostEntry(ipAddress);
        if (entry != null)
        {
            return entry.HostName;
        }
    }
    catch (SocketException ex)
    {
        System.Console.WriteLine(ex.Message + " - " + ipAddress);
    }

    return null;
}

This method enumerates all windows machines successfully, but there are network printers in it. I can easily ignore my printers' hostname, but it will not be a good solution. I have to make sure that only the devices with the Windows operating system returned.

Any idea how to do it without a third party library? If there is a better way, we don't have to use GetHostName method.

P.S. Linux, MacOS, Android and IOS devices are not found as expected.

3

There are 3 best solutions below

0
madjack On BEST ANSWER

According to @Jeroen van Langen's comment, I changed my GetHostName method with GetWindowsHostName.

private string GetWindowsHostName(string ipAddress)
{
    try
    {
        IPHostEntry entry = Dns.GetHostEntry(ipAddress);
        if (entry != null)
        {
            try
            {
                using (TcpClient tcpClient = new TcpClient())
                {
                    // 445 is default TCP SMB port
                    tcpClient.Connect(ipAddress, 445);
                }

                using (TcpClient tcpClient = new TcpClient())
                {
                    // 139 is default TCP NetBIOS port.
                    tcpClient.Connect(ipAddress, 139);
                }

                return entry.HostName;
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.Message);
            }
        }
    }
    catch (SocketException ex)
    {
        System.Console.WriteLine(ex.Message + " - " + ipAddress);
    }

    return null;
}

There can be false positive, but this is unlikely and acceptable for me.

0
SACn On

Service detection would not be true as there may be linux or other box emulating Windows FileSharing

Use systeminfo /s IPADDRESS shell command from Windows Machine to reliably fetch remote Windows OS details. You code will be like following:

string IPADDRESS = "192.168.1.1";

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;

p.startInfo.FileName = "cmd.exe";
p.startInfo.Arguments = "/C systeminfo /s IPADDRESS";
p.Start();
p.WaitForExit();

string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();

if(output.Contains("Microsoft Windows")) { Console.WriteLine("Windows OS"); }
0
Fabian De La Pena Montero On

One way you can attempt to detect the OS in a remote machine is through the use of ping. Ping each IP address and get the TTL. That should give you an idea of the OS you're dealing with. A table matching TTL to OS can be found here: http://www.kellyodonnell.com/content/determining-os-type-ping