#!/bin/bash
# Network testing script v 1.11
# (c) 2005-2020 Javier Fernandez-Sanguino
#
# This script will test your system's network configuration using basic
# tests and providing both information (INFO messages), warnings (WARN)
# and possible errors (ERR messages) by checking:
# - Interface status
# - Availability of configured routers, including the default route
# - Proper host resolution, including DNS checks
# - Proper network connectivity, including ICMP and web connections to 
#   a remote web server (the web server used for the tests can be configured, 
#   see below)
#
# Some of the network tests are described in more detail at
# http://ubuntuforums.org/archive/index.php/t-25557.html
# 
# The script does not need special privileges to run as it does not 
# do any system change. It also will not fix the errors by itself.
# 
# Additional software requirements:
#    * ip from the iproute2 package. (could probably be rewrittent to
#    use ifconfig only or to parse /proc)
#    * ping from the iputils-ping package or the netkit-ping package.
#    * nc from the netcat-openbsd or ncat package.
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#  
# You can also find a copy of the GNU General Public License at
# http://www.gnu.org/licenses/licenses.html#TOCLGPL
#
# TODO
# - Works only on Linux, can this be generalised for other UNIX systems
#   (probably not unless rewritten in C)
# - Does not check for errors properly, use -e and test intensively
#   so that expected errors are trapped
#   (specially for tools that are not available, like netcat)
# - If the tools are localised to languages != english the script might 
#   break 
# - Ask 'host' maintainer to implement error codes as done with 
#   dlint
# - Should be able to check if DNS server is in the same network, if 
#   it doesn't answer to pings, check ARP in that case.
# - DHCP checks?
# - Other internal services tests? (LDAP if using pam...)
# - Generate summary of errors in the end (pretty report?)
# - Check if packets are being dropped by local firewall? (use dmesg
#   and look for our tests)
# - Support wireless interfaces? (use iwconfig)
# - Check for more than one web server (have CHECK_HOSTS be a number
#   of hosts and determine a metric to spout an error) ?
# - Use traceroute or tcptraceroute to see if there is network connectivity?
#   (traceroute is usually blocked by firewalls but tcptraceroute might
#   be an alternative to using nc)
# - Use mii-tool (requires root privileges)
# - Use ping -s XXXX to detect invalid MTUs
# - Use arpping to detect another host with our same IP address
# - Check other TODOs inline in the code

# Default values
VERB=3
LOG=0
while getopts ":hsv:" Option
do
    case $Option in
    v )	VERB=$OPTARG;;
    s )	LOG=1;;
    * )	cat <<- EOF
	Usage: $0 [-s][-v <num>]

	 -s     Also log messages to local3 syslog facility
	 -v 0   Silent run
	 -v 1   Show only error messages
	 -v 2   Show error and warning messages
	 -v 3   Show information messages (default)
	 -v 4   Show detailed system information
	 -v 5   Show information from external APIs (IP address and GeoIP)

EOF
	 exit 0;;
    esac
done

# BEGIN configuration
# Configure to your needs, these values will be used when
# checking DNS and Internet connectivity
# DNS name to resolve.
# These are default values which can be overriden by the environment.
[ -z "$CHECK_HOST" ] && CHECK_HOST=www.debian.org
[ -z "$CHECK_IP_ADRESS" ] && CHECK_IP_ADRESS=194.109.137.218
# Web server to check for
[ -z "$CHECK_WEB_HOST" ] && CHECK_WEB_HOST=www.debian.org
[ -z "$CHECK_WEB_PORT" ] && CHECK_WEB_PORT=80
# Web service to check for a specific content (fixed MD5 value)
[ -z "$CHECK_WEB_URL" ] && CHECK_WEB_URL="http://network-test.debian.org/nm"
[ -z "$CHECK_WEB_MD5" ] && CHECK_WEB_MD5="8ba464e5fb512de41d9b1816e7582743"
# Service to return IP address
# It should just return the IP address
# for example in PHP have an index.php file with:
# "<?php echo $_SERVER['REMOTE_ADDR'] ?>" 
#with no carriage returns or line feed characters.

