BASH: keep connection alive - bash

I have the following scenario:
I use netcat to connect to a host running telnet server on port 23, I log in using provided username and password, issue some commands, after which I need to do fairly complex analysis of the provided output. Naturally, expect comes to mind, with a script like this:
spawn nc host 23
send "user\r"
send "pass\r"
send "command\r"
expect EOF
then, it is executed with expect example.scr >output.log, so the output file can be parsed. The parser is 150+ lines of bash code that executes under 2 seconds, and makes a decision what command should be executed next. Thus, it replaces "command" with "command2", and executes the expect script again, like this:
sed -i '/send "command\r"/send "command2\r"/' example.scr
expect example.scr >output.log
Obviously, it is not needed to re-establish telnet connection and perform log in process all over again, just to issue a single telnet command after 2 seconds of processing. A conclusion can be made, that telnet session should be kept alive as a background process, so one could freely talk to it at any given time. Naturally, using named pipes comes to mind:
mkfifo in
mkfifo output.log
cat in | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" >in
cat output.log
After the file is written to, EOF causes the named pipe to close, thus terminating the telnet session. I was thinking what kind of eternal process could be piped to netcat so it can be used as telnet relay to host. I came up with a very silly idea, but it works:
nc -k -l 666 | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" | nc localhost 666
cat output.log
The netcat server is started with k(eep alive), listening on port 666, and any data stream is redirected to the netcat telnet client connected to the host, while the entire conversation is dumped to output.log. One can now echo telnet commands to nc localhost 666, and read the result from output.log.
One should keep in mind that the expect script can be easily modified to accommodate SSH and even serial console connection, just by spawning ssh or socat instead of netcat. I never liked expect because it forces a use of another scripting language within bash, requires tcl libraries, and needs to be compiled for the embedded platforms, while netcat is a part of busybox and readily available everywhere.
So, the question is - could this be done in a simpler way? I'd put my bet on having some sort of link between console and TCP socket. Any suggestions are appreciated.

How about using like a file descriptor?
exec 3<>/dev/tcp/host/port
while true; do
echo -e "user\npass\ncommand" >&3
read_response_generate_next_command <&3 >&3
# if no more commands, break;
done
exec 3>&-

Related

SSH Remote Forwarding - Send to Background & Save Output as Variable

I'm working on a bash script to connect to a server via SSH that is running sish (https://github.com/antoniomika/sish). This will essentially create a port forward on the internet like ngrok using only SSH. Here is what happens during normal usage.
The command:
ssh -i ./tun -o StrictHostKeyChecking=no -R 5900:localhost:5900 tun.domain.tld sleep 10
The response:
Starting SSH Forwarding service for tcp:5900. Forwarded connections can be accessed via the following methods:
TCP: tun.domain.tld:43345
Now I need to send the ssh command to the background and figure out some way of capturing the response from the server as a variable so that I can grab the port that sish has assigned and send that somewhere (probably a webhook). I've tried a few things like using -f and piping to a file or named pipe and trying to cat it, but the issue is that the piping to the file never works and although the file is created, it's always empty. Any assistance would be greatly appreciated.
If you're running a single instance of sish (and the tunnel you're attempting to define) you can actually have sish bind the specific part you want (in this case 5900).
You just set the --bind-random-ports=false flag on your server command in order to tell sish that it's okay to not use random ports.
If you don't want to do this (or you have multiple clients that will expose this same port), you can use a simple script like the following:
#!/bin/bash
ADDR=""
# Start the tunnel. Use a phony command to tell ssh to clean the output.
exec 3< <(ssh -R 5900:localhost:5900 tun.domain.tld foobar 2>&1 | grep --line-buffered TCP | awk '{print $2; system("")}')
# Get our buffered output that is now just the address sish has given to us.
for i in 1; do
read <&3 line;
ADDR="$line"
done
# Here is where you'd call the webhook
echo "Do something with $ADDR"
# If you want the ssh command to continue to run in the background
# you can omit the following. This is to wait for the ssh command to
# exit or until this script dies in order to kill the ssh command.
PIDS=($(pgrep -P $(pgrep -P $$)))
function killssh() {
kill ${PIDS[0]}
}
trap killssh EXIT
while kill -0 ${PIDS[0]} 2> /dev/null; do sleep 1; done;
sish also has an admin api which you can scrape. The information on that is available here.
References: I build and maintain sish and use it myself (as well as a similar type of script).

