#!/bin/bash
#
# __copy1__
# __copy2__
#
# rough sort of network discovery, or "broadcast ping", runs many
# probes in background scanning a network range (class C assumed)
# and shows online/offline addresses
#
CMD=$(basename $0)
CMDVER="1.2"
CMDSTR="$CMD v$CMDVER (2022-02-06)"

set -e -u

usage()
{
	echo "
== $CMDSTR == ping network range ==

usage: $CMD [options] network [first_ip [last_ip]]

options:
 -u|--up	shows only up addresses
 -d|--down	shows only down addresses
 -n|--no-resolv	do not resolve hostnames

default first and last ip is 1 and 254 (entire class C network; note
that use class C default even for non-class C network, like 10.x)

examples:
 $CMD 10.1.1		(scans entire class C net, 1 .. 254)
 $CMD 10.10.1 200 250

" >&2
	exit 1
}

cleanup()
{
	trap '' EXIT ERR 1 2 3
	cd /tmp
	rm -rf $tmp
	trap EXIT ERR 1 2 3
	return 0
}

show_host()
{
	local ip=$1
	local status=${2:-}
	local name=

	if [ "X$status" != "X" ]
	then
		printf " %-20s %-10s " $ip "$status"
	else
		printf " %-20s " $ip
	fi

	$F_resolve || {
		echo
		return 0
	}
	name=$(host $ip 2>/dev/null | head -1)
	case $name in
	  *NXDOMAIN*)	echo ;;
	  *)		echo "$name" | sed -e 's/.* pointer //' -e 's/\.$//' ;;
	esac
	return 0
}


# (MAIN)

only_up=false
only_down=false
F_resolve=true

while [ $# != 0 ]
do
  case $1 in
    -u|--up)		only_up=true; only_down=false ;;
    -d|--down)		only_down=true; only_up=false ;;
    -n|--no-resolv)	F_resolve=false ;;
    -*|"")		usage ;;
    *)			break ;;
  esac
  shift
done

[ $# -lt 1 -o $# -gt 3 ] && usage

net=$1
first=${2:-1}
last=${3:-254}
tmp=$(mktemp -d /tmp/${CMD}-XXXXXXXXXX)

echo -en "\nSCAN $net.$first - $net.$last" >&2
$only_up && echo -en " (ONLY UP HOSTS)" >&2
$only_down && echo -en " (ONLY OFFLINE HOSTS)" >&2
echo -e "\n" >&2

trap "echo '*INTR*'; cleanup; exit 255" 1 2 3
trap "cleanup" EXIT

tput sc >&2
cr="$(tput rc)$(tput el)"

ip=$first
while [ $ip -le $last ]
do
	echo -n "$cr running probes ($net.$ip) ... ">&2

	touch $tmp/$net.$ip.run

	sh -c "ping -w 3 -c 1 $net.$ip >/dev/null 2>/dev/null \
		&& mv $tmp/$net.$ip.run $tmp/$net.$ip.ok \
		|| mv $tmp/$net.$ip.run $tmp/$net.$ip.bad" &
	ip=$(expr $ip + 1)
	sleep 0.1
done

while :
do
	sleep 0.4
	set $(ls $tmp/*.run 2>/dev/null | wc -l)
	[ $1 == 0 ] && break
	echo -n "$cr wait ... ($1 probes left) ..." >&2
done
echo -n "$cr" >&2
sleep 1

# look the tempdir content, using the same sequence used
# to probe (using ls or sort on dir content will not have
# the same result, ie 10.1.1.201 is less than 10.1.1.3)
#
ip=$first
while [ $ip -le $last ]
do
	$only_up && {
		[ -f $tmp/$net.$ip.ok ] && show_host $net.$ip
		ip=$(expr $ip + 1)
		continue
	}
	$only_down && {
		[ -f $tmp/$net.$ip.bad ] && show_host $net.$ip
		ip=$(expr $ip + 1)
		continue
	}
	[ -f $tmp/$net.$ip.ok ] && show_host $net.$ip "ok" || show_host $net.$ip "(offline)"
	ip=$(expr $ip + 1)
done

cleanup
exit 0