# External services to obtain system information, these are only
# queried if the user selects the highest debug level
[ -z "$CHECK_IP_URL" ] && CHECK_IP_URL=https://api.ipify.org
[ -z "$CHECK_IP6_URL" ] && CHECK_IP6_URL=https://api6.ipify.org
[ -z "$CHECK_GEOIP_URL" ] && CHECK_GEOIP_URL=http://api.geoiplookup.net/
# END configuration
export CHECK_HOST CHECK_IP_ADRESS CHECK_WEB_HOST CHECK_WEB_PORT CHECK_IP_URL CHECK_IP6_URL CHECK_GEOIP_URL

PATH=/bin:/sbin:/usr/bin:/usr/sbin
# Set our locale environment, just in case any of the tools are translated
LC_ALL=C
export PATH LC_ALL

# Trap interrupts
trap 'echo "`basename $0`: Quitting early due to interrupt" 1>&2; exit 1;' 1 2 3 15

# error reporting and logging functions
debug () {
    [ "$VERB" -gt 3 ] && echo "DEBUG: $1"
}
debugline () {
    echo "--------------------------------------------------------------------------------"
}
    

info () {
    [ "$VERB" -gt 2 ] && echo "INFO: $1"
    [ "$VERB" -gt 2 ] && [ "$LOG" -eq 1 ] && logger -p local3.info "$0 INFO: $1"
}

warn () {
    [ "$VERB" -gt 1 ] && echo "WARN: $1"
    [ "$VERB" -gt 1 ] && [ "$LOG" -eq 1 ] && logger -p local3.warn "$0 WARN: $1"
}

err () {
    [ "$VERB" -gt 0 ] && echo "ERR: $1" >&2
    [ "$VERB" -gt 0 ] && [ "$LOG" -eq 1 ] && logger -p local3.err "$0 ERR: $1"
}

# Report and error with a command an clear a temporary file
cmd_err()  {
     command=$1
     exitval=$2
     tempfile=$3
     warn "Execution of '$command' on the interface did not complete successfully (exit value $2)."
     clear_temp $tempfile
}

clear_temp() {
     tempfile=$1
     if  [ -n "$tempfile" ] && [ -e "$tempfile" ]; then
	     /bin/rm -f -- "$tempfile"
     fi
     trap  0 1 2 3 13 15
}



# Check if all commands we need are available
# NOTE:  if using nslookup add "nslookup dnsutils"
( /bin/echo -e "netstat net-tools\nifconfig net-tools\n\
ping netkit-ping|inetutils-ping|iputils-ping\n\
arp net-tools\nip iproute\nhost host|bind9-host\nmktemp debianutils\n\
nc netcat-openbsd" | 
while read cmd package; do
if ! `which $cmd 2>/dev/null >&2`; then
	err "$cmd is not available! (please install $package)" 
	exit 1
fi
done ) || exit 1
# Recommended programs
( /bin/echo -e "ethtool ethtool" |
while read cmd package; do
if ! `which $cmd 2>/dev/null >&2`; then
	warn "$cmd is not available (consider installing $package)" 
	exit 1
fi
done )

# Default route for programs
ETHTOOL=/usr/sbin/ethtool
MIITOOL=/sbin/mii-tool

# Other needs
# We need /proc/net
if [ ! -d /proc/net ] ; then
	err "/proc is not available! Please mount it ('mount -t /proc')"
	exit 1
fi

defaultif=none
export defaultif

