Test-CWJTCPPort.psm1

function Test-CWJTCPPort
{
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]
        $ComputerName, #TODO: allow for array of hostnames/ports
        
        [Parameter(Mandatory=$true, Position=1)]
        [uint16]
        $Port,

        [Parameter(Mandatory=$false, Position=2)]
        [int]
        $Timeout = 5000,

        [Parameter(Position=3)]
        [switch]
        $ReturnObject = $false,

        [Parameter(Mandatory=$false)]
        [switch]
        $DontFragment,

        [Parameter(Mandatory=$false)]
        [ValidateSet('Socket', 'TcpClient')]
        [string]
        $ConnectionType = 'Socket'
    )
        
    try
    {
        $DestinationIP = [System.Net.Dns]::GetHostAddresses($ComputerName)[0].IPAddressToString
    }
    catch
    {
        throw "Unable to resolve address: $ComputerName"
    }

    $DestinationEndPoint = New-Object System.Net.IPEndPoint ([ipaddress]$DestinationIP), $Port
    
    $AddressFamily = $DestinationEndPoint.AddressFamily

    try
    {
        if($ConnectionType -eq 'Socket')
        {
            $SocketType    = [System.Net.Sockets.SocketType]::Stream #TODO: come back to explore other possible values
            $ProtocolType  = [System.Net.Sockets.ProtocolType]::Tcp

            $tcpclient = New-Object System.Net.Sockets.Socket $AddressFamily, $SocketType, $ProtocolType
            #TODO: change name to $Socket

            $tcpclient.LingerState = New-Object System.Net.Sockets.LingerOption -ArgumentList $false, 0
            
            # Don't set DontFragment on IPv6 addresses, exception returned...why?
            #TODO: can I do this always if I'm using socket()?
            if($AddressFamily -eq 'InterNetwork')
            {
                $tcpclient.DontFragment = $DontFragment
            }
        }
        else
        {
            $tcpclient = New-Object System.Net.Sockets.TcpClient -ArgumentList $AddressFamily

            $tcpclient.Client.LingerState = New-Object System.Net.Sockets.LingerOption -ArgumentList $false, 0

            # Don't set DontFragment on IPv6 addresses, exception returned...why?
            #TODO: can I do this always if I'm using socket()?
            if($AddressFamily -eq 'InterNetwork')
            {
                $tcpclient.Client.DontFragment = $DontFragment
            }
        }

        $connection = $tcpclient.BeginConnect($DestinationIP, $Port, $null, $null)
        
        $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        
        $success = $connection.AsyncWaitHandle.WaitOne($Timeout, $true)
        
        $ResponseTime = $Stopwatch.ElapsedMilliseconds

        if($tcpclient.Connected)
        {
            $status = 'Open'
            $tcpclient.EndConnect($connection)
        }
        else
        {
            if($success)
            {
                $status = 'Closed'
            }
            else
            {
                $status = 'Filtered'
            }
        }

        if($ReturnObject)
        {
            [pscustomobject]@{
                ComputerName = $ComputerName
                Port         = $Port
                Status       = $status
                ResponseTime = $ResponseTime
            }
        }
        else
        {
            if($status -eq 'Open')
            {
                return $true
            }
            else
            {
                return $false
            }
        }
    }
    catch
    {
        Write-Error $_.tostring()
    }
    finally
    {
        $tcpclient.Dispose()
    }
}




<#
 
#Repro of retransmit and ignored RSTs
 
$IP = '192.168.1.1'
$Port = 0
$Timeout = 1000
 
$Socket = New-Object System.Net.Sockets.TcpClient -ArgumentList 'InterNetwork'
 
$connection = $Socket.BeginConnect($IP, $Port, $null, $null)
 
$success = $connection.AsyncWaitHandle.WaitOne($Timeout, $true)
 
if($Socket.Connected)
{
    $status = 'Open'
}
elseif($success)
{
    $status = 'Closed'
}
else
{
    $status = 'Filtered'
}
 
$Socket.Dispose()
 
#>


#TODO: use $socket.localenpoint to show local ip/port