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 ([char] 4)) { 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, 0, 1024)
$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.]