# Check the network routes configured in the system
# 
# Specifically, check the default route to see if the system has one.
# If it has, the interface configured for it is identified
check_routes () {
# Extract the interface of our default route
	local status=0
        local defaultroutes=""
	defaultif="`netstat -nr |grep ^0.0.0.0 | awk '{print $8}' | head -1`"
	defaultroutes="`netstat -nr |grep ^0.0.0.0 | wc -l`"
	if [ -z "$defaultif" ] ; then
		defaultif=none
		warn "This system does not have a default route"
		status=1
	elif [ "$defaultroutes" -gt 1 ] ; then
		warn "This system has more than one default route"
	else 
		info "This system has exactly one default route"
	fi
        debug "Interfaces routes: $defaultif"
        debug "System routes: $defaultroutes"
	return $status
}



# Check the status of the loopback interface
#
# If the loopback interface is not define there might be issues
# with local connnectivity.
check_local () {
	local status=0
# Is there a loopback interface? 
	if [ -n "`ip link show lo`" ] ; then
# OK, can we ping localhost
		if  ! check_host localhost 1; then
# Check 127.0.0.1  instead (not everybody uses this IP address however,
# although its the one commonly used)
			if  ! check_host 127.0.0.1 1; then
				err "Cannot ping localhost (127.0.0.1), loopback is broken in this system"
			else
				err "Localhost is not answering but 127.0.0.1, check /etc/hosts and verify localhost points to 127.0.0.1"
			fi
		else
		 info "Loopback interface is working properly"
		fi
			
	else
		err "There is no loopback interface in this system"
		status=1
	fi
	return $status
}

# Check the link of a Ethernet interface using 'miitool'
check_if_link_miitool () {
	local ifname=$1
	local status=0
	[ ! -x "$MIITOOL" ] && return 0
	if $MIITOOL $ifname 2>&1| grep -q "no link"; then
		status=1
	fi
	return $status
}

# Check the link of a Ethernet interface using 'ethtool'
check_if_link_ethtool () {
# Note: Unlike other sections of the script we need to be root
# to test this
	local ifname=$1
	local status=0
	[ ! -x "$ETHTOOL" ] && return 0
	LINK="`$ETHTOOL $ifname 2>&1| grep \"Link detected\"`"
	# If ethtool fails to print out the link line we break off
	# notice that ethtool cannot get the link status out of all
	# possible network interfaces
	[ -z "$LINK" ] && return
	if ! echo $LINK | grep -q "Link detected: yes" ; then
		status=1
	fi
	return $status
}

# Check the link of a Ethernet interface using 'ip link'
check_if_link_iplink () {
	local ifname=$1
	local status=0
	[ ! -x /sbin/ip ] && return 0
# Check if the interface is up
        if [ -z "`ip link show $real_iface up 2>/dev/null`" ]; then
            status=1
	fi
	return $status
}


# Check the link of a Ethernet interface 
#
# Depending on how is the script is running either ethtool or
# 'ip link' is used.
check_if_link_ethernet() {
	local status=-1
	local iface=$1
	# Use ethtool if installed (preferable to mii-tool)
	# If none are installed we will test using 'ip link show'
	if [ "`id -u`" -eq 0 ] ; then
		if [ -x "$ETHTOOL" ] ; then
			check_if_link_ethtool $iface
			status=$?
		elif [ -x "$MIITOOL" ]; then
			check_if_link_miitool $iface
			status=$?
		fi
	fi
	# If no test has done use ip link
	if [ $status -eq -1 ]; then
		check_if_link_iplink $iface
		status=$?
	fi
	return $status
}

# Check the link of a generic  interface 
# 
check_if_link_generic() {
	local status=-1
	local iface=$1
        check_if_link_iplink $iface
        status=$?
	return $status
}

# Check a network interface
#
# Test a network interface to see if it is working properly. Valid
# tests for interfaces are:
#  - link tests (for Ethernet interfaces)
#  - IP address assignment. An interface without IP might indicate
#    an error in network connectity (e.g. failure to obtain an IP
#    using DHCP)
#  - packet statistics. An interface with 0 packets transmitted / received
#    or errors might indicate a malfunctioning device.
# 
# TODO:
#  - do specific wireless tests for WiFi networks (e.g. determine if the
#  interface is properly associated with its configured network - check if the
#  link up test should be sufficient for this)

