Use Sockets to Scan the Ports on a Machine




Use Sockets to Scan the Ports on a Machine

Problem

You want to determine the open ports on a machine to see where the security risks are.

Solution

Use the CheapoPortScanner class constructed for your use; its code is shown in Figure. CheapoPortScanner uses the Socket class to attempt to open a socket and connect to an address on a given port. The OpenPortFound event is available for a callback when an open port is found in the range supplied to the CheapoPortScanner constructor or in the default range (1 to 65535). By default, CheapoPortScanner will scan the local machine.

CheapoPortScanner class

class CheapoPortScanner
{
    #region Private consts and members
    const int PORT_MIN_VALUE = 1;
    const int PORT_MAX_VALUE = 65535;

    private int _minPort = PORT_MIN_VALUE;
    private int _maxPort = PORT_MAX_VALUE;
    private List<int> _openPorts = null;
    private List<int> _closedPorts = null;
    private string _host = "127.0.0.1"; // localhost
    #endregion

    #region Event
    public class OpenPortEventArgs : EventArgs
    {
        int _portNum;
        public OpenPortEventArgs(int portNum) : base()
        {
            _portNum = portNum;
        }

        public int PortNum
        {
        get { return _portNum; }
        }
    }

    public delegate void OpenPortFoundEventHandler(object sender, OpenPortEventArgs args);
    public event OpenPortFoundEventHandler OpenPortFound;
    #endregion // Event

    #region CTORs & Init code
    public CheapoPortScanner()
    {
        // Defaults are already set for ports and localhost
        SetupLists();
    }

    public CheapoPortScanner(string host, int minPort, int maxPort)
    {
        if (minPort > maxPort)
            throw new
              ArgumentException("Min port cannot be greater than max port");
        if (minPort < PORT_MIN_VALUE || minPort > PORT_MAX_VALUE)
            throw new ArgumentOutOfRangeException("Min port cannot be less than "+
                          PORT_MIN_VALUE + " or greater than " + PORT_MAX_VALUE);
        if (maxPort < PORT_MIN_VALUE || maxPort > PORT_MAX_VALUE)
            throw new ArgumentOutOfRangeException("Max port cannot be less than "+
                          PORT_MIN_VALUE + " or greater than " + PORT_MAX_VALUE);

        _host = host;
        _minPort = minPort;
        _maxPort = maxPort;
        SetupLists();
    }

    private void SetupLists()
    {
        // Set up lists with capacity to hold half of range
        // Since we can't know how many ports are going to be open,
        // we compromise and allocate enough for half

        // rangeCount is max - min + 1
        int rangeCount = (_maxPort - _minPort) + 1;

        // If there are an odd number, bump by one to get one extra slot.
        if (rangeCount % 2 != 0)
            rangeCount += 1;

        // Reserve half the ports in the range for each
        _openPorts = new List<int>(rangeCount / 2);
        _closedPorts = new List<int>(rangeCount / 2);
    }
    #endregion // CTORs & Init code

There are two properties on CheapoPortScanner that bear mentioning. The OpenPorts and ClosedPorts properties return a ReadOnlyCollection of type int that is a list of the ports that are open and closed, respectively. Their code is shown in Figure.

OpenPorts and ClosedPorts properties

#region Properties

public ReadOnlyCollection<int> OpenPorts
{
    get { return new ReadOnlyCollection<int>(_openPorts); }
}


public ReadOnlyCollection<int> ClosedPorts
{
    get { return new ReadOnlyCollection<int>(_closedPorts); }
}
#endregion // Properties

#region Private Methods
private void CheckPort(int port)
{
    if (IsPortOpen(port))
    {
        // If we got here it is open
        _openPorts.Add(port);

        // Notify anyone paying attention
        OpenPortFoundEventHandler openPortFound = OpenPortFound;
        if (openPortFound != null)
         openPortFound(this, new OpenPortEventArgs(port));
    }
    else
    {
        // Server doesn't have that port open
        _closedPorts.Add(port);
    }
}

private bool IsPortOpen(int port)
{
    Socket sock = null;
    try
    {
        // Make a TCP-based socket
        sock = new Socket(AddressFamily.InterNetwork,
                        SocketType.Stream,
                        ProtocolType.Tcp);
        // Connect
        sock.Connect(_host, port);
        return true;
    }
    catch (SocketException se)
    {
        if (se.SocketErrorCode == SocketError.ConnectionRefused)
        {
            return false;
        }
        else
        {
            // An error occurred when attempting to access the socket
            Debug.WriteLine(se.ToString());
            Console.WriteLine(se.ToString());
        }
    }
    finally
    {
        if (sock != null)
        {
            if (sock.Connected)
                sock.Disconnect(false);
            sock.Close();
        }
    }
    return false;
}
#endregion

The trigger method for the CheapoPortScanner is Scan. Scan will check all of the ports in the range specified in the constructor. The ReportToConsole method will dump the pertinent information about the last scan to the console output stream:

	#region Public Methods
	public void Scan()
	{
	    for (int port = _minPort; port <= _maxPort; port++)
	    {
	        CheckPort(port);
	    }
	}


	public void ReportToConsole()
	{
	    Console.WriteLine("Port Scan for host at {0}:", _host.ToString());
	    Console.WriteLine("\tStarting Port: {0}; Ending Port: {1}",
	                      _minPort, _maxPort);
	    Console.WriteLine("\tOpen ports:");
	    foreach (int port in _openPorts)
	    {
	        Console.WriteLine("\t\tPort {0}", port);
	    }
	    Console.WriteLine("\tClosed ports:");
	    foreach (int port in _closedPorts)
	    {
	        Console.WriteLine("\t\tPort {0}", port);
	    }
	}

	#endregion // Public Methods
}

The PortScan method demonstrates how to use CheapoPortScanner by scanning ports 130 on the local machine. It first subscribes to the OpenPortFound event. The handler method for this event, cps_OpenPortFound, writes out the number of any port found open. Next, PortScan calls the Scan method. Finally, it calls ReportToConsole to show the full results of the scan, including the closed ports as well as the open ones.

	public static void PortScan ()
	{
	    // Do a specific range
	    Console.WriteLine("Checking ports 1-30 on localhost…");
	    CheapoPortScanner cps = new CheapoPortScanner("127.0.0.1",1,30);
	    cps.OpenPortFound +=
	        new CheapoPortScanner.OpenPortFoundEventHandler(cps_OpenPortFound);
	    cps.Scan();
	    Console.WriteLine("Found {0} ports open and {1} ports closed",
	            cps.OpenPorts.Count, cps.ClosedPorts.Count);

	    // Do the local machine, whole port range 1-65535
	    cps = new CheapoPortScanner();
	    cps.Scan();
	    cps.ReportToConsole();
	}

	static void cps_OpenPortFound(object sender, CheapoPortScanner.OpenPortEventArgs
	args)
	{
	    Console.WriteLine("OpenPortFound reported port {0} was open",args.PPortNumP);
	}

The output for the port scanner as shown appears here:

	Checking ports 1-30 on localhost…
	OpenPortFound reported port 22 was open
	OpenPortFound reported port 26 was open
	Found 2 ports open and 28 ports closed

Discussion

Open ports on a machine are significant because they indicate the presence of a program listening on those ports. Hackers look for "open" ports as ways to enter your systems without permission. CheapoPortScanner is an admittedly rudimentary mechanism for checking for open ports, but it demonstrates the principle well enough to provide a good starting point.

If you run this on a corporate network, you may quickly get a visit from your network administrator, as you may set off alarms in some intrusion-detection systems. Be judicious in your use of this code.


See Also

See the "Socket Class" and "Sockets" topics in the MSDN documentation.