Bash (simulate a telnet client with tcp socket and obtain the first line)

I'm trying to simulating a telnet client in bash using tcp socket and I'm starting from zero.
Doing this I am reading and writing on a file descriptor (stdin stdout).
(I know that it's too much simple to do $telnet hostname, but I'm doing this to understand bash).
To start I want to get the first line (welcome message).
There are some troubles with particular ip.
With some it works, but with some others it fails. It has to go good with all!
Example with some random Ip I found to test it
It works good with:
exec 3<> /dev/tcp/77.239.64.182/23; cat <&3
output:
????????
* TELRAD CMTS UBR-10K *
User Access Verification
Username:
Here the trouble
It works bad with:
exec 3<> /dev/tcp/77.239.64.158/23; cat <&3
output:
0???? ??#??'
but, if I use telnet the output it's different:
$ telnet 77.239.64.158
Trying 77.239.64.158...
Connected to 77.239.64.158.
Escape character is '^]'.
MikroTik v6.18
Login:
I thought that the trouble was that the client had to send some string before having the welcome message.
So I changed it to:
exec 3<> /dev/tcp/77.239.64.158/23; echo -e "aaaaaaaa\r\n\rAAAA...lonstring...AAAA\r\n\r\n\rAAAAlonstringAAAA\n\r\n" >&3; cat <&3
Same thing, same output.
Someone able to adjust this?!

Telnet smtp command bash 2.05 script problem