check_if () {
	local ifname=$1
	local status=0
	local realif=$ifname
	[ -z "$ifname" ] && return 1

# Check if the interface has a link
# The following allows for specific tests to be done on specific
# types of interfaces
	case "$ifname" in
	        en*@* | eth*@*) 
		      realif="`echo $ifname | sed -e 's/^.*@//g'`"
		      ifname="`echo $ifname | sed -e 's/@.*//g'`"
	              check_if_link_ethernet $realif ; status=$?;;
	        en* | eth*) check_if_link_ethernet $ifname ; status=$?;;
	        *)  check_if_link_generic $ifname; status=$? ;;
	esac
# Print results
	if [ $status -ne 0 ] ; then
		if  [ "$ifname" = "$defaultif" ] ; then
			err "The $ifname interface that is associated with your default route has no link!"
		else 
			if [ "$realif" = "$ifname" ] ; then
		                warn "Interface $ifname does not have link"
			else
				warn "The interface $realif does not have link (underlying interface of $ifname"
			fi
		fi
	fi
# Prepare a tempfile
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15
# Find IP addresses for $ifname
	ip addr show $ifname 2>/dev/null >$tempfile  
	exitval=$?

# Debug information
        if [ "$VERB" -gt 3 ] ; then
            debug "Interface information:"
            debugline
            cat $tempfile
            debugline
        fi

	if [ "$exitval" != "0" ] || [ ! -s "$tempfile" ] ; then
		cmd_err "ip addr show" $exitval $tempfile
		clear_temp $tempfile
		return 1
	fi
		
	inetaddr="`cat \"$tempfile\" | grep \"inet \" | awk '{print $2}' | sed -e 's/\/.*//'`"
	if [ -z "$inetaddr" ] ; then
		warn "The $ifname interface does not have an IP address assigned"
		status=1
	else
# TODO: WARN if more than 2 IP addresses?
		echo $inetaddr | while read ipaddr; do
			info "The $ifname interface has IP address $ipaddr assigned"
		done
	fi

# Lookup TX and RX statistics
	ifconfig $ifname 2>/dev/null >$tempfile  
	exitval=$?
	if [ "$exitval" != "0" ] || [ ! -s "$tempfile" ] ; then
		cmd_err "ifconfig" $exitval $tempfile
		clear_temp $tempfile
		return 1
	fi
        if [ "$VERB" -gt 3 ] ; then
            debug "Interface detail statistics:"
            debugline
            cat $tempfile
            debugline
        fi

# TODO: This is done using ifconfig but could use /proc/net/dev for
# more readibility or, better, 'netstat -i'
	txpkts="`cat \"$tempfile\" | awk '/TX packets/ { print $3 }' |sed 's/.*://'`"
	rxpkts="`cat \"$tempfile\" | awk '/RX packets/ { print $3 }' |sed 's/.*://'`"
	txerrors="`cat \"$tempfile\" | awk '/TX errors/ { print $3 }' |sed 's/.*://'`"
	rxerrors="`cat \"$tempfile\" | awk '/RX errors/ { print $3 }' |sed 's/.*://'`"

# Abort if we do not have values to check
	[ -z "$txpkts" ] &&  [ -z "$rxpkts" ]  && return 0

# TODO: Check also frames and collisions, to detect faulty cables
# or network devices (cheap hubs)
	if [ "$txpkts" -eq 0 ] && [ "$rxpkts" -eq 0 ] ; then
		err "The $ifname interface has not tx or rx any packets. Link down?"
		status=1
	elif  [ "$txpkts" -eq 0 ]; then
		warn "The $ifname interface has not transmitted any packets."
	elif [ "$rxpkts" -eq 0 ] ; then
		warn "The $ifname interface has not received any packets."
	else
		info "The $ifname interface has tx and rx packets."
	fi

