#!/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