#!/bin/sh


# Little custom script to check expiration date of DNS domains

# Default values
#RANGE_WARNING=":2592000" # 30 days
#RANGE_CRITICAL=":691200" # 8 days
THRESHOLD_WARNING="2592000" # 30 days
THRESHOLD_CRITICAL="691200" # 8 days
TIMESTAMP_NOW="$( date +%s )"


# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""

PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"

# Stop at the first non-catched error
set -e

# Include check_range()
. $PROGPATH/utils.sh
# No real need for this script but then you have to copy the status
# Not really clean...
#STATE_OK=0
#STATE_WARNING=1
#STATE_CRITICAL=2
#STATE_UNKNOWN=3
#STATE_DEPENDENT=4


#
# Help function
#
usage() {
	cat <<EOF
Usage :
  $0 [-w warning_threshold] [-c critical_threshold] domain1 [domain2] ...

Thresholds in seconds (or suffix by 'd' for days).

Default values:
  warning_threshold  : $THRESHOLD_WARNING
  critical_threshold : $THRESHOLD_CRITICAL
EOF
}


#
# Days/Months/Hours to seconds convert function
#
convert_to_seconds() {
	local UNIT VALUE
	UNIT="$( echo "$1" | sed 's/.*\(.\)$/\1/;q' )"
	VALUE="$( echo "$1" | sed 's/^\([0-9]*\).*/\1/;q' )"

	case "$UNIT" in
		'd')
			# Unit is days
			echo "$( expr "$VALUE" \* 86400 )"
			;;

		*)
			echo "$1"
			;;
	esac
}


#
# Check if arg is an integer
# (copied from jilles @ http://stackoverflow.com/questions/806906/how-do-i-test-if-a-variable-is-a-number-in-bash )
#
is_int() {
	case "$1" in
		''|*[!0-9]*) return 1;;
		*) return 0;;
	esac
}

#
# Extract the date fs a timestamp rom the whois response
# (print 1 line max.)
# (exit status = 1 if problem)
#
extract_date_from_whois() {
	local EXPIRATION_DATE

	# We put each format on a dedicated line
	# + filter only the first line
	# Note : some sed implementations seems not to offer
	#        case-insensitive option, so we try not to
	#        use it for the moment.
	# 1 - kernel.org, icann.org
	# 2 - coca-cola.com (we try to ignore lines starting with space...)
	# 3 - facebook.com (...but if there's nothing else, take it anyway)
	# 4 - some databases with simple display (.se, .si)
	# Add your own filter here :)
	EXPIRATION_DATE="$( sed -n \
		-e 's/^Registry Expiry Date: //p' \
		-e '/^[^[:space:]]/s/.*\(xpiry\|xpiration\) Date: //p' \
		-e 's/.*\(xpiry\|xpiration\) Date: //p' \
		-e 's/^expires\?:[[:space:]]*//p' \
		| sed 'q'
		)"

	# Small protection against injection
	if [ -n "$( echo "$EXPIRATION_DATE" | LANG=C sed -n '/^[a-zA-Z0-9:.\-]\{1,64\}$/p' )" ]; then
		date --date="$EXPIRATION_DATE" +%s
	fi
}

# Some early checks
if ! which whois >/dev/null 2>&1; then
	echo "UNKNOWN: 'whois' command not found."
	exit 1
fi


#
# Parameters management
#
while [ "$#" -gt 0 ]; do
	while getopts hw:c: f; do
		case "$f" in
			'h')
				usage
				exit
				;;

			'w')
				THRESHOLD_WARNING="$( convert_to_seconds "$OPTARG" )"
				;;

			'c')
				THRESHOLD_CRITICAL="$( convert_to_seconds "$OPTARG" )"
				;;

			\?)
				usage
				exit 1
				;;
		esac
	done
	shift $( expr $OPTIND - 1 )

	# Little checks
	if ! is_int "$THRESHOLD_WARNING" || ! is_int "$THRESHOLD_CRITICAL"; then
		echo "UNKNOWN invalid parameter : one of the threshold is not an integer."
		exit $STATE_UNKNOWN
	fi

	# End of the options, we get the domain name
	if [ -z "$1" ]; then
		# No more options but no domain specified ? Weird but well...
		break
	fi
	DOMAIN="$1"
	shift

	# get the expiration date for this domain
	test -n "$EXPIRATION_DATE" && sleep 2  # trying not to flood the whois servers
	EXPIRATION_DATE="$( whois "$DOMAIN" | extract_date_from_whois )"
	if [ -z "$EXPIRATION_DATE" ]; then
		# We couldn't get the date :-(
		if [ "$OUTPUT_EXIT_STATUS" -eq "$STATE_OK" ]; then
			OUTPUT_EXIT_STATUS="$STATE_UNKNOWN"
		fi
		OUTPUT_DETAIL_UNKNOWN="$OUTPUT_DETAIL_UNKNOWN $DOMAIN:could_not_get_date"
		break
	fi

	# Dispatch in the OK/Warning/Critical boxes
	OUTPUT_DOMAIN_DETAIL="$DOMAIN:$( date --date=@$EXPIRATION_DATE +%FT%T%z)"
	if [ "$EXPIRATION_DATE" -le "$( expr "$TIMESTAMP_NOW" + "$THRESHOLD_CRITICAL" )" ]; then
		# Domain is critical
		OUTPUT_EXIT_STATUS="$STATE_CRITICAL"
		OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL $OUTPUT_DOMAIN_DETAIL"
	elif [ "$EXPIRATION_DATE" -le "$( expr "$TIMESTAMP_NOW" + "$THRESHOLD_WARNING" )" ]; then
		# Domain is warning
		OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING $OUTPUT_DOMAIN_DETAIL"
		# we don't change if the status is already Critical
		# (but we take precedence over Unknown)
		if [ "$OUTPUT_EXIT_STATUS" -ne "$STATE_CRITICAL" ]; then
			OUTPUT_EXIT_STATUS="$STATE_WARNING"
		fi
	else
		# Domain is Ok
		OUTPUT_DETAIL_OK="$OUTPUT_DETAIL_OK $OUTPUT_DOMAIN_DETAIL"
	fi
done

# final output
case "$OUTPUT_EXIT_STATUS" in
	"$STATE_OK")
		echo "OK $OUTPUT_DETAIL_OK"
		;;

	"$STATE_WARNING")
		echo "WARNING $OUTPUT_DETAIL_WARNING"
		;;

	"$STATE_CRITICAL")
		echo "CRITICAL $OUTPUT_DETAIL_CRITICAL"
		;;

	"$STATE_UNKNOWN")
		echo "UNKNOWN $OUTPUT_DETAIL_UNKNOWN"
		;;

	*)
		echo "WTF"
		;;
esac

exit "$OUTPUT_EXIT_STATUS"