#!/usr/bin/ksh93 # # list_active_local_tcp_ports1.ksh - list port numbers of local TCP connections # # Written by Roland Mainz # # # simple netstat -n parser # function netstat_list_connections { set -o nounset nameref data=$1 compound out=( typeset stdout stderr ; integer res ) out.stderr="${ { out.stdout="${ LC_ALL='POSIX' PATH='/usr/bin:/bin' netstat -a -n ; (( out.res=$? )) ; }" ; } 2>&1 ; }" if (( out.res != 0 )) ; then print -u2 -f $"%s: netstat returned %d exit code.\n" \ "$0" out.res return 1 fi if [[ "${out.stderr}" != '' ]] ; then # # Handle known Linux netstat warnings # if [[ "${out.stderr}" != $'warning, got bogus unix line.' ]] ; then print -u2 -f $"%s: netstat returned unknown error message %q.\n" \ "$0" "${out.stderr}" return 1 fi fi typeset -a data.connections typeset l typeset leftover integer dci=0 # data.connections array index while read l ; do leftover="${l/~(Elrx) (?: # non-capturing group # # regex group for tcp,udp # (tcp|tcp6|udp|udp6|raw|raw6|sctp|sctp6) # Proto [[:space:]]+ ([[:digit:]]+) # Recv-Q [[:space:]]+ ([[:digit:]]+) # Send-Q [[:space:]]+ ([^[:space:]]+) # Local Address [[:space:]]+ ([^[:space:]]+) # Foreign Address (?: | [[:space:]]+ ([^[:space:]]*?) # State (Optional) ) | # # regex for unix # (unix) # Proto [[:space:]]+ ([[:digit:]]+) # RefCnt [[:space:]]+ (\[.+?\]) # Flags [[:space:]]+ ([^[:space:]]+) # Type [[:space:]]+ ([^[:space:]]*?) # State (optional) [[:space:]]+ ([[:digit:]]+) # I-Node (?: | [[:space:]]+ ([^[:space:]]+) # Path (optional) ) ) /X}" # If the regex above did not match then .sh.match # remains untouched, so we might see data from the # previous round. # So we check the "leftover" var whether it just # contains the dummy value of "X" to indicate a # successful regex match if [[ "$leftover" == 'X' ]] ; then #print -v .sh.match if [[ "${.sh.match[1]-}" != '' ]] ; then nameref dcn=data.connections[$dci] typeset dcn.proto="${.sh.match[1]}" typeset dcn.recv_q="${.sh.match[2]}" typeset dcn.send_q="${.sh.match[3]}" typeset dcn.local_address="${.sh.match[4]}" typeset dcn.foreign_address="${.sh.match[5]}" typeset dcn.state="${.sh.match[6]}" ((dci++)) elif [[ "${.sh.match[7]-}" != '' ]] ; then nameref dcn=data.connections[$dci] typeset dcn.proto="${.sh.match[7]}" typeset dcn.refcnt="${.sh.match[8]}" typeset dcn.flags="${.sh.match[9]}" typeset dcn.type="${.sh.match[10]}" [[ "${.sh.match[11]}" != '' ]] && typeset dcn.state="${.sh.match[11]}" typeset dcn.inode="${.sh.match[12]}" [[ "${.sh.match[13]}" != '' ]] && typeset dcn.path="${.sh.match[13]}" ((dci++)) fi else true #printf $"leftover=%q\n" "${leftover}" fi done <<<"${out.stdout}" return 0 } function netstat_list_active_local_tcp_connections { set -o nounset nameref ar=$1 compound c integer port integer i netstat_list_connections c || return 1 #print -v c [[ -v ar ]] || integer -a ar for i in "${!c.connections[@]}" ; do nameref n=c.connections[$i] # look for only for TCP connections which match # 127.0.*.* or IPv6 ::1 for localhost # 0.0.0.0 or IPv6 :: for all addresses (e.g. servers) if [[ "${n.proto}" == ~(El)tcp && \ "${n.local_address}" == ~(Elr)((127\.0\..+|::1)|(::|0\.0\.0\.0|)):[[:digit:]]+ ]] ; then port="${n.local_address##*:}" #printf $"port = %d\n" port (( ar[port]=1 )) fi done return 0 } function netstat_find_next_free_local_tcp_port { set -o nounset compound c=( integer -a ar ) nameref ret_free_port=$1 integer start_port integer end_port integer i netstat_list_active_local_tcp_connections c.ar || return 1 #print -v c (( start_port=$2 )) if (( $# > 2 )) ; then (( end_port=$3 )) else (( end_port=65535 )) fi for ((i=start_port ; i < end_port ; i++ )) ; do if [[ ! -v c.ar[i] ]] ; then (( ret_free_port=i )) return 0 fi done return 1 } function main { if (( $# > 0 )) ; then integer port=-1 print -u1 -f $"# next free TCP port:\n" netstat_find_next_free_local_tcp_port port "$@" || return 1 print -f $"%d\n" port else set -o nounset compound c=( integer -a ar ) print -u1 -f $"# list of open local TCP ports:\n" netstat_list_active_local_tcp_connections c.ar || return 1 #print -v c (( ${#c.ar[@]} > 0 )) && printf $"%d\n" "${!c.ar[@]}" fi return 0 } # main entry point main "$@" exit $? # EOF.