PowerShell Cookbook

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: 216
This Year: 16
This Month: 0
This Week: 0
Comments: 523

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] | | #