# Abort if we do not have values to check
	[ -z "$txerrors" ] &&  [ -z "$rxerrors" ]  && return 0
# TODO: It should be best if there was a comparison with tx/rx packets.
# a few errors are not uncommon if the card has been running for a long
# time. It would be better if a relative comparison was done (i.e.
# less than 1% ok, more than 20% warning, over 80% major issue, etc.)
	if [ "$txerrors" -ne 0 ]; then
		warn "The $ifname interface has tx errors."
	fi
	if [ "$rxerrors" -ne 0 ]; then
                warn "The $ifname interface has rx errors."
	fi

	clear_temp $tempfile

	return $status
}

# Check the status of the network interfaces in the host
#
# The list of network interfaces is obtained using 'ip link' and
# this list is reviewed to see if there is any valid (i.e. "UP")
# interface.
# If there is, the interface is analysed to determine if it
# is working properly.
# 
check_netif () {
	local status=0

        # Debugging information
        if [ "$VERB" -gt 3 ] ; then
            debug "List of interfaces using IP:"
            debugline
            ip link show
            debugline
            if [ -x /usr/bin/nmcli ]; then
                debug "List of interfaces known to Network Manager:"
                debugline
                nmcli -p device show |tee
                debugline
                debug "List of connections known to Network Manager:"
                debugline
                nmcli -p connection show |tee
                debugline
            fi
        fi

	ip link show | egrep '^[[:digit:]]' | 
	{ 
	validif=0
	while read ifnumber ifname status extra; do
		ifname="`echo $ifname |sed -e 's/:$//'`" # Strip the ending ':'
		[ "$ifname" = "lo" ] && continue  # Skip loopback
                # TODO: this is redundant with the check if_link test
                # (although faster since using it would make us call 'ip'
                # twice.
		if  [ -n "`echo $extra | grep DOWN `" ] ||
		    [ -n "`echo $status | grep NO-CARRIER`" ] 
		then
			if  [ "$ifname" = "$defaultif" ] ; then
				err "The $ifname interface that is associated with your default route is down!"
				status=1
			elif  [ "$ifname" = "lo"  ] ; then
				err "Your lo interface is down, this might cause issues with local applications (but not necessarily with network connectivity)"
			else
				warn "The $ifname interface is down"
			fi
			if  [ -n "`echo $extra | grep LOWERLAYERDOWN `" ] ; then
		      		realif="`echo $ifname | sed -e 's/^.*@//g'`"
				info "$ifname is down because the underlying interface '$realif' is down"
			fi

		else
		# Check network routes associated with this interface
			info "The $ifname interface is up"
			check_if $ifname # Check the interface
			check_netroute $ifname # Check routes assigned to it
			# TODO: Determine under which conditions an
			# interface is valid
			validif=$(( $validif +1 )) 
		fi
	done; 
	return $validif; 
	} 
	if [ "$?" -eq 0 ] ; then
		err "No valid network interfaces were found. System does not have network connectivity"
		status=1
	fi

	return $status
}

# Checks the network routes assigned to a given interface
# if any of the routers defined for a network route does not
# answer then consider it is not reachable (and, consequently, its route
# might not be reachable). We do not consider the remote network
# as unavailable as there might be two different routers for 
# the same network and we do not differentiate these
check_netroute () {
	local ifname=$1
	[ -z "$ifname" ] && return 1

        # Debugging information
        if [ "$VERB" -gt 3 ] ; then
            debug "List of ip routes using IP:"
            debugline
            ip route show
            debugline
            debug "List of routes using netst:"
            debugline
            netstat -nr 
            debugline
        fi

	netstat -nr  | grep "${ifname}$" |
	while read network gw netmask flags mss window irtt iface; do
	# For each gw that is not the default one or a direct network,
	# check it
	# (default route is tested separately)
		if [ "$network" != "0.0.0.0" ] && [ "$gw" != "0.0.0.0" ]; then
			if ! check_router $gw  ; then
				err "The router $gw (interface $ifname) is not reachable (network $network migth not be available)"
				return 1
			fi
		fi
	done
	return 0
}

