For a school project, I have to create a bash script which communicates with a distant netcat server and sends commands to this server, and gets back responses.
In my client script, I have a sendMessage function that sends messages to the server, and then displays the response from the server. My issue is that after receiving a response, netcat is still waiting for data from server and won't close the connection.
I already tried the -q argument, and I don't get my responses with it, the -w argument is almost what I want, but the minimum value is 1 second, which is not possible to use in my case.
Here is my function:
function sendMessage {
netcat -w 1 "$ipAddress" "$port" <<< "$*"
}
I'm still searching on my side but I have to admit a little help would be very welcome.
Finally found a working way (maybe not the best but at least it's perfectly doing what I want :D)
after all functions I send a "ENDRESPONSE" message, and on my client, I test if I have this message or not :
function sendMessage {
while read line; do
if [[ $line == "ENDRESPONSE" ]]; then
break
else
echo $line
fi
done < <(netcat "$ipAddress" "$port" <<< "$*")
}
Anyway thanks for your help, I'll try to implement other solutions later !
You can exec nc as coproc, send the line to the server, then use read with a timeout to read the response, check whether the response is complete and then close the connection by sending a signal to nc.
Instead of using nc you can perhaps also use bash TCP capabilities, like
exec 5<>/dev/tcp/"$HOST"/"$PORT"
read -t "$TIMEOUT" -n "$MAXLEN" line
exec 5<&- 5>&-
Related
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"
for my work I need to connect to a lot of different servers every day: telnet ti the host, enter username, enter password - commence work.
Now I wanted to make life easier by automatically entering the username - I managed to do that, but telnet quits afterwards, that's obviously not what I wanted.
I work from a system with BASH and I can't install any programs there, so please don't give answers like "Use expect, that solves your problem easily..."
My tries led me to this:
function tn() { (echo "user"
sleep 1) | telnet $1 23
}
Calling the function with tn 123.45.67.89 connects to the server at 123.45.67.89, where the username is asked, which is entered automatically - great!
But then the password is asked, and instead of letting me enter it and begin my work, the connection is closed.
I really hope someone knows a solution for this!
Thanks in advance!
You might want to look at the expect command to script interactions with telnet:
#!/usr/bin/env bash
function tn() {
expect -f <<EOF
spawn telnet $1
expect "login"
send "${2}\r"
interact
EOF
}
telnetis designed for interactive usage. Use netcat, ncat, nc, socat or any other tool of this family.
Example:
( echo "user"; sleep 1) | ncat $1 23
But if you want to simulate interactive behavior, use socat and redirect stdin+stdout to a script:
Example:
socat TCP:$1:23 EXEC:my-shell.sh
In this case, a TCP connection for address $1 port 23 is established and stdin+stdout are redirected to stdout+stdin of the script. See man socat for details and more options.
my-shell.sh look for example like:
#!/bin/sh
read line
do_domething "$line"
printf "reply\n"
read line
do_domething "$line"
printf "reply\n"
btw, I have tested nothing (just written down)
I have a bash script which is doing very plain sftp to transfer data to production and uat servers. See my code below.
if [ `ls -1 ${inputPath}|wc -l` -gt 0 ]; then
sh -x wipprod.sh >> ${sftpProdLog}
sh -x wipdev.sh >> ${sftpDevLog}
sh -x wipdevone.sh >> ${sftpDevoneLog}
fi
sometimes the UAT server may go down. In those cases the number of scripts hanged are getting increased. If it reaches the user max. number of process the other scripts also getting affected. So I am thinking before executing each of the above script i have to test the port 22 availability on the destination server. Then I can execute the script.
Is this the right way? If yes what is the optimistic way to do that? If no what else is the best approach to avoid unnecessary sftp connection when destination not available? Thanks in advance.
Use sftp in batch mode together with ConnectTimeout option explicitely set. Sftp will take care about up/down problems by itself.
Note, that ConnectTimeout should be slightly higher if your network is slow.
Then put sftp commands into your wip*.sh backup scripts.
If UAT host is up:
[localuser#localhost tmp]$ sftp -b - -o ConnectTimeout=1 remoteuser#this_host_is_up <<<"put testfile.xml /tmp/"; echo $?
sftp> put testfile.xml /tmp/
Uploading testfile.xml to /tmp/testfile.xml
0
File is uploaded, sftp exits with exit code 0.
If UAT host is down, sftp exits wihin 1 second with exit code 255.
[localuser#localhost tmp]$ sftp -b - -o ConnectTimeout=1 remoteuser#this_host_is_down <<<"put testfile.xml /tmp/"; echo $?
ssh: connect to host this_host_is_down port 22: Connection timed out
Couldn't read packet: Connection reset by peer
255
It sounds reasonable - if the server is inaccessible you want to immediately report an error and not try and block.
The question is - why does the SFTP command block if the server is unavailable? If the server is down, then I'd expect the port open to fail almost immediately and you need only detect that the SFTP copy has failed and abort early.
If you want to detect a closed port in bash, you can simply as bash to connect to it directly - for example:
(echo "" > /dev/tcp/remote-host/22) 2>/dev/null || echo "failed"
This will open the port and immediately close it, and report a failure if the port is closed.
On the other hand, if the server is inaccessible because the port is blocked (in a firewall, or something, that drops all packets), then it makes sense for your process to hang and the base TCP test above will also hang.
Again this is something that should probably be handled by your SFTP remote copy using a timeout parameter, as suggested in the comments, but a bash script to detect blocked port is also doable and will probably look something like this:
(
(echo "" > /dev/tcp/remote-host/22) &
pid=$!
timeout=3
while kill -0 $pid 2>/dev/null; do
sleep 1
timeout=$(( $timeout - 1 ))
[ "$timeout" -le 0 ] && kill $pid && exit 1
done
) || echo "failed"
(I'm going to ignore the ls ...|wc business, other than to say something like find and xargs --no-run-if-empty are generally more robust if you have GNU find, or possibly AIX has an equivalent.)
You can perform a runtime connectivity check, OpenSSH comes with ssh-keyscan to quickly probe an SSH server port and dump the public key(s), but sadly it doesn't provide a usable exit code, leaving parsing the output as a messy solution.
Instead you can do a basic check with a bash one-liner:
read -t 2 banner < /dev/tcp/127.0.0.1/22
where /dev/tcp/127.0.0.1/22 (or /dev/tcp/hostname/ssh) indicates the host and port to connect to.
This relies on the fact that the SSH server will return an identifying banner terminated with CRLF. Feel free to inspect $banner. If it fails after the indicated timeout read will receive SIGALARM (exit code 142), and connection refused will result in exit code 1.
(Support for /dev/tcp and network redirection is enabled by default since before bash-2.05, though it can be disabled explicitly with --disable-net-redirections or with --enable-minimal-config at build time.)
To prevent such problems, an alternative is to set a timeout: with any of the ssh, scp or sftp commands you can set a connection timeout with the option -o ConnectTimeout=15, or, implicitly via ~/.ssh/config:
Host 1.2.3.4 myserver1
ConnectionTimeout 15
The commands will return non-zero on timeout (though the three commands may not all return the same exit code on timeout). See this related question: how to make SSH command execution to timeout
Finally, if you have GNU parallel you may use its sem command to limit concurrency to prevent this kind of problem, see https://unix.stackexchange.com/questions/168978/limit-maximum-number-of-concurrent-scp-processes-running-on-a-host .
I am trying to write a small script in bash. My target is that a Phone Call on my FritzBox will mute or pause my TV.
I get the information of a call via telnet (telnet fritz.box 1012) on my RaspberryPi and if there is coming a phone call in I get this output:
24.08.15 14:03:05;RING;0;017mobilephonenumber;49304myhomenumber;SIP0;
The only Part that is every time the same is the RING before the calling number. What I need is a script that checks the output of the telnet and if in the telnet output is a ring than execute an action in my case i just need to do a http request on an internal site or start another script.
This is what I have tried is this:
#!/bin/bash
#string='echo "My string"'
string=$(telnet –e p fritz.box 1012)
for reqsubstr in 'alt' 'RING';do
if [ -z "${string##*$reqsubstr*}" ] ;then
echo "String '$string' contain substring: '$reqsubstr'."
else
echo "String '$string' don't contain substring: '$reqsubstr'."
fi
done
But I don`t get the output of my telnet session into the string. Anyone who can help me?
After you latest comment, and reading your question again, I think we can go for something simpler based on nc (netcat).
Let assume we create a bash script called action.sh
#!/bin/bash
while true;
do
read logline
for substr in 'alt' '\;RING\;'
do
if [[ "$logline" = *${substr}* ]]; then
echo "Got a match"
fi
done
done
Make the script executable, chmod +x action.sh, then start it with nc as follow:
nc -l fritz.box 1012 | ./action.sh
It will listen infinitely for incoming traffic from the box and should act as you want. I don't have your box obviously and I tested by starting it as nc -l 127.0.0.1 12000 and then provided input via telnet 127.0.0.1 12000, providing your RING sample. It seems to work.
$ nc -l 127.0.0.1 12000 | ./action.sh
Got a match
017mobilephonenumber
Got a match
017mobilephonenumber
Would this be more acceptable to you ? without expect (I am not an expect wizard anyway).
I hope this answer will help you more.
do you have access to expect? It is meant exactly for such kind of scenario. man expect is your friend, and I found a fair tutorial via google. If you google for modem expect you can find sample script for such scenario (I can find some for FritzBox too but not sure it's the same box as yours). I haven't used it in ages, though.
Here my short conclusion:
To see incomming calls on your FritzBox you can simply execute:
telnet fritz.box 1012
in your terminal.
The output should look like that:
24.08.15 14:03:05;RING;0;017mobilephonenumber;49304myhomenumber;SIP0;
I my case I want the execute an command the mutes my TV.
Here are the instructions how I controll my TV via Web/RaspberryPi (Philips Harmony Hub) if some one is interested.
To execute a command on an incomming call create a script called action.sh
#!/bin/bash
while true;
do
read logline
for substr in 'alt' '\;RING\;'
do
if [[ "$logline" = *${substr}* ]]; then
echo "Got a match"
#here you can put the command you want to execute on an incoming call
fi
done
done
After you create it make it executable using chmod +x action.sh
After that you can start the script using nc 192.168.1.1 1012 | action.sh
And that's how you execute a script on an incomming call!
Special thanks to tgo!!
Hello
i am trying to make a shell script that checks for a ping response. if there is no response i want it to send me an email telling me that it is down.
What i am trying to do is to receive an email if one of my switches go down.
this is going to run as a cronjob btw
thank you in advance for any help.
I do not know if ping's return value is standardized, so check the documentation for your ping, but if your ping returns a reasonable value then you can simply do:
while ping -c 1 $IP; do
sleep $TIMEOUT
done
mail -s "$IP is down" email#address
("Reasonable value" meaning zero if at least one response is received. BSD ping satisfies that condition.)
This is not tested, but it should give you the idea how to solve this:
ping -c 1 <SOME IP> | grep "1 received" || echo "I lost connection" | mail -s "link is down!!" you#example.com