Powershell TCP client with error handling - powershell

I need help with a Powershell TCP client that can monitor when the server closes the connection. The starting point for this code is from here and looks like this:
## Connect-Computer.ps1
## Interact with a service on a remote TCP port
param(
[string] $remoteHost = "localhost",
[int] $port = 23
)
try
{
## Open the socket, and connect to the computer on the specified port
write-host "Connecting to $remoteHost on port $port"
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
if($socket -eq $null) { return; }
$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter($stream)
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
while($true)
{
## Allow data to buffer for a bit
start-sleep -m 500
## Read all the data available from the stream, writing it to the
## screen when done.
while($stream.DataAvailable)
{
$read = $stream.Read($buffer, 0, 1024)
write-host -n ($encoding.GetString($buffer, 0, $read))
}
## Read the user's command, quitting if they hit ^D
$command = read-host
## Write their command to the remote host
$writer.WriteLine($command)
$writer.Flush()
}
}
finally
{
## Close the streams
$writer.Close()
$stream.Close()
}
The problem is when the server closes the port the client continues to wait. How can I use a buffered read but still check if the port is still open? I dont need to know immediately if the port closes, just after a few minutes.
Thanks in advance for any help.

Related

Powershell P2P with UPnP hole punching

I'm trying to get my head around how P2P works and how one can implement working P2P without having to forward ports on the router. As far as I know to do this we need to "punch holes" and one of the means of doing this is with NATUPnP.
Below I've got some powershell code which sets up a TCP listener on local port 42008:
$port = 42008
# Punch hole
$upnp = New-Object -ComObject "HNetCfg.NATUPnP"
$upnp.StaticPortMappingCollection.Add(42002, "TCP", $port, "localhost", $true, "Socket test")
# Create a TCP listener object
$listener = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Any, $port)
# Start the listener
$listener.Start()
# Wait for incoming connections
while ($true) {
# Accept incoming connections
$client = $listener.AcceptTcpClient()
# Get the stream for the client
$stream = $client.GetStream()
# Read data from the stream
$buffer = New-Object byte[] 1024
$read = $stream.Read($buffer, 0, 1024)
# Convert the data to a string and print it
$data = [System.Text.Encoding]::ASCII.GetString($buffer, 0, $read)
Write-Host "Received: $data"
# Close the stream and the client
$stream.Close()
$client.Close()
}
As far as I understand it the hole punching above should redirect messages sent to <publicIP>:42002 to localhost:<$port> where the TCP Listener is listening. I then run the following client:
$upnp = New-Object -ComObject "HNetCfg.NATUPnP"
$externalIP = $upnp.StaticPortMappingCollection.item(42002, "TCP").ExternalIPAddress
$externalPort = 42002
# Connect to the remote machine
$client = New-Object System.Net.Sockets.TcpClient($externalIP, $externalPort)
# Get the stream for the client
$stream = $client.GetStream()
# Write data to the stream
$data = "Hello, World 2!"
$buffer = [System.Text.Encoding]::ASCII.GetBytes($data)
$stream.Write($buffer, 0, $buffer.Length)
# Close the stream and the client
$stream.Close()
$client.Close()
In this example I open up upnp just to grab the external IP and the external port, then I ping a message via TcpClient.
As far as I know this should work? But I keep getting the following exception:
New-Object : Exception calling ".ctor" with "2" argument(s): "No connection could be made because the target machine actively refused it <XX.XX.XX.XX>:42002"
At C:\Users\sancarn\Desktop\tbd\Powershell\SocketClient.ps1:7 char:11
+ $client = New-Object System.Net.Sockets.TcpClient($externalIP, $exter ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
Why is my machine actively refusing connection? I've actively disabled my firewall just to check whether that was the issue, but to no avail. I can't think what else could be wrong unless I've just got the wrong end of the stick as to how to use it? Any ideas?

PowerShell and Telnet Integration Outputs Junk Chars

I have tried to get PowerShell automation for Telnet connecting to Windows Server 2012 R2, Ubuntu 16.04 and Windows XP SP3 and all servers are throwing junk characters as output.
I was going through rfc854 but I could not get this done.
Could you please help me on how to resolve this issue.
Below code would try to connect Telnet server with credentials and output dir list.
Code
param (
[string] $terminalServer = "192.168.19.131",
[int] $port = 23,
[string] $username = "username",
[string] $password = "password",
[int] $commandDelay = 1000,
[string] $output = ""
)
function GetOutput {
## Create a buffer to receive the response
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
$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
$stream.ReadTimeout = 2000
do {
try {
$read = $stream.Read($buffer, 0, 1024)
if($read -gt 0) {
$foundmore = $true
$outputBuffer += ($encoding.GetString($buffer, 0, $read))
}
} catch { $foundMore = $false; $read = 0 }
} while($read -gt 0)
} while($foundmore)
$outputBuffer
}
$output = ""
write-host "Connecting to $terminalServer on port $port..."
trap { Write-Error "Could not connect to the server: $_"; exit }
$socket = new-object System.Net.Sockets.TcpClient($terminalServer, $port)
write-host "Connected. `n"
$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter $stream
## Receive the output that has buffered so far
$SCRIPT:output += GetOutput
$writer.WriteLine($username)
$writer.Flush()
Start-Sleep -m $commandDelay
$SCRIPT:output += GetOutput
$writer.WriteLine($password)
$writer.Flush()
Start-Sleep -m $commandDelay
$SCRIPT:output += GetOutput
$writer.WriteLine("dir")
$writer.Flush()
Start-Sleep -m $commandDelay
$SCRIPT:output += GetOutput
$writer.WriteLine("exit")
$writer.Flush()
Start-Sleep -m $commandDelay
## Close the streams
$writer.Close()
$stream.Close()
write-host "All streams are closed. The final output is"
$output
Output
D:\Tools\T24\PowerShell>powershell .\telnet_test.ps1
Connecting to 192.168.19.131 on port 23...
Connected.
All streams are closed. The final output is
??%??????'???? ??
I have tried with jBASE Telnetd Server Version 4.1.1 on Windows Server 2012 R2 and perfectly listing directory.
So, with Windows version of Telnet requires some more manipulation as per rfc854 implementation.