# Checks the default network routes 
check_default_route () {
 	local valid_defgw=0
	netstat -nr |grep ^0.0.0.0 | {
 	valid=0	
	while read network gw netmask flags mss window irtt iface; do
	# Test each default route separately
	# check it
	# (default route is tested separately)
		if ! check_router $gw  ; then
			warn "The router $gw for the default route (in interface $iface) is not reachable"
		else
		 	valid=$(( $valid +1 )) 
		fi
	done;
	return $valid; 
	} 
 	valid_defgw=$?
	if [ "$valid_defgw" -eq 0 ] ; then
		err "The default route is not available"
		return 1
	fi
	return 0
}

# Checks if a router is up and alive
#  The check is done first by sending ICMP queries to the router
#  and then we check if we have the ARP address of it. 
#  A router that does not answer to ICMP queries is just a warning
#  (some firewalls will behave this way) but we consider it 
#  a failure if we do not obtain a proper MAC address 
check_router () {
	local router=$1
	local status=0
	[ -z "$router" ] && return 1
# First ping the router, if it does not answer then check arp tables and
# see if we have an arp. We use 5 packets since it is in our local network.
	ping -n -q -c 5 "$router" >/dev/null 2>&1 
	if [ "$?" -ne 0 ]; then
		warn "Router $router does not answer to ICMP pings"
# Router does not answer, check arp
		routerarp="`arp -n | grep \"^$router\" | grep -v incomplete`"
		if [ -z "$routerarp" ] ; then
			err "We cannot retrieve a MAC address for router $router"
			status=1
		fi
	fi
	if [ "$status" -eq 0 ] ; then
		info "The router $router is reachable"
	fi
	return $status
}

# Check if a host is reachable
# The host is checked by first sending ICMP queries
# if the host does not answer to them it might be firewalled
# or unavailable. 
# This script is intented to be run for any host (local or remote)
# so it does not check for ARP replies (unlike the check_router() 
# function above.
#
# TODO: 
# - if the host is in our local network (no route needs to be used) then
#   check ARP availability
# - if the host is not on our local network then check if we have a route
#   for it
# - if it is a remote host we could alternatively use some other network
#   tests since ICMP queries are (unfortunately) many times firewalled.
check_host () {
	local host=$1
	[ -z "$host" ] && return 1
# Use 10 packets as we expect this to be outside of our network
	COUNT=10
	[ -n "$2" ] && COUNT=$2
	status=0
	ping -n -q -c $COUNT "$host" >/dev/null 2>&1 
	if [ "$?" -ne 0 ]; then
		warn "Host $host does not answer to ICMP pings"
		status=1
	else
		info "Host $host answers to ICMP pings"
	fi
	return $status
}