Line by line works but in script it does not.
I tried everything i could think of.
This does not work in bash 2.05
{ sleep 1; echo "helo localhost"; sleep 1; echo "mail from: test#maillinux.net"; sleep 1; echo "rcpt to: user#actualdomain.org"; sleep 1; echo "data"; sleep 1; echo "subject: test"; echo; echo "some text" ;echo "."; echo "quit"; } | telnet mailserver.actualdomain.org 25
but if you do line by line
telnet mailserver.actualdomain.org 25
220...
helo localhost
250...ok
mail from: test#maillinux.net
250..ok
rcpt to: user#actualdomain.org
250...ok
data
354 Start mail input; end with <CRLF>.<CRLF>
subject: test
some text
.
quit
then it works and i get mail.
the script works in newer bash 4.4 but i need it to work in 2.05.
seems to me that it does not do echo "." et the end and does not finish mail...and therefore does not send it.
but why???
I expect it to send mail but it does not
This is probably a difference in the version of telnet, not the version of bash.
One common problem when using network tools in a pipeline like this is how they handle EOFs on stdin. What you need is behavior like this:
Your input is written to telnet's stdin
...while this is happening, telnet starts sending that input to the remote server...
Telnet's stdin is given an EOF (end-of-file). Some broken versions of telnet exit immediately at this point.
...telnet keeps sending everything it read from its stdin over to the remote server.
...telnet finishes sending everything it read to the remote server, and then closes the sending side of the socket (via a FIN packet)
...the server keeps sending content to the client until it's sent any responses that need to be done, and then the server closes its own side by sending another FIN
telnet prints everything it got prior to getting a FIN from the server. Completion of this step is when a correctly-written telnet actually exits.
As noted above, many versions of telnet that actually exist were written assuming they would be used by humans, not scripts. Instead of sending buffered content over the wire on receiving an EOF, and waiting to receive any responses from the server before shutting down the receive side, they just quit immediately the moment an EOF is seen.
You can work around it by adding a delay at the end.
It's an ugly hack, and better replaced with using tools that know how to shut down a socket correctly, but the cheap-and-dirty approach looks something like this:
{ ...; echo "."; echo "quit"; sleep 2; } | telnet mailserver.actualdomain.org 25
For new development, consider using a built-to-purpose tool like socat, or one of the many implementations of netcat.
socat's documented behavior explicitly makes it wait to finish sending content it received on its stdin, and printing responses it received over the socket, before exiting; insofar as it has a timeout on how long it waits for responses from the server, that timeout can be explicitly configured.
Agree with Charles, this must be a different version of telnet. And a few thoughts,
You can simply use expect and send commands to automate the telnet command,
#!/user/bin/expect
spawn telnet localhost 25 <<EOF
hello localhost
mail from: local#localhost
rcpet to: user#localhost
data
Some data
.
EOF
Or,
create a text file with the mail commands and use netcat (if the telnet doesn't support for stream input)
cat mailCmd.txt | nc localhost 25
The best and reliable way is to use a utility like socat or sendmail
sendmail user#localhost < "Emailcontent.txt"

How to get telnet execution result code from shell script?

Using the following shell code for remote telnet request:
{
sleep 5
echo admin
sleep 3
echo pass
sleep 3
echo ls
sleep 5
echo exit
} | telnet 172.16.1.1
I want to check if telnet connection was successful or not. Trying to use $?:
echo $?
But it always returns "1", even if telnet connection was OK.
Telnet is exceptionally difficult to script in this way, there is a high degree of asynchronicity with the time it takes to establish a connection and for your intended actions to complete. expect was created for exactly this kind of purpose. You launch a program, like telnet, then declare a series of expectations - eg, when 'username: ' is emitted from the program, and an action to trigger (eg: typing in the username).
There are also libraries or wrappers for expect in many languages:
python expect
ruby expect
perl expect
Here is an example that drives telnet to make an HTTP HEAD request:
set timeout 20
spawn telnet localhost 80
expect "Connected to "
send "HEAD / HTTP/1.0\r\n\r\n"
expect "HTTP 200 OK"
Given all of this, I feel I must point out that telnet is considered insecure. Ssh is a much better choice and supports better choices for authentication (eg: public/private key auth), restrictions for commands that can be run (via .ssh/authorized_keys). With ssh, and ssh-keys set up, your script reduces to a single shell command:
ssh user#hostname ls
ssh has great support for safe, secure remote command execution.
If I'm remembering correctly, this expect script does what you're doing above.
#!/usr/bin/expect
spawn telnet 172.16.1.1
expect username:
send admin
expect password:
send pass
expect "\$ "
send ls
expect "\$ "
send exit
Here's a useful link for getting started: http://oreilly.com/catalog/expect/chapter/ch03.html

Telnet inside a shell script

How can I run telnet inside a shell script and execute commands on the remote server?
I do not have expect installed on my solaris machine because of security reasons.
I also do not have the perl net::telnet module installed.
So with out using expect and perl how can I do it?
I tried the below thing but its not working.
#!/usr/bin/sh
telnet 172.16.69.116 <<!
user
password
ls
exit
!
When I execute it, this is what I am getting:
> cat tel.sh
telnet 172.16.69.116 <<EOF
xxxxxx
xxxxxxxxx
ls
exit
EOF
> tel.sh
Trying 172.16.69.116...
Connected to 172.16.69.116.
Escape character is '^]'.
Connection to 172.16.69.116 closed by foreign host.
>
Some of your commands might be discarded. You can achieve finer control with ordinary script constructs and then send required commands through a pipe with echo. Group the list of commands to make one "session":-
{
sleep 5
echo user
sleep 3
echo password
sleep 3
echo ls
sleep 5
echo exit
} | telnet 172.16.65.209
I had the same issue...however, at least in my environment it turned out being the SSL Certificate on the destination server was corrupted in some way and the server team took care of the issue.
Now, what I'm trying to do is to figure out how to get a script to run the exact same thing you're doing above except I want it to dump out the exact same scenario above into a file and then when it encounters a server in which it actually connects, I want it to provide the escape character (^]) and go on to the next server.

Resources