Bash script telnet to test multiple addresses and ports - bash

I am required to test at least 130 ip addresses and ports.
I am hoping to write a bash script such that it reads the ip address and ports from an input file.
I have the following
while read line
do
telnet $line >> $2
done < $1
This is a crappy code as it cannot determine whether its connected or failed, and I have to rely on its auto escape character to disconnect from a connection.
How can I improvise this such that it updates $2 with the status quickly?
I am working on Redhat and do not have netcat or expect installed..

As other stackoverflower's said, I would recommend using nmap or netcat if avilable.
However, if you cannot use those software, you can use bash's builtin /dev/tcp/<host>/<port> instead.
http://www.gnu.org/software/bash/manual/bashref.html#Redirections
I could'nt figure out which version of bash you are using, but /dev/tcp/... seems to implemented since some old bash.
#!/bin/bash
echo "scanme.nmap.org 21
scanme.nmap.org 22
scanme.nmap.org 23
scanme.nmap.org 79
scanme.nmap.org 80
scanme.nmap.org 81" | \
while read host port; do
r=$(bash -c 'exec 3<> /dev/tcp/'$host'/'$port';echo $?' 2>/dev/null)
if [ "$r" = "0" ]; then
echo $host $port is open
else
echo $host $port is closed
fi
done
This produces
scanme.nmap.org 21 is closed
scanme.nmap.org 22 is open
scanme.nmap.org 23 is closed
scanme.nmap.org 79 is closed
scanme.nmap.org 80 is open
scanme.nmap.org 81 is closed
UPDATED: The following can do timeout.
Although it may seem little tricky, idea is just to kill the child process after some timeout.
Bash script that kills a child process after a given timeout
#!/bin/bash
echo "scanme.nmap.org 80
scanme.nmap.org 81
192.168.0.100 1" | (
TCP_TIMEOUT=3
while read host port; do
(CURPID=$BASHPID;
(sleep $TCP_TIMEOUT;kill $CURPID) &
exec 3<> /dev/tcp/$host/$port
) 2>/dev/null
case $? in
0)
echo $host $port is open;;
1)
echo $host $port is closed;;
143) # killed by SIGTERM
echo $host $port timeouted;;
esac
done
) 2>/dev/null # avoid bash message "Terminated ..."
this produces
scanme.nmap.org 80 is open
scanme.nmap.org 81 is closed
192.168.0.100 1 timeouted
since 192.168.100 does not exist in my local network.

A slight update to the accepted answer:
#!/bin/bash
# supertelnet 127.0.0.1:3306 10.10.10.45:22
(
TCP_TIMEOUT=3
for hostport in ${#}; do
a=(${hostport//:/ })
host=${a[0]}
port=${a[1]}
(CURPID=$BASHPID;
(sleep $TCP_TIMEOUT;kill $CURPID) &
exec 3<> /dev/tcp/$host/$port
) 2>/dev/null
case $? in
0)
echo $host $port is open;;
1)
echo $host $port is closed;;
143) # killed by SIGTERM
echo $host $port timeouted;;
esac
done
) 2>/dev/null # avoid bash message "Terminated ..."
I find this to be a lot more friendly as a script.