# Check the nameservers defined in /etc/resolv.conf
#  First the /etc/resolv.conf file is parsed, then, for each nameserver 
#  found in it:
#  - We check if it is reachable using check_host 
#  - We try to make a DNS query to see if it answers
# 
# NOTE: Not all systems use DNS, /etc/nsswitch.conf might be configured
# to use LDAP or file-based (ug!) queries to find out remote hosts.
# However, in most cases, failure of DNS means the network connectivity
# fails too.
check_dns () {
	local status=1
	local nsfound=0
	local nsok=0
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15

        # Debugging information
        if [ "$VERB" -gt 3 ] ; then
            debug "DNS resolution configuration":
            debugline
    	    cat /etc/resolv.conf 
            debugline
        fi

	cat /etc/resolv.conf | grep -v ^# | grep nameserver | 
	awk '/nameserver/ { for (i=2;i<=NF;i++) {  print $i ; } }'  >$tempfile
	if [ ! -s "$tempfile" ] ; then
		err "The system does not have any nameserver configured"	
		return 1
	fi

	for nameserver in `cat $tempfile`;  do
		nsfound=$(( $nsfound + 1 ))
		info "This system is configured to use nameserver $nameserver"
		check_host $nameserver 5
		if check_ns $nameserver ; then
			nsok=$(( $nsok +1 ))
		else
			status=$?
		fi
	done


	#Could also do:
	#nsfound="`wc -l $tempfile | awk '{print $1}'`"
	clear_temp $tempfile

	if [ "$nsfound" -eq 0 ] ; then
		err "The system does not have any nameserver configured"	
		return 1
	else
		if [ "$status" -ne 0 -a "$nsok" -eq 0 ]  ; then
			if [ "$nsfound" -eq 1 ] ; then
				err "There is one nameserver configured for this system but it does not work properly"
			else
				err "There are $nsfound nameservers configured for this system and none of them works properly"
			fi
		else
			if [ "$nsfound" -eq 1 ] ; then
				info "The nameserver configured for this system works properly"
			else
				info "There are $nsfound nameservers configured for this system and $nsok are working properly"
			fi
		fi
	fi
	return $status
}

# Check the nameserver using a pre-defined host
#  This function tests a remote DNS server by querying it with 
#  a pre-defined host and tries to determine if the DNS query
#  works as expected
#
# TODO: use nslookup?
#	nslookup $CHECK_HOST -$nameserver 
check_ns () {
	local nameserver=$1
	local status=1
	[ -z "$nameserver" ] && return 1
	CHECK_RESULT="$CHECK_HOST .* $CHECK_IP_ADDRESS"
# Using dnscheck:
	dnscheck="`host -t A $CHECK_HOST $nameserver 2>&1 | tail -1`"
	if [ -n "`echo $dnscheck |grep NXDOMAIN`" ] ; then
		err "Dns server $nameserver does not resolv properly"
	elif [ -n "`echo $dnscheck | grep \"timed out\"`" ] ; then
		err "Dns server $nameserver is not available"
	elif [ -z "`echo $dnscheck | egrep \"$CHECK_RESULT\"`" ] ; then
		warn "Dns server $nameserver did not return the expected result for $CHECK_HOST"
	else
		info "Dns server $nameserver resolved correctly $CHECK_HOST"
		status=0
	fi

# Using dlint
#	dlint $CHECK_HOST @$nameserver >/dev/null 2>&1
#	if [ $? -eq 2 ] ; then
#		err "Dns server $nameserver does not resolv properly"
#	elif [ $? -ne 0 ]; then
#		err "Unexpected error when testing $nameserver"
#	else
#		info "Dns server $nameserver resolved correctly $CHECK_HOST"
#		status=0
#	fi

	return $status
}

# Checks network connectivity to a web host
#
#  Makes a check to test if a remote (pre-defined) system is available through
#  the network.
#
#  This function currently uses two tests:
#   - check_host() which implements network test (ICMP queries)
#   - a standard query to the web server, the query is direct using
#     netcat. I.e. it does not use any proxy settings available in the system
#
# TODO: 
# - this could also implement proxy checks (if the http_proxy environment is
#   defined?)
check_conn () {
	local status=0
	if ! check_host $CHECK_WEB_HOST >/dev/null ; then
		warn "System does not seem to reach Internet host $CHECK_WEB_HOST through ICMP"
	else
		info "System can reach Internet host $CHECK_WEB_HOST"
	fi
# Check web access, using nc
	/bin/echo -e "HEAD / HTTP/1.0\n\n" |nc -w 20 $CHECK_WEB_HOST $CHECK_WEB_PORT >/dev/null 2>&1
	if [ $? -ne 0 ] ; then
		err "Cannot access web server at Internet host $CHECK_WEB_HOST (port $CHECK_WEB_PORT)"
		status=1
	else
		info "System can access web server at Internet host $CHECK_WEB_HOST (port $CHECK_WEB_PORT)"
	fi
	return $status
}