Can only either receive or send via TCP

I'm having trouble writing a PowerShell script that can both send and receive data via a TCP connection. It only seems to let me do one or the other.
Below is what I have so far. I want to listen and wait for a connection, then once established, receive a string containing an IP address, do some fancy lookup to see what user is logged into that machine, then send back the username. If I only send data, it works. If I only receive data, it works. If I try to do both, only the receive works. What am I doing wrong?
$port = 1234
do {
$user = ""
$endpoint = new-object System.Net.IPEndPoint ([system.net.ipaddress]::any, $port)
$listener = new-object System.Net.Sockets.TcpListener $endpoint
$listener.start()
$client = $listener.AcceptTcpClient() # will block here until connection
$stream = $client.GetStream();
$reader = New-Object System.IO.StreamReader $stream
$writer = New-Object System.IO.StreamWriter $stream
$add = $reader.ReadLine()
#$reader.close()
write-host "Request from " $add
if($add) {
$user = & wmic /Node:$add ComputerSystem Get UserName
write-host "User returned is " $user[2]
}
if($user[2] -eq "ERROR:") {
$user[2] = "ErrNoUserW"
} elseif(!$user[2]) {
$user[2] = "ErrServerW"
}
$writer.Write($user[2])
#$writer.close()
$stream.close()
$client.close()
$listener.stop()
} while(1)
I thought that the stream could only be read or write at any given time. Have you tried closing the stream then re-opening it?

PowerShell Named Pipe: no connection?

I need a named pipe to read and write.
In a program I create the pipe server using from kernel32.dll:
string PipeName = "\\\\.\\pipe\\myMT4";
int PipeMode = PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT; # tried too: PIPE_NOWAIT
int hPipe = CreateNamedPipeW(
PipeName,
PIPE_ACCESS_DUPLEX,
PipeMode,
PIPE_UNLIMITED_INSTANCES,1024,1024,
NMPWAIT_USE_DEFAULT_WAIT,NULL);
The handle hPipe is valid - every things seems to be ok here!
But in a PowerShell-script I want to open a client, connect and open the writer -
and cannot connect => timed out
function connect{
Param ([PSObject] $h)
...
$h.Pipe = New-Object -TypeName System.IO.Pipes.NamedPipeClientStream "\\.\pipe\PipeTest"
$h.Pipe.Connect( 5000 )
$h.Writer = New-Object -TypeName System.IO.StreamWriter $h.Pipe, $h.Encode
I really would prefer this way to have a similar access when reading and writen
either from/to the pipe and sockets e.g.:
function write{
Param ([PSObject] $h, [string] $line )
try {
$h.Writer.Write($line)
}
What is wrong?
Thanks in advance,
Gooly.
PS:
It seems that the program cannot deal with pipe-servers - I have to open a pipe-client, and that works but that causes other problems:
I define for the PowerShell-pipe-server :
$pipeName = "testpipe"
$pipeDir = [System.IO.Pipes.PipeDirection]::InOut
$pipeMsg = [System.IO.Pipes.PipeTransmissionMode]::Message
$pipeOpti = [System.IO.Pipes.PipeOptions]::Asynchronous
$pipe = New-Object system.IO.Pipes.NamedPipeServerStream(
$pipeName, $pipeDir, 1, $pipeMsg, $pipeOpti )
$pipe.WaitForConnection() #
$sw = new-object System.IO.StreamWriter $pipe
$sw.AutoFlush = $true
$sw.WriteLine("Server pid is $pid")
$sw.Dispose()
$pipe.Dispose()
1) My first problem is now that the powerShell-pipe-server is blocked by
$pipe.WaitForConnection()
until a client connects but it must handle 2 different sockets independently meanwhile and
2) if the client closes the connection I was unable to tell the client to open the same pipe again and the client gets the Windows-error: ERROR_PIPE_BUSY 231
Form the program I connect to the server with the kernel32.dll-function:
int CallNamedPipeW(string PipeName,
string outBuffer, int outBufferSz,
uint& inBuffer[], int inBufferSz,
int& bytesRead[], int timeOut
);
Any ideas?
Hmm, I can get named pipes to work between two different PowerShell sessions so I don't think it is an inherent PowerShell limitation:
Here is the server script:
$pipe = new-object System.IO.Pipes.NamedPipeServerStream 'testpipe','Out'
$pipe.WaitForConnection()
$sw = new-object System.IO.StreamWriter $pipe
$sw.AutoFlush = $true
$sw.WriteLine("Server pid is $pid")
$sw.Dispose()
$pipe.Dispose()
Here is the client script:
$pipe = new-object System.IO.Pipes.NamedPipeClientStream '.','testpipe','In'
$pipe.Connect()
$sr = new-object System.IO.StreamReader $pipe
while (($data = $sr.ReadLine()) -ne $null) { "Received: $data" }
$sr.Dispose()
$pipe.Dispose()
Client outputs:
Received: Server pid is 22836

Powershell v2.0 , unable to read the correct data from a tcpclient

I am very new to powershell programming,I have the following script, which reads from a socket stream as long as data is available, and then sends a "q" to exit the connection.
I am running this on win 2003 server std,Power shell V2.0.
When I execute the pgm, the output is the text from the pgm, which is reading and buffering.It almost looks like The following is the output:
Connecting to aardwolf.org on port 23
while($stream.DataAvailable)
{
$read = $stream.Read($buffer, 0, 1024)
write-Host $read
if($read -gt 0)
{
$outputBuffer += ($encoding.GetString($buffer, 0, $read))
}
}## Read the user's command, quitting if they hit
Following is the code:
# Interact with a service on a remote TCP port
param( [string] $remoteHost = "aardwolf.org",
[int] $port = 23 )
## Open the socket, and connect to the computer on the specified port
write-host "Connecting to $remoteHost on port $port"
$socket = new-object System.Net.Sockets.TcpClient($remoteHost, $port)
if($socket -eq $null) { return; }
$stream = $socket.GetStream()
$writer = new-object System.IO.StreamWriter($stream)
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
while($true)
{
## Allow data to buffer for a bit
start-sleep -m 500
$outputBuffer = ""
## Read all the data available from the stream, writing it to the ## screen when done.
if($stream.CanRead)
{
while($stream.DataAvailable)
{
$read = $stream.Read($buffer, 0, 1024)
write-Host $read
if($read -gt 0)
{
$outputBuffer += ($encoding.GetString($buffer, 0, $read))
}
}## Read the user's command, quitting if they hit
}
write-Host $outputBUffer
$command = "q"
$writer.WriteLine($command)
$writer.Flush()
break
}
## Close the streams
if($writer -ne $null)
{
$writer.Close()
$stream.Close()
}
Need some help resolving the issue.