PowerShell Cookbook

Twitter Updates

    follow me on Twitter

    Search

    Categories

     

    On this page

    Scripting Network / TCP Connections in PowerShell

    Archive

    Blogroll

    Disclaimer
    I work for Microsoft.

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    RSS 2.0 | Atom 1.0 | CDF

    Send mail to the author(s) E-mail

    Total Posts: 235
    This Year: 12
    This Month: 0
    This Week: 0
    Comments: 634

    Sign In

     Wednesday, November 29, 2006
    Thursday, November 30, 2006 2:04:06 AM (Pacific Standard Time, UTC-08:00) ( )

    Awhile back, I introduced a script that allows you interact with remote TCP ports (such as Telnet.) While useful, it worked only interactively. It would be even more useful if you were able to script a network or TCP connection.

    Let me introduce Connect-Computer.ps1 v2, which allows exactly that:

    First, a simple scripted HTTP session:

    $http = @"
    GET / HTTP/1.1
    Host:search.msn.com
    `n`n
    "@

    $http | Connect-Computer search.msn.com 80

     

    Second, a scripted POP3 session (Parse-TextObject comes from here: http://www.leeholmes.com/blog/parsetextObjectAWKWithAVengeance.aspx):

    if(-not (test-path Variable:\mailCredential))
    {
       $mailCredential = Get-Credential
    }
    $address = $mailCredential.UserName
    $password = $mailCredential.GetNetworkCredential().Password
    $pop3Commands = "USER $address","PASS $password","STAT","QUIT"
    $output = $pop3Commands | Connect-Computer mail.leeholmes.com 110
    $inbox = $output.Split("`n")[3]
    $status = $inbox | Parse-TextObject -PropertyName "Response","Waiting","BytesTotal","Extra"
    "{0} messages waiting, totaling {1} bytes." -f $status.Waiting,$status.BytesTotal

     

    Now, here is Connect-Computer.ps1

    ## Connect-Computer.ps1
    ## Interact with a service on a remote TCP port
    param(
            [string] $remoteHost = "localhost",
            [int] $port = 80,
            [string] $inputObject,
            [int] $commandDelay = 100
         )

    [string] $output = ""

    $currentInput = $inputObject
    if(-not $currentInput)
    {
        $SCRIPT:currentInput = @($input)
    }
    $scriptedMode = [bool] $currentInput

    function Main
    {
        ## Open the socket, and connect to the computer on the specified port
        if(-not $scriptedMode)
        {
            write-host "Connecting to $remoteHost on port $port"
        }

        trap { throw "Could not connect to remote computer: $_"; exit }
        $socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)

        if(-not $scriptedMode)
        {
            write-host "Connected.  Press ^D followed by [ENTER] to exit.`n"
        }

        $stream = $socket.GetStream()
        $writer = new-object System.IO.StreamWriter($stream)

        ## Create a buffer to receive the response
        $buffer = new-object System.Byte[] 1024
        $encoding = new-object System.Text.AsciiEncoding


        while($true)
        {
            ## Receive the output that has buffered so far
            $SCRIPT:output += GetOutput

            ## If we're in scripted mode, send the commands,
            ## receive the output, and exit.
            if($scriptedMode)
            {
                foreach($line in $currentInput)
                {
                    $writer.WriteLine($line)
                    $writer.Flush()
                    Start-Sleep -m $commandDelay
                }

                $SCRIPT:output += GetOutput

                break
            }
            ## If we're in interactive mode, write the buffered
            ## output, and respond to input.
            else
            {
                if($output) 
                {
                    foreach($line in $output.Split("`n"))
                    {
                        write-host $line
                    }
                    $SCRIPT:output = ""
                }

                ## Read the user's command, quitting if they hit ^D
                $command = read-host
                if($command -eq ([char4)) { break; }

                ## Otherwise, Write their command to the remote host     
                $writer.WriteLine($command)
                $writer.Flush()
            }
        }

        ## Close the streams
        $writer.Close()
        $stream.Close()

        ## If we're in scripted mode, return the output
        if($scriptedMode)
        {
            $output
        }
    }

    ## Read output from a remote host
    function GetOutput
    {
        $outputBuffer = ""
        $foundMore = $false 

        ## Read all the data available from the stream, writing it to the
        ## output buffer when done.
        do
        {
            ## Allow data to buffer for a bit
            start-sleep -m 1000

            ## Read what data is available
            $foundmore = $false
            while($stream.DataAvailable) 
            {
                $read = $stream.Read($buffer, 01024)   
                $outputBuffer += ($encoding.GetString($buffer, 0, $read)) 
                $foundmore = $true
            }
        } while($foundmore)

        $outputBuffer
    }

    . main
    [Edit: Thanks to Marco for pointing out a few issues that come up when you try to retrieve massive amounts of data (such as a newsgroup listing). I've updated the script to fix those.]
    Comments [2] | | #