Pure bash nmap replacment:
Sorry for comming so late on this question.
Speed parallelized process
This could be a lot quicker if all probe are done together:
TargetList=(
scanme.nmap.org:21 scanme.nmap.org:22 scanme.nmap.org:23
scanme.nmap.org:79 scanme.nmap.org:80 scanme.nmap.org:81
)
checkTcpConn() {
local line testfd bpid=$BASHPID
( sleep 3 && kill -INT $bpid && echo $1 timeout) &
if exec {testfd}<>/dev/tcp/${1/:/\/};then
echo >&$testfd $'\r\n\r\n'
read -ru $testfd -t 1 line
[[ $line ]] &&
echo $1 open $line ||
echo $1 open
exec {testfd}<&-
else
echo $1 closed
fi
}
for target in ${TargetList[#]};do
checkTcpConn $target &
done 2>/dev/null | sort
will output quickly:
scanme.nmap.org:21 closed
scanme.nmap.org:22 open SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13
scanme.nmap.org:23 closed
scanme.nmap.org:79 closed
scanme.nmap.org:80 open HTTP/1.1 400 Bad Request
scanme.nmap.org:81 closed
or worst:
for target in scanme.nmap.org:{{1..1024},3128,3306,5432,5900,8080};do
checkTcpConn $target &
sleep .002
done 2>/dev/null | grep open
scanme.nmap.org:22 open SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13
scanme.nmap.org:80 open HTTP/1.1 400 Bad Request
And with a timeout:
for target in scanme.nmap.org:2{2,1} 192.168.210.123:1 ;do
checkTcpConn $target &
done 2>/dev/null |
sort
192.168.210.123:1 timeout
scanme.nmap.org:21 closed
scanme.nmap.org:22 open SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13
Nota The last pipe done 2>/dev/null | sort is required in order to avoid job control messages. For showing raw output, use
...
done 2>/dev/null | cat

Related

Bash: how to make telnet or nc work in background until connection is closed

Below I am describing what I was struggling with and the decision that I came to currently. Please point me to the cleverer/smaller decisions, also would be glad to receive feedback.
So, there are a publisher and a client on a localhost, they communicate through port 8080. I can telnet or nc to this port and write output to a log normally, but cannot make the same commands work in background.
What I see is that when launched in background they stop immediately after getting the first input (is it really so?), but in foreground they work as they should and die only after publisher closes connection at this port.
This is what occurs normally:
> telnet localhost 8080 | tee output.log (or >>output.log, no matter)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Publisher starts sending information via port.
***some necessary output***
Publisher closes the port.
Connection closed by foreign host.
But when launched in background it stops immediately, without waiting for output:
> nohup telnet localhost 8080 | tee output.log (or <command> &, or nohup <command> &)
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.
You use a netcat command instead :
nohup nc -d localhost 8080 >>console-8080.txt &
If you want to indent the log file with the date :
nohup nc -d localhost 8080 | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-8080.txt; done &
To restart nc process in case it closes :
#!/bin/bash
ip=$1
port=$2
while [ 1 ]
do
nc -d $ip $port | while read line ; do echo `date +'%d%m%Y-%H%M%S'`: $line >>console-$ip-$port.txt; done
sleep .1
done
Here is an expect script that I`ve come to. It is being launched in background.
nohup ./telnet_expect.sh &
The script spawns new bash session and performs usual redirection to a file.
Usually I arrange communicating between bash and expect via env variables, here I have not implemented it because the whole use case is small enough.
#!/bin/bash
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n"
logFile="/export/home/<user>/<folder>/output.log"
logFileExpect="/export/home/<user>/<folder>/expect.log"
echo > "$logFile"
echo > "$logFileExpect"
until nc -w 1 localhost 8080
do
sleep 1
done
expect -c "
log_user 1
set timeout 250
proc err_exit {msg} {
puts \"---\"
puts stderr \"\$msg\"
send \"exit status: \$?\r\"
exit 1
}
exp_internal -f /export/home/<user>/<folder>/expect.log 1
spawn bash
send \"telnet localhost 8080 >> /export/home/<user>/<folder>/output.log\r\"
sleep 10
expect {
\"*onnection closed by foreign host*\" {
send \"echo Success\r\"
}
timeout {
err_exit \"\nError: timeout\n\"
}
}
send \"exit\r\"
expect eof
"
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n"
Upd: below is more configurable version which takes log file as an argument. This is how to launch:
nohup ./telnet_expect.sh <your_output_log_file> &
The values of IP and port here are defined only once, so it is easy to move them to arguments as well. Also there are debug log and console log to understand what exactly is going on. User can provide either absolute or relative path to a log file.
#!/bin/bash
scriptFolderPath=$(dirname $(readlink -f "$0"))
logDir="$scriptFolderPath"
logFileDefault="$logDir"/"output_default.log"
logFileExpect="$logDir"/"expect.log"
ip="localhost"
port="8080"
logConsole="$logDir"/"console_telnet.log"
echo > "$logConsole"
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Started telnet_expect.sh\n" >> "$logConsole"
### debug info
logDebug="$logDir"/"debug_telnet.log"
echo > "$logDebug"
exec 5> "$logDebug"
BASH_XTRACEFD="5"
PS4='$LINENO: '
set -x
if [ -z "$1" ]; then
logFile="$logFileDefault"
else
if [[ $logFile =~ "/" ]]; then
logFile="$1"
else
echo "Got the log file in the same folder as the script" >> "$logConsole"
logFile="$scriptFolderPath"/"$1"
fi
fi
echo > "$logFile"
echo > "$logFileExpect"
### setting envs
echo -e "\nsetting BCKGR_TELNET_LOG" >> "$logConsole"
export BCKGR_TELNET_LOG="$logFile"
echo "set BCKGR_TELNET_LOG: $BCKGR_TELNET_LOG" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_EXPECT" >> "$logConsole"
export BCKGR_TELNET_LOG_EXPECT="$logFileExpect"
echo -e "set BCKGR_TELNET_LOG_EXPECT: $BCKGR_TELNET_LOG_EXPECT\n" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_PORT" >> "$logConsole"
export BCKGR_TELNET_LOG_PORT="$port"
echo -e "set BCKGR_TELNET_LOG_PORT: $BCKGR_TELNET_LOG_PORT\n" >> "$logConsole"
echo -e "\nsetting BCKGR_TELNET_LOG_IP" >> "$logConsole"
export BCKGR_TELNET_LOG_IP="$ip"
echo -e "set BCKGR_TELNET_LOG_IP: $BCKGR_TELNET_LOG_IP\n" >> "$logConsole"
until nc -w 1 "$ip" "$port"
do
sleep 1
done
expect -c "
log_user 1
set timeout 250
proc err_exit {msg} {
puts \"---\"
puts stderr \"\$msg\"
send \"exit status: \$?\r\"
exit 1
}
puts \"Reading BCKGR_TELNET_LOG_EXPECT\"
if {[info exists env(BCKGR_TELNET_LOG_EXPECT)]} {
set bckgr_telnet_log_expect $::env(BCKGR_TELNET_LOG_EXPECT)
puts \"Found BCKGR_TELNET_LOG_EXPECT\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_EXPECT\"
}
puts \"Reading BCKGR_TELNET_LOG\"
if {[info exists env(BCKGR_TELNET_LOG)]} {
set bckgr_telnet_log $::env(BCKGR_TELNET_LOG)
puts \"Found BCKGR_TELNET_LOG\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG\"
}
puts \"Reading BCKGR_TELNET_LOG_PORT\"
if {[info exists env(BCKGR_TELNET_LOG_PORT)]} {
set bckgr_telnet_log_port $::env(BCKGR_TELNET_LOG_PORT)
puts \"Found BCKGR_TELNET_LOG_PORT\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_PORT\"
}
puts \"Reading BCKGR_TELNET_LOG_IP\"
if {[info exists env(BCKGR_TELNET_LOG_IP)]} {
set bckgr_telnet_log_ip $::env(BCKGR_TELNET_LOG_IP)
puts \"Found BCKGR_TELNET_LOG_IP\"
} else {
err_exit \"Error while reading env variable BCKGR_TELNET_LOG_IP\"
}
exp_internal -f \$bckgr_telnet_log_expect 1
spawn bash
send \"telnet \$bckgr_telnet_log_ip \$bckgr_telnet_log_port >> \$bckgr_telnet_log\r\"
sleep 10
expect {
\"*onnection closed by foreign host*\" {
send \"echo Success\r\"
}
timeout {
err_exit \"\nError: timeout\n\"
}
}
send \"exit\r\"
expect eof
"
echo -e "\nUnsetting env variables" >> "$logConsole"
unset BCKGR_TELNET_LOG
unset BCKGR_TELNET_LOG_EXPECT
unset BCKGR_TELNET_LOG_PORT
unset BCKGR_TELNET_LOG_IP
echo -e "\n$(date +%Y-%m-%d_%H:%M:%S,%3N) -- Finished telnet_expect.sh\n" >> "$logConsole"

netcat daemon for calling functions in sh script

I'm new to shell programming and I have created a script that opens a connection to a server of mine. I want to have this script listen for an input from a client node and use that to run a function.
This is my process.
Run script > opens listener > on second computer use netcat to connect > run a function in the script on the server called nodefunction
I have server_port coded to '4444'
File name: run_hangman
nc -l -k -v -p 4444 | bash hangman
File name: hangman
#!/bin/bash
msg_timeout=0
host_ows=1
server_port=4444
dubOws=xxx.xxx.xxx.xxx
initServer() {
hostIP=`ip -o addr show dev "eth0" | awk '$3 == "inet" {print $4}' | sed -r 's!/.*!!; s!.*\.!!'`
hostOws=`echo $hostIP | cut -d . -f 4`
}
servermsg(){ #message //if = --n, echos on same line
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[SERVER] "$1
else
echo -n `date +"%T"` "[SERVER] "
fi
}
owsmsg(){ #message //if = --n, echos on same line
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[OWS] "$1
else
echo -n `date +"%T"` "[OWS] "
fi
}
playermsg() {
if [ "$1" != "--n" ]
then
echo `date +"%T"` "[PLAYER] "$1
else
echo -n `date +"%T"` "[PLAYER] "
fi
}
question(){ #question, read, example
servermsg "$1"
if [ -n "$3" ]
then
servermsg "$3"
fi
read $2
echo ""
}
owsArray(){ #
for targetOws in $player_list
do
owsArray+=("OWS"$targetOws)
done
echo -n ${owsArray[*]}
echo
}
openSocket() {
servermsg "Starting the Game Listener"
servermsg "Opening Listener on port "$server_port
#nc -k -l $server_port |bash
#nc -kl -q 1 -p $server_port # This should create the listener & This is where everything stops.
servermsg "Now listening on port "$server_port
}
initServer
owsmsg "Starting server on OWS"$hostOws"..."
question "Enter all the OWSs that will play:" player_list "Example: 1 9 14 23"
echo $player_list
question "Type a category hint:" game_cat "Example: Type of Animal"
question "Type your word:" game_word "Example: zebra"
question "How many guesses:" game_guesses "Example: 7"
servermsg "OWS"$host_ows "has created a Hangman session"
servermsg "Players are:"; servermsg --n; owsArray
servermsg "Your word is "${#game_word}" letters long and players have "$game_guesses" guesses"
question "If this is all correct press enter, or CTRL+C to cancel"
openSocket
# I think I need a While script here to read the RAW input and run the playermsg function with the input?
I run the run_hangman file and then I connect to it via my node computer. I enter the following line and echo "1 2 3" because that is what I need. I also can't enter "1 2 3" directly into the window running "run_hangman" as if I press enter it goes to a new line.
echo "1 2 3" >/dev/tcp/xxx.xxx.xxx.xxx/4444
The server shows that it connected
Listening on [0.0.0.0] (family 0, port 4444)
14:52:24 [OWS] Starting server on OWS225...
14:52:24 [SERVER] Enter all the OWSs that will play:
14:52:24 [SERVER] Example: 1 9 14 23
Connection from [xxx.xxx.xxx.xxx] port 4444 [tcp/*] accepted (family 2, sport 41564)
Connection closed, listening again.
1 2 3
Now once it gets to openSocket it will allow me to send one more echo and then it closes on the server. I need to get what I presume is a while statement and have it listen for an input like "playermsg 'has started a game'" and have it actually run that function on the server.
Will I be able to get this to run, almost seems like it has to be in the background? I've been using nc(1) for reference and some websites said to try -d and that didn't work either.
I got it figured out. I did indeed need a while statement.
openSocket
while read -r value; do
val=${value:10}
if [[ "$value" == playermsg* ]]; then
val=${value:10}
playermsg $val
elif [[ "$value" == servermsg* ]]; then
val=${value:10}
servermsg $val
else
echo "Returned "$value
echo "Value was "$val
fi
done
So now on the second computer I simply run
echo "playermsg testing" >/dev/tcp/xxx.xxx.xxx.xxx/4444
Then the server displays the following:
15:29:36 [SERVER] Starting the Game Listener
15:29:36 [SERVER] Opening Listener on port 4440
15:29:36 [SERVER] Now listening on port 4440
15:29:37 [PLAYER] testing

Shell script port scanner

I would like to scan multiple ports in multiple hosts. I used this script but it takes long time to show the result.
#!/bin/bash
hosts=(
"server1"
"server2"
)
for host in "${hosts[#]}"
do
echo "=========================================="
echo "Scanning $host"
echo "=========================================="
for port in {21,22,80}
do
echo "" > /dev/tcp/$host/$port && echo "Port $port is open"
done 2>/dev/null
done
Some people suggested to use telnet or NetCat instead but i prefer to do it without installing any new packages. So, are there any ways to speed it up by multithreading or other way.
You could use GNU Parallel to run all the checks in parallel. I am not the best at using it, and #OleTange (the author) normally has to correct me but I keep trying. So, let's try your case, by building up to it slowly:
parallel echo {1} {2} ::: 192.168.0.1 192.168.0.8 ::: 21 22 80
192.168.0.8 22
192.168.0.8 80
192.168.0.8 21
192.168.0.1 80
192.168.0.1 22
192.168.0.1 21
looks kind of hopeful to me. Then I add in -k to keep the results in order, and I supply a function that takes those IP addresses and ports as arguments:
parallel -k 'echo "" > /dev/tcp/{1}/{2} && echo {1}:{2} is open' ::: 192.168.0.1 192.168.0.8 ::: 21 22 80 2>/dev/null
192.168.0.1:80 is open
192.168.0.8:21 is open
192.168.0.8:22 is open
192.168.0.8:80 is open
This will run 8 jobs in parallel if your CPU has 8 cores, however echo is not very resource intensive so you can probably run 32 in parallel, so add -j 32 after the -k.
If you wanted to stick closer to your own script, you can do it like this:
#!/bin/bash
hosts=(
"192.168.0.1"
"192.168.0.8"
)
for host in "${hosts[#]}"
do
for port in {21,22,80}
do
echo "(echo > /dev/tcp/$host/$port) 2>/dev/null && echo Host:$host Port:$port is open"
done
done | parallel -k -j 32
Basically, instead of running your commands, I am just sending them to the stdin of parallel so it can do its magic with them.
You could run all three pokes in the background, then wait for them all to finish, and probably slash the running time to 1/3.
for port in 21 22 80; do
echo "" > /dev/tcp/$host/$port 2>/dev/null &
pid[$port]=$!
done
for port in 21 22 80; do
wait $pid[$port] && echo "Port $port" is open"
done
You could add parallelism by running multiple hosts in the background, too, but that should be an obvious extension.
#!/bin/bash
function alarm {
local timeout=$1; shift;
# execute command, store PID
bash -c "$#" &
local pid=$!
# sleep for $timeout seconds, then attempt to kill PID
{
sleep "$timeout"
kill $pid 2> /dev/null
} &
wait $pid 2> /dev/null
return $?
}
function scan {
if [[ -z $1 || -z $2 ]]; then
echo "Usage: ./scanner <host> <port, ports, or port-range>"
echo "Example: ./scanner google.com 79-81"
return
fi
local host=$1
local ports=()
# store user-provided ports in array
case $2 in
*-*)
IFS=- read start end <<< "$2"
for ((port=start; port <= end; port++)); do
ports+=($port)
done
;;
*,*)
IFS=, read -ra ports <<< "$2"
;;
*)
ports+=($2)
;;
esac
# attempt to write to each port, print open if successful, closed if not
for port in "${ports[#]}"; do
alarm 1 "echo >/dev/tcp/$host/$port" &&
echo "$port/tcp open" ||
echo "$port/tcp closed"
done
}
scan $1 $2

UNIX script abruptly ending due to ssh command [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
ssh invocation in script function
Below UNIX script abruptly ends while reading second line from file. When I comment 'ssh' command the script works as expected. I think I will have to run ssh command in a different process, but haven't got a handle yet as regards to how to do that. Any help in resolving this problem is highly appreciated.
*#!/usr/bin/ksh
exec 3<&0
exec 0<./bulkInput.dat
#cat ./bulkInput.dat | while read searchToken || (echo "reading failedi $?" && false)
index=0
while true
do
index=`expr $index + 1`
if [ $index -gt 450 ]
then
echo "Index limit reached. Now exiting"
exit 0
fi
read searchToken
if [ $? -ne "0" ]
then
echo "Read failed"
fi
echo "Search token is "${searchToken}
echo "************************************ **********************************"
echo "plsa0666 WSIP server " > WSIP.log
ssh zq742888#plsa0666 'grep -r '$searchToken' /logs/jas/was60/wsip/wsip*/wsip*' >> WSIP.log
echo "plsa0667 WSIP server " >> WSIP.log
#ssh zq742888#plsa0667 'grep -r '$searchToken' /logs/jas/was60/wsip/wsip*/wsip*' >> WSIP.log
echo "plsa0668 WSIP server " >> WSIP.log
#ssh zq742888#plsa0668 'grep -r '$searchToken' /logs/jas/was60/wsip/wsip*/wsip*' >> WSIP.log
echo "plsa4407 WSIP server " >> WSIP.log
#ssh zq742888#plsa4407 'grep -r '$searchToken' /logs/jas/was60/wsip/wsip*/wsip*' >> WSIP.log
echo "plsa0412 server " >> WSIP.log
cp WSIP.log bulk/WSIP.log_${searchToken}
echo $?
done
exec 0<&3
echo "Exiting script"*
ssh(1) is reading all of stdin and exhausting it, causing the next shell read to return false and break the loop. Try one of these:
ssh -n zq742888#plsa0666 ...
or
ssh < /dev/null zq742888#plsa0666 ...
to prevent this behavior.
Run the ssh command from the shell prompt and see what it does. If it is asking for input (e.g. password) then that may be problem.
There is also a flag to run in script mode(from memory -b but you should check) and that may also help you.
The -i flag allows you to specify the key to use if that is the problem.

Finding next open port

Is there any way, using basic Unix commands, to find the next unused port number, starting at port 4444 and going upwards? I'm ssh'ed (via openssh) into a Windows XP machine, running Cygwin tools and using a bash shell.
Thanks, - Dave
Try this:
for port in $(seq 4444 65000); do echo -ne "\035" | telnet 127.0.0.1 $port > /dev/null 2>&1; [ $? -eq 1 ] && echo "unused $port" && break; done
where
seq 4444 65000 - port range for check
echo -ne "\035" - escape character to force close telnet session (^])
if telnet finishes with exit code 1 that mean connection refused:
$ telnet 127.0.0.1 4444
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused
telnet: Unable to connect to remote host
$ echo $?
1
else we decided that connection was success with exit code 0.
EDIT:
Special for cygwin: You need to install additional package inetutils that is contain telnet port and use the script as follows:
for port in $(seq 4444 65000); do echo -ne "\035" | /usr/bin/telnet 127.0.0.1 $port > /dev/null 2>&1; [ $? -eq 1 ] && echo "unused $port" && break; done
Same as above, but written as a function
function get_unused_port() {
for port in $(seq 4444 65000);
do
echo -ne "\035" | telnet 127.0.0.1 $port > /dev/null 2>&1;
[ $? -eq 1 ] && echo "$port" && break;
done
}
FREE_PORT="$(get_unused_port)"
echo $FREE_PORT
The following function doesn't depend on telnet/netcat as it generates a random port in the local port range and compares it with a list of ports currently used by running applications.
Should work on any *nix that supports proc filesystem. Generates a free ephemeral port to be used by your application.
function EPHEMERAL_PORT(){
while true; do
LISTENING_PORTS=$(cat /proc/net/tcp | awk 'NR >1 {print $2}' | awk -F':' '{print $2}');
LISTENING_PORTS=$(for PORT in ${LISTENING_PORTS}; do echo $((16#${PORT})); done|sort -g);
# echo "32768 60999" | read LPORT UPORT
read LPORT UPORT < /proc/sys/net/ipv4/ip_local_port_range
MPORT=$[$LPORT + ($RANDOM % $UPORT)];
if (echo "${LISTENING_PORTS[#]}" | grep -xqv $MPORT); then
echo $MPORT;
break;
fi
done
}
Apparently TCP connections can be used as file descriptors on linux. The following function uses that technique and should be faster than the previous one.
function EPHYMERAL_PORT(){
LPORT=32768;
UPORT=60999;
while true; do
MPORT=$[$LPORT + ($RANDOM % $UPORT)];
(echo "" >/dev/tcp/127.0.0.1/${MPORT}) >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo $MPORT;
return 0;
fi
done
}
This is a cross platform function that uses osquery to get a list of listening ports. Should also work on Windows.
function EPHYMERAL_PORT(){
while true; do
echo "32768 60999" | read LPORT UPORT
MPORT=$[$LPORT + ($RANDOM % $UPORT)];
LISTENING_PORTS=$(osqueryi --header=false --list "select port from listening_ports order by port");
if (echo "${LISTENING_PORTS[#]}" | grep -xqv $MPORT); then
echo $MPORT;
break;
fi
done
}
Usage instructions. Bind the output to a variable and use in scripts. Tested on Ubuntu 16.04
root#ubuntu:~> EPHYMERAL_PORT
59453
root#ubuntu:~> PORT=$(EPHYMERAL_PORT)

Resources