#!/bin/ksh93 --
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

# Check hostname configuration as per the sendmail code.
#
# For details see  http://www.sendmail.com/sm/open_source/docs/vendor_info/sun/migration.html#FQHN
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# Portions Copyright 2014 Jens Elkner.

PATH=/bin:/usr/sbin

# If $1 has a ".", accept it and exit.

function accept_if_fully_qualified {
	case $1 in
	*.*)
		print "Hostname '${myhostname}' OK: fully qualified as '$1'"
		exit 0
		;;
	esac
}

# Check the `getent hosts $1` output, skipping the 1st entry (IP address).

function check_gethostbyname {
	typeset H=${ getent hosts $1 ; } host
	for host in ${H##*(\S)*(\s)} ; do
		accept_if_fully_qualified $host
	done
}

# Parse /etc/hosts, looking for $1 as an entry by itself, and try to find
# a long name on the same line.  First kill all comments, then check for
# $1 as a word by itself, then take just the first such line, then skip
# its first entry (IP address).

function check_hosts_file {
	typeset LOOKUP=$1 LINE entry
	while read LINE ; do
		LINE=${LINE//#*}			# strip comments
		if [[ ${LINE} =~ (\s)${LOOKUP}(\s|$) ]]; then
			for entry in ${LINE##*(\S)*(\s)} ; do
				accept_if_fully_qualified ${entry}
			done
		fi
	done < /etc/hosts
}

# Parse the output of `nslookup $1`, checking the Name and Aliases.

function check_dns {
	typeset KEY VAL TAIL
	nslookup $1 2>/dev/null | while read KEY VAL TAIL ; do
		[[ ${KEY} == 'Name:' || ${KEY} == 'Aliases:' ]] && \
			accept_if_fully_qualified ${VAL}
	done
}

# Check the `ypmatch $1 hosts` output, skipping the 1st entry (IP address).

function check_nis {
	typeset LINE entry
	ypmatch $1 hosts | while read LINE ; do
		for entry in ${LINE##*(\S)*(\s)} ; do
			accept_if_fully_qualified ${entry}
		done
	done
}

# Recommend how to reconfigure to get $1.$2 as the FQHN.
# $3 is the first entry for hosts in /etc/nsswitch.conf . 

function suggest_fix_and_exit {
	typeset myhost=$1
	typeset suggested_domain=$2
	typeset fhe=$3
	typeset myipaddr=${ getent hosts ${myhost} ; }
	myipaddr=${myipaddr%%+(\s)*} 

	# aliases: skip the 1st & 2nd entries: IP address & canonical name

	typeset ALIASES='' IP FQHN TAIL
	integer FOUND=0
	while read IP FQHN TAIL ; do
		if [[ ${IP} == ${myipaddr} ]]; then
			ALIASES=${TAIL}
			FOUND=1
			break
		fi	
	done < /etc/hosts
	[[ -z ${ALIASES} ]] && ALIASES='[ aliases ... ]'

	print -n 'We recommend '
	if [[ ${fhe} != 'files' ]] ; then
		print 'listing files first for hosts in /etc/nsswitch.conf'
		print -n 'and then '
	fi
	if (( ! FOUND )); then
		print 'changing the /etc/hosts entry:\n'
		print "${myipaddr} ${myhost} ${ALIASES}\n"
		print 'to:\n'
	else
		print 'adding the /etc/hosts entry:\n'
	fi
	print "${myipaddr}\t${myhost} ${myhost}.${suggested_domain} ${ALIASES}"
	exit 0
}

# Fall back to the NIS domain, minus the first label.  If it is non-null,
# use it but recommend against it.  $2 is just informative, indicating whether
# we're checking the NIS domain.  $3 is to pass on.

function check_nis_domain {
	nisdomain=${ domainname ; }
	realdomain=${nisdomain#*.}
	if [[ -n ${realdomain} ]] ; then
		print "Hostname $1 can be fully qualified using NIS$2 domain"
		print "	${nisdomain}"
		print "resulting in the name"
		print "	$1.${realdomain}"
		print "but this is bad practice.\n"
		suggest_fix_and_exit $1 ${realdomain} $3
	fi
}

myhostname=${ hostname ; }	# global variable

# Goal: try to fully qualify `hostname` as sendmail would.
# Algorithm (stop as soon as a name with a dot is found):
#    1. gethostbyname (simulate with getent hosts)
#    2. fall back to individual hosts: methods in nsswitch.conf, using
#       only those that are configured, in their configured order
#       * files (parse /etc/hosts directly)
#       * dns (parse nslookup output)
#       * nis (parse ypmatch output)
#    3. fall back to the NIS domain name.
# If none of the above succeed, give up.  Recommend:
#    a. the domain entry in /etc/resolv.conf, if one exists
#    b. "pick.some.domain"
function doMain {
	typeset hosts_line first_hosts_entry entry nis_domains KEY VAL TAIL

	check_gethostbyname ${myhostname}

	while read KEY VAL ; do
		if [[ ${KEY} == 'hosts:' ]]; then
			hosts_line=${VAL%%#*}
			break
		fi
	done < /etc/nsswitch.conf
	first_hosts_entry=${hosts_line%%+(\s)*}
	nis_domains=''
	
	for entry in ${hosts_line}; do
		case ${entry} in
		files)
			check_hosts_file ${myhostname}
			;;
		dns)
			check_dns ${myhostname}
			;;
		nis)
			check_nis ${myhostname}
			nis_domains="${nis_domains} nis"
			;;
		esac
	done
	
	for entry in ${nis_domains} ; do
		[[ ${entry} == 'nis' ]] && \
			check_nis_domain ${myhostname} '' ${first_hosts_entry}
	done
	
	typeset realdomain=''
	while read KEY VAL TAIL ; do
		[[ ${KEY} == 'domain' || ${KEY} == 'search' ]] && realdomain=${VAL}
	done < /etc/resolv.conf
	
	[[ ${realdomain} =~ [^.]\.[^.] ]] || realdomain='pick.some.domain'
	
	print "Hostname ${myhostname} could not be fully qualified."
	suggest_fix_and_exit ${myhostname} ${realdomain} ${first_hosts_entry}
}

X="[+NAME?check-hostname - check if sendmail can determine the system's fully-qualified host name]"'
[+DESCRIPTION?The check-hostname script is a migration aid for \bsendmail\b(1M).  This script tries to determine the local host'"'"'s fully-qualified host name (FQHN) in a manner similar to \bsendmail\b(1M). If check-hostname is able to determine the \bFQHN\b of the local host, it reports  success.  Otherwise, check-hostname reports how to reconfigure the system so that the \bFQHN\b can be properly determined.]
[h:help?Print this help and exit]
[F:functions?Print out the list of all defined functionsi in this script and exit.]
[T:trace]:[fnlist?A comma separated list of functions of this script to trace.]
[+FILES]{
	[+/etc/hosts?Host name database]
	[+/etc/nsswitch.conf?Name service switch configuration file]
	[+/etc/resolv.conf?Configuration file for name server routines]
}
[+SEE ALSO?\bdomainname\b(1M), \bsendmail\b(1M), \bhosts\b(4)]
'
while getopts -a 'check-hostname' "${ print $X ; }" OPT ; do
	case "$OPT" in
		F) typeset +f ; exit 0 ;;
		T) [[ ${OPTARG} == 'ALL' ]] && \
			typeset -ft ${ typeset +f ; } || typeset -ft ${OPTARG//,/ } ;;
		h) $0 --man ; exit ;;
	esac
done
X=$((OPTIND-1))
shift $X

doMain
