Using expect to run "nc -w 3 -z $IP 22" command on macOS but get long waiting timeout - linux

I using linux expect command to execute "nc -w 3 -z $IP 22" command on macOS.
I expected timeout after 3 seconds but I got 1m 15s (Due to "nc" command can setting timeout via "-w" argument).
I run the same code at other linux(Debian 7.8, ubuntu 14.04) machines are normally.
Who can tell me why I got the different result between linux and macOS ?
Following content is my source code:
#!/usr/bin/expect -f
set DUT_IP "192.168.1.2"
if {0!=[catch {exec sh -c "nc -w 3 -z $DUT_IP 22 && echo \$?"}]} {
send_user "Cannot connect to DUT $DUT_IP\n"
exit -1
} else {
puts "IP exist"
}

Related

Bash script that check connection with host:port using telnet and netcar

I have task to create quite complicated bash script, which at first part is checking the connection to host:port with telnet and- if telnet would fail or would not be installed, try the connection using netcat.
I have problem with loop, where it will skip netcat if telnet would connect to the host and also, if both- telnet and netcat would fail- script would finish with error message.
The script:
#!/bin/bash
echo Type host IP address
read REMOTEHOST
echo Type port number
read REMOTEPORT
TIMEOUT=5
echo quit | timeout --signal=9 5 telnet $REMOTEHOST $REMOTEPORT
if nc -w $TIMEOUT -z $REMOTEHOST $REMOTEPORT; then
echo "I was able to connect to ${REMOTEHOST}:${REMOTEPORT}"
else
echo "Connection to ${REMOTEHOST}:${REMOTEPORT} failed. Exit code from Netcat was ($?)."
fi
You can use the $? variable to get the exit code from the last command.
I found that your original telnet command exits with error code 1 on my system because the escape character is ^]. When I telnet manually I need to hit ctrl-] to enter the telnet prompt, then I can enter 'quit'.
The trick here is you cannot just type ^], you have to type ctrl-v ctrl-]
ctrl-v tells the system to capture the next ctrl character.
The following gives me an exit code of 0, and you can verify by running it manually with echo $? at the command line
-- remember to use ctrl-v ctr-]
$ (echo ^]; echo quit) | timeout --signal=9 5 telnet <REMOTEHOST> <REMOTEPORT>
$ echo $?
Then you can use this in your script:
#!/bin/bash
echo Type host IP address
read REMOTEHOST
echo Type port number
read REMOTEPORT
TIMEOUT=5
(echo ^]; echo quit) | timeout --signal=9 5 telnet $REMOTEHOST $REMOTEPORT > /dev/null 2>&1
TELNET_EXIT_CODE=$?
if [[ $TELNET_EXIT_CODE -ne 0 ]]; then
nc -w $TIMEOUT -z $REMOTEHOST $REMOTEPORT > /dev/null 2>&1
NC_EXIT_CODE=$?
fi
if [[ $TELNET_EXIT_CODE -eq 0 ]] || [[ $NC_EXIT_CODE -eq 0 ]]; then
echo "success"
else
echo "fail"
fi
Tested on Ubuntu 20.04.04, GNU bash version 5.0.17, Telnet version 0.17-41

Linux shell (sh) CLI test if ping successful

How to wire linux shell (sh) script to test with ping if host is reachable?
I guess there could be solution that uses grep but maybe ping provides that option by itself?
I am more into getting a whitelisting a successful ping operation that reached the host then checking if there was any error. I don't care about the reason of ping not succeeding in reaching a host.
I would like to limit ping attempts count and maximum amount of time to reach the host so the script does not waits too long for ping trying to reach a host.
dt=$(date +%d)
cksize=50
echo "Start $(date)"
while IFS= read -r sn
do
echo "*************************************************"
echo "Begin checking NODES client: $sn"
if ping -c 1 "$sn" -i 5 > /dev/null
then
echo "$sn node up"
else
echo "$sn node down"
fi
done < server_list
parallel -j0 --timeout 15 'ping -c 5 -i 0.2 {} >/dev/null 2>&1 && echo {} up || echo {} down' ::: freenetproject.org debian.org no-such.domain {1..254}.2.3.4
You can do it like this. It will do it in parallel for all hosts.
#!/bin/bash
for server in 'google.com' 'github.com' 'fakeserver.com'
do
{ ping -o "$server" &>/dev/null && echo "$server is UP" || echo "$server is DOWN" ; } &
done
wait
Regards!

How can I execute more than one command in exec command of linux expect

I'm trying to detect a host is available by using expect of Linux.
Now, I can using the following commands via bash to get the return value and check host status.
#!/usr/bin/bash
nc -z $DUT_IP 22 && echo $?
I want to do the same things via expect. But seems I failed.
#!/usr/bin/expect --
set DUT_IP "192.168.1.9"
set result [exec nc -z $DUT_IP 22]
send_user "$result\n\n"
I got the following error messages:
invalid port &&
while executing
"exec nc -z $DUT_IP 22 && echo $?"
invoked from within
"set result [exec nc -z $DUT_IP 22 && echo $?] "
(file "reboot.exp" line 44)
Use of && to separate commands is a shell construct, not an expect construct. You can explicitly launch a shell for that command list:
set result [exec sh -c "nc -z $DUT_IP 22 && echo $?"]
Note that will only print the exit status if the command succeeded, thus result will either be "0" or empty. You want to use ; instead of &&

bash: checking host availability over tcp [duplicate]

I'm looking for a quick and simple method for properly testing if a given TCP port is open on a remote server, from inside a Shell script.
I've managed to do it with the telnet command, and it works fine when the port is opened, but it doesn't seem to timeout when it's not and just hangs there...
Here's a sample:
l_TELNET=`echo "quit" | telnet $SERVER $PORT | grep "Escape character is"`
if [ "$?" -ne 0 ]; then
echo "Connection to $SERVER on port $PORT failed"
exit 1
else
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
fi
I either need a better way, or a way to force telnet to timeout if it doesn't connect in under 8 seconds for example, and return something I can catch in Shell (return code, or string in stdout).
I know of the Perl method, which uses the IO::Socket::INET module and wrote a successful script that tests a port, but would rather like to avoid using Perl if possible.
Note: This is what my server is running (where I need to run this from)
SunOS 5.10 Generic_139556-08 i86pc i386 i86pc
As pointed by B. Rhodes, nc (netcat) will do the job. A more compact way to use it:
nc -z <host> <port>
That way nc will only check if the port is open, exiting with 0 on success, 1 on failure.
For a quick interactive check (with a 5 seconds timeout):
nc -z -v -w5 <host> <port>
It's easy enough to do with the -z and -w TIMEOUT options to nc, but not all systems have nc installed. If you have a recent enough version of bash, this will work:
# Connection successful:
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/80'
$ echo $?
0
# Connection failure prior to the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/sfsfdfdff.com/80'
bash: sfsfdfdff.com: Name or service not known
bash: /dev/tcp/sfsfdfdff.com/80: Invalid argument
$ echo $?
1
# Connection not established by the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/81'
$ echo $?
124
What's happening here is that timeout will run the subcommand and kill it if it doesn't exit within the specified timeout (1 second in the above example). In this case bash is the subcommand and uses its special /dev/tcp handling to try and open a connection to the server and port specified. If bash can open the connection within the timeout, cat will just close it immediately (since it's reading from /dev/null) and exit with a status code of 0 which will propagate through bash and then timeout. If bash gets a connection failure prior to the specified timeout, then bash will exit with an exit code of 1 which timeout will also return. And if bash isn't able to establish a connection and the specified timeout expires, then timeout will kill bash and exit with a status of 124.
TOC:
Using bash and timeout
Command
Examples
Using nc
Command
RHEL 6 (nc-1.84)
Installation
Examples
RHEL 7 (nmap-ncat-6.40)
Installation
Examples
Remarks
Using bash and timeout:
Note that timeout should be present with RHEL 6+, or is alternatively found in GNU coreutils 8.22. On MacOS, install it using brew install coreutils and use it as gtimeout.
Command:
$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${HOST}/${PORT}"; echo $?
If parametrizing the host and port, be sure to specify them as ${HOST} and ${PORT} as is above. Do not specify them merely as $HOST and $PORT, i.e. without the braces; it won't work in this case.
Example:
Success:
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0
Failure:
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124
If you must preserve the exit status of bash,
$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143
Using nc:
Note that a backward incompatible version of nc gets installed on RHEL 7.
Command:
Note that the command below is unique in that it is identical for both RHEL 6 and 7. It's just the installation and output that are different.
$ nc -w $TIMEOUT_SECONDS -v $HOST $PORT </dev/null; echo $?
RHEL 6 (nc-1.84):
Installation:
$ sudo yum install nc
Examples:
Success:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Failure:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
nc: connect to canyouseeme.org port 81 (tcp) timed out: Operation now in progress
1
If the hostname maps to multiple IPs, the above failing command will cycle through many or all of them. For example:
$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
1
RHEL 7 (nmap-ncat-6.40):
Installation:
$ sudo yum install nmap-ncat
Examples:
Success:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connected to 52.202.215.126:80.
Ncat: 0 bytes sent, 0 bytes received in 0.22 seconds.
0
Failure:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1
If the hostname maps to multiple IPs, the above failing command will cycle through many or all of them. For example:
$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection to 104.43.195.251 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.100.122.175 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.96.52.53 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 191.239.213.197 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection timed out.
1
Remarks:
The -v (--verbose) argument and the echo $? command are of course for illustration only.
With netcat you can check whether a port is open like this:
nc my.example.com 80 < /dev/null
The return value of nc will be success if the TCP port was opened, and failure (typically the return code 1) if it could not make the TCP connection.
Some versions of nc will hang when you try this, because they do not close the sending half of their socket even after receiving the end-of-file from /dev/null. On my own Ubuntu laptop (18.04), the netcat-openbsd version of netcat that I have installed offers a workaround: the -N option is necessary to get an immediate result:
nc -N my.example.com 80 < /dev/null
In Bash using pseudo-device files for TCP/UDP connections is straight forward. Here is the script:
#!/usr/bin/env bash
SERVER=example.com
PORT=80
</dev/tcp/$SERVER/$PORT
if [ "$?" -ne 0 ]; then
echo "Connection to $SERVER on port $PORT failed"
exit 1
else
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
fi
Testing:
$ ./test.sh
Connection to example.com on port 80 succeeded
Here is one-liner (Bash syntax):
</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.
Note that some servers can be firewall protected from SYN flood attacks, so you may experience a TCP connection timeout (~75secs). To workaround the timeout issue, try:
timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.
See: How to decrease TCP connect() system call timeout?
I needed a more flexible solution for working on multiple git repositories so I wrote the following sh code based on 1 and 2. You can use your server address instead of gitlab.com and your port in replace of 22.
SERVER=gitlab.com
PORT=22
nc -z -v -w5 $SERVER $PORT
result1=$?
#Do whatever you want
if [ "$result1" != 0 ]; then
echo 'port 22 is closed'
else
echo 'port 22 is open'
fi
check ports using bash
Example
$ ./test_port_bash.sh 192.168.7.7 22
the port 22 is open
Code
HOST=$1
PORT=$2
exec 3> /dev/tcp/${HOST}/${PORT}
if [ $? -eq 0 ];then echo "the port $2 is open";else echo "the port $2 is closed";fi
If you're using ksh or bash they both support IO redirection to/from a socket using the /dev/tcp/IP/PORT construct. In this Korn shell example I am redirecting no-op's (:) std-in from a socket:
W$ python -m SimpleHTTPServer &
[1] 16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000
The shell prints an error if the socket is not open:
W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]
You can therefore use this as the test in an if condition:
SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
print succeeded
else
print failed
fi
The no-op is in a subshell so I can throw std-err away if the std-in redirection fails.
I often use /dev/tcp for checking the availability of a resource over HTTP:
W$ print arghhh > grr.html
W$ python -m SimpleHTTPServer &
[1] 16863
Serving HTTP on 0.0.0.0 port 8000 ...
W$ (print -u9 'GET /grr.html HTTP/1.0\n';cat <&9) 9<>/dev/tcp/127.0.0.1/8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.6.1
Date: Thu, 14 Feb 2013 12:56:29 GMT
Content-type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Feb 2013 12:55:44 GMT
arghhh
W$
This one-liner opens file descriptor 9 for reading from and writing to the socket, prints the HTTP GET to the socket and uses cat to read from the socket.
While an old question, I've just dealt with a variant of it, but none of the solutions here were applicable, so I found another, and am adding it for posterity. Yes, I know the OP said they were aware of this option and it didn't suit them, but for anyone following afterwards it might prove useful.
In my case, I want to test for the availability of a local apt-cacher-ng service from a docker build. That means absolutely nothing can be installed prior to the test. No nc, nmap, expect, telnet or python. perl however is present, along with the core libraries, so I used this:
perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
In some cases where tools like curl, telnet, nc o nmap are unavailable you still have a chance with wget
if [[ $(wget -q -t 1 --spider --dns-timeout 3 --connect-timeout 10 host:port; echo $?) -eq 0 ]]; then echo "OK"; else echo "FAIL"; fi
If you want to use nc but don't have a version that support -z, try using --send-only:
nc --send-only <IP> <PORT> </dev/null
and with timeout:
nc -w 1 --send-only <IP> <PORT> </dev/null
and without DNS lookup if it's an IP:
nc -n -w 1 --send-only <IP> <PORT> </dev/null
It returns the codes as the -z based on if it can connect or not.
Building on the most highly voted answer, here is a function to wait for two ports to be open, with a timeout as well. Note the two ports that mus be open, 8890 and 1111, as well as the max_attempts (1 per second).
function wait_for_server_to_boot()
{
echo "Waiting for server to boot up..."
attempts=0
max_attempts=30
while ( nc 127.0.0.1 8890 < /dev/null || nc 127.0.0.1 1111 < /dev/null ) && [[ $attempts < $max_attempts ]] ; do
attempts=$((attempts+1))
sleep 1;
echo "waiting... (${attempts}/${max_attempts})"
done
}
I needed short script which was run in cron and hasn't output. I solve my trouble using nmap
open=`nmap -p $PORT $SERVER | grep "$PORT" | grep open`
if [ -z "$open" ]; then
echo "Connection to $SERVER on port $PORT failed"
exit 1
else
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
fi
To run it You should install nmap because it is not default installed package.
I'm guessing that it's too late for an answer, and this might not be a good one, but here you go...
What about putting it inside of a while loop with a timer on it of some sort. I'm more of a Perl guy than Solaris, but depending on the shell you're using, you should be able to do something like:
TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever
And then just add a flag in the while loop, so that if it times out before completing, you can cite the timeout as reason for failure.
I suspect that the telnet has a timeout switch as well, but just off the top of my head, I think the above will work.
This uses telnet behind the scenes, and seems to work fine on mac/linux. It doesn't use netcat because of the differences between the versions on linux/mac, and this works with a default mac install.
Example:
$ is_port_open.sh 80 google.com
OPEN
$ is_port_open.sh 8080 google.com
CLOSED
is_port_open.sh
PORT=$1
HOST=$2
TIMEOUT_IN_SEC=${3:-1}
VALUE_IF_OPEN=${4:-"OPEN"}
VALUE_IF_CLOSED=${5:-"CLOSED"}
function eztern()
{
if [ "$1" == "$2" ]
then
echo $3
else
echo $4
fi
}
# cross platform timeout util to support mac mostly
# https://gist.github.com/jaytaylor/6527607
function eztimeout() { perl -e 'alarm shift; exec #ARGV' "$#"; }
function testPort()
{
OPTS=""
# find out if port is open using telnet
# by saving telnet output to temporary file
# and looking for "Escape character" response
# from telnet
FILENAME="/tmp/__port_check_$(uuidgen)"
RESULT=$(eztimeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1)
rm -f $FILENAME;
SUCCESS=$(eztern "$RESULT" "Escape character is '^]'." "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")
echo "$SUCCESS"
}
testPort
My machine does not support nc or /dev/tcp/$hostname/$port but timeout, so I came back to telnet as follows:
if echo "quit" | timeout 2 telnet $SERVER $PORT 2>&1 | grep -q 'Connected to'; then
echo "Connection to $SERVER on port $PORT succeeded"
exit 0
else
echo "Connection to $SERVER on port $PORT failed"
exit 1
fi
nmap-ncat to test for local port that is not already in use
availabletobindon() {
port="$1"
nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
return "$?"
}

Continue to grep for traceroute result with bash

Every night I go through the same process of checking failover systems for our T1's. I essentially go through the following process:
Start the failover process.
traceroute $server;
Once I see it's failed over, I verify that connections work by SSHing into a server.
ssh $server;
Then once I see it works, I take it off of failover.
So what I want to do is to continually run a traceroute until I get a certain result, then run a SSH command.
Put your list of successful messages in a file (omit the variable lines and fractions of the line, and use a ^ to identify the start of the line, as such:)
patterns.list:
^ 7 4.68.63.165
^ 8 4.68.17.133
^ 9 4.79.168.210
^10 216.239.48.108
^11 66.249.94.46
^12 72.14.204.99
Then a simple while loop:
while ! traceroute -n ${TARGET} | grep -f patterns.list
do
sleep 5 # 5 second delay between traceroutes, for niceness.
done
ssh ${DESTINATION}
Use traceroute -n to generate the output so you don't get an IP address that resolves one time, but and a name the next, resulting in a false positive.
I think you could be better off using ping command to verify server's accessability than traceroute.
It is easy to check for return status of ping command without using any grep at all:
if [ ping -c 4 -n -q 10.10.10.10 >/dev/null 2>& ]; then
echo "Server is ok"
else
echo "Server is down"
fi
If you want to do it continually in a loop, try this:
function check_ssh {
# do your ssh stuff here
echo "performing ssh test"
}
while : ; do
if [ ping -c 4 -n -q 10.10.10.10 >/dev/null 2>& ]; then
echo "Server is ok"
check_ssh
else
echo "Server is down"
fi
sleep 60
done

Resources