#!/bin/sh
# ns, 14-Aug-91 Noah Friedman <friedman@prep.ai.mit.edu>
# $Id: ns,v 1.4 1996/10/23 01:56:21 friedman Exp $
#
# A front end for nslookup(1) to provide more access to the interactive
# features on the command line.  It also does some smart manipulation of
# arguments (like automatically twiddling IP addresses when doing PTR
# queries).  I don't have time to document all the bugs^H^H^H^Hfeatures.  I
# suggest you read through the script here, and read the manual page for
# nslookup if you haven't already.
#
# Old versions of bash may not be able to parse this script.  I've only
# tested it under 1.09 and later.  Also, it requires you to have a working
# nslookup.
#
# Public domain.
#
# TODO: write this in v7 sh.

# Feed this script to bash if sh is not already bash in disguise.
case "${BASH_VERSION+set}" in
  set ) : ;;
  * ) exec ${BASH-bash} $0 ${1+"$@"} ;;
esac

function usage ()
{
    if [ $# -gt 0 ]; then
       echo -e "${progname}: $*\n" 1>&2
    fi

    cat 1>&2 <<EOF
Usage: ${progname} {-D} {-q querytype} {-s server} [-t tty]|[host|ipaddr] ...
       ${progname} [-ls recordtype] [-s server] [domain] ...

   Arguments in [] are required.  Those in {} are optional.

   default querytype is "${querytype}"

   querytype can be one of: ANY CNAME HINFO MX NS PTR SOA
   recordtype can be one of: a d h m s t

   if -t option is specified, the remote host specified in /etc/utmp for
   that tty will be looked up.  Note that this doesn't work well since most
   hostnames are longer than the 16 char limit in utmp.

   For a good time, type "man nslookup"

EOF
    exit 1
}

function main ()
{
    initialize_variables "$@"
    test $# -gt 0 || usage
    parse_command_args "$@"

    test -n "${debug+set}" && set -x

    do_nslookup "$(get_hosts)"
    exit 0
}

# Change your default server (if you want to) by changing the
# variable ``name_server''.  You can also change the default query type.
function initialize_variables ()
{
    progname="${0##*/}"       # equivalent to "basename $0"
    name_server=""
    querytype="PTR"

    bq="\`"  # To prevent hairy quoting and escaping later.
    eq="'"

    unset hostlist ttys ls_str recordtype
}

function parse_command_args ()
{
 local orig_number_options=$#
 local tty_arg

    # unset option variables to make sure they weren't accidentally
    # exported
    unset debug

    # If you add new commands be sure to change the wildcards below to make
    # sure they are unambiguous (i.e. only match one possible long option)
    # Be sure to show at least one instance of the full long option name to
    # document what the long option is canonically called.
    while [ $# -gt 0 ]; do
       case z$1 in
          z-D | z--debug | z--d* )
             debug=t
             shift
            ;;
          z-h | z--help | z--h* )
             usage
            ;;
          z-ls | z--list* | z--l* )
             get_option_argument recordtype "$1" "$2"
             shift $?
             recordtype="$(echo ${recordtype} | tr '[A-Z]' '[a-z]')"
             querytype="ANY"  # to prevent reverse-PTRing address
            ;;
          z-q | z--querytype* | z--q* )
             get_option_argument querytype "$1" "$2"
             shift $?
             querytype="$(echo ${querytype} | tr '[a-z]' '[A-Z]')"
            ;;
          z-s | z--server* | z--s* )
             get_option_argument name_server "$1" "$2"
             shift $?
            ;;
          z-t | z--tty* | z--t* )
             get_option_argument tty_arg "$1" "$2"
             shift $?
             ttys="${ttys} $(echo ${tty_arg} | sed 's/\/dev\///')"
            ;;
          z-* )
             usage "${bq}${1}${eq} is not a valid option."
            ;;
          *)
             hostlist="${hostlist} $1"
             shift
            ;;
       esac
    done

    # Return number of shifted arguments so calling function can shift
    # appropriate amount.
    return $[ orig_number_options - $# ]
}

# Usage: get_option_argument VARIABLE OPTION ARG {OPTIONAL}
#    where VARIABLE is shell variable that will be set to the value ARG.
#    Long option syntax is `--foo=bar' or `--foo bar'.  3rd argument ARG
#    won't get used if first long option syntax was used.  If 4 arg
#    OPTIONAL is non-empty, option isn't required to have an argument; if
#    the argument is missing, VARIABLE is set to the empty value.
# Returns number of positions caller should shift
function get_option_argument ()
{
 local variable="$1"
 local option="$2"
 local arg="$3"
 local arg_optional="$4"

    # All long options must be at least 3 characters long (--o*), whereas
    # short options are only two chars (-o) and arguments are always
    # separate.
    if [ ${#option} -ge 3 -a "z${option#*=}" != "z${option}" ]; then
       arg="${option#*=}"  # Strip off anything before and including `=' char
       eval ${variable}=\'"${arg}"\'
       return 1
    else
       if [ -z "${arg}" -a -z "${arg_optional}" ]; then
          usage "option ${bq}${option}${eq} requires argument."
       fi
       eval ${variable}=\'"${arg}"\'
       return 2
    fi
}

function get_hosts ()
{
 local h1="$(get_tty_hosts)"
 local h2="$(get_hostlist_hosts)"

    echo ${h1} ${h2}
}

function get_tty_hosts ()
{
 local tty
 local list=""
 local ptr_cmd=":"

    if [ "${querytype}" = "PTR" -o "${querytype}" = "SOA" ]; then
        ptr_cmd='addr="$(reverse ${addr})"'
    fi

    for tty in ${ttys} ; do
       addr="$(who | fgrep ${tty} \
                   | sed -n '1{s/^[^(]*(*\([^):]*\)[):].*/\1/;s/[^@]*@//;p;}')"
       eval ${ptr_cmd}     # I should be shot for doing this.

       list="${list} ${addr}"
    done

    echo "${list}"
}

function get_hostlist_hosts ()
{
 local list=""

    if [ "${querytype}" = "PTR" -o "${querytype}" = "SOA" ]; then
       for host in ${hostlist} ; do
          list="${list} $(reverse ${host})"
       done
    else
       list="${hostlist}"
    fi

    echo "${list}"
}

function reverse ()
{
 local IFS="."
 local addr=""
 local arg=""

    set -- $1
    for arg in "$@" ; do
       addr="${arg}.${addr}"
    done

    echo "${addr}in-addr.arpa"
}

function do_nslookup ()
{
 local set_name_server_str=""
 local host_str=""

    if [ -n "${recordtype}" ]; then
       ls_str="ls -${recordtype} "
       ns_output_regexp="^ .*"
    else
       ns_output_regexp="[Aa]uthoritative|=|(\*\*\*)"
    fi

    if [ -n "${name_server}" ]; then
       set_name_server_str="server ${name_server}\n"
    fi

    for host in $* ; do
       host_str="${host_str}\n${ls_str}${host}"
    done

    ns_string="${set_name_server_str}set q=${querytype}${host_str}"

    echo -e "${ns_string}" | nslookup 2>&1 | egrep "${ns_output_regexp}"
}

main "$@"

#
# eof
#