# Checks network connectivity to a web host checking the content
#
#  Makes a check to test if a remote (pre-defined) system is available through
#  the network and the content it delivers is provided without changes.
#
check_web_content () {
	local status=0
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15
# Check IP address, using curl
        /usr/bin/curl -s $CHECK_WEB_URL >$tempfile 2>&1
	if [ $? -ne 0 ] ; then
		err "Cannot access web service at  $CHECK_WEB_URL"
		status=1
	else
		info "System can access web service at $CHECK_WEB_URL"
	fi
        md5=`md5sum $tempfile | awk '{print $1}'`
        if [ $md5 = "$CHECK_WEB_MD5" ] ; then
            info "System can connect to the Internet and received files without changes"
        else
            err "System can access the Internet, but the received files are modified somehow (file had md5 '$md5', expected ' $CHECK_WEB_MD5'"
        fi
        clear_temp $tempfile
        return $status
}

# Checks the public IP address of a service
#
#  Connects to a public web sevice to determine the system's public IP address
#
check_ip_addr () {
	local status=0
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15
# Check IP address, using curl
        /usr/bin/curl -s $CHECK_IP_URL >$tempfile 2>&1
# Alternatively:
#       URI=`echo $CHECK_IP_URL | sed -e 's|^[^/]*/|/|' 
#	/bin/echo -e "GET $URI HTTP/1.0\n\n" |nc -w 20 $CHECK_IP_URL 80 >$tempfile 2>&1
	if [ $? -ne 0 ] ; then
		err "Cannot access IPv4 address service at Internet host $CHECK_IP_URL"
		status=1
	else
		info "System can access web server at Internet host $CHECK_IP_URL"
                ip_address=`cat $tempfile`
                info "System public IPv4 address is $ip_address"
        fi
        clear_temp $tempfile
        return $status
}

check_ip6_addr () {
	local status=0
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15
# Check IP address, using curl
        /usr/bin/curl -s $CHECK_IP6_URL >$tempfile 2>&1
	if [ $? -ne 0 ] ; then
		err "Cannot access IPv6 address service at Internet host $CHECK_IP6_URL"
		status=1
	else
		info "System can access web server at Internet host $CHECK_IP6_URL"
                ip_address=`cat $tempfile`
                info "System public IPv6 address is $ip_address"
        fi
        clear_temp $tempfile
        return $status
}

check_geoip_info () {
	local status=0
	tempfile="`mktemp --tmpdir tmptestnet.XXXXXX`" || {  err "Cannot create temporary file! Aborting! " ; exit 1; }
	trap " [ -f \"$tempfile\" ] && /bin/rm -f -- \"$tempfile\"" 0 1 2 3 13 15
# Check IP address, using curl
        /usr/bin/curl -s $CHECK_GEOIP_URL >$tempfile 2>&1
	if [ $? -ne 0 ] ; then
		err "Cannot access GeoIP service at Internet host $CHECK_GEOIP_URL"
		status=1
	else
		info "System can access web server at Internet host $CHECK_GEOIP_URL"
                geoip_info=`cat $tempfile`
                info "System public geoIP information: $geoip_info"
        fi
        clear_temp $tempfile
        return $status
}


# TODO: checks could be conditioned to speed up the script, i.e. :
#  - if there is no proper interface setup don't bother with DNS 
#  - don't do Internet external checks if DNS is not setup properly
check_routes
check_local || exit 1
check_netif || exit 1
check_default_route || exit 1
check_dns   || exit 1
check_conn  || exit 1
if [ "$VERB" -gt 4 ] ; then
    check_web_content
    check_ip_addr
    check_ip6_addr
fi

exit 0

