#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-

. /usr/local/bin/usr_local_tput.bash || exit 2
PREFIX=/usr/local
ROLE=proxy
NOW=$( date -u +%y-%m-%d_%H%m )
DEBUG=1
PROXY_WLAN=wlan4
prog=$( basename $0 .bash )

# SocksPolicy Accept in /etc/tor/torrc - required and works with sslscan

ip route | grep ^default || { ERROR "not connected" ; exit 1 ; }
PATH=$PATH:/sbin
BADSSL_SITES=(
    self-signed.badssl.com
    expired.badssl.com
    mixed.badssl.com
    rc4.badssl.com
    hsts.badssl.com
)

badssl=0
[ "$#" -eq 0 ] && badssl=1
# tests="$MODE"
tests=""
nodig=1
verbosity=2
outdir=/tmp
timeout=20

#[ -f /usr/local/etc/testforge/testforge.bash ] && \
#    . /usr/local/etc/testforge/testforge.bash

. /usr/local/bin/proxy_ping_lib.bash || \
    { ERROR loading /usr/local/bin/proxy_ping_lib.bash ; exit 2; }


if [ -f /usr/local/etc/ssl/cacert-testforge.pem ] ; then
    CAFILE=/usr/local/etc/ssl/cacert-testforge.pem
else
    CERT=$( proxy_ping_update_cacert )
    [ "$?" -ne 0 -o -n "$CERT" ] && CAFILE=$CERT || \
	    CAFILE=/usr/local/etc/ssl/cacert-testforge.pem
fi

[ -z "$MODE" ] || MODE=`proxy_ping_mode`

if [ "$MODE" = tor ] ; then

    [ -z "PROXY_WLAN" ] && PROXY_WLAN=`proxy_get_if`
    [ -z "PROXY_WLAN" ] && { ERROR " error getting device $?" ; exit 3 ; }

    if ip route | grep ^def ; then
	PROXY_WLAN_IP=$( proxy_get_wlan_ip )
	[ -n "$PROXY_WLAN_IP" ] || { ERROR "no PROXY_WLAN_IP" ; exit 4 ; }
    fi
fi

usage() {
    echo "Usage: $0 [OPTIONS] dirs-or-files"
    echo
    echo " -B | --badssl                 - test badssl.org sites"
    echo " -D | --nodig                  - no dig sites"
    echo " -T | --tests                  - ping tests to run first"
    echo " -o | --outdir=/tmp            - output directory"
    echo " -v | --verbosity=$verbosity   - verbosity 0 least 5 most"
    echo
    echo " -V | --version                - print version of this script"
    echo " -h | --help                   - print this help"
}

SHORTOPTS="hVBDT:v:"
LONGOPTS="help,version:,badssl,nodig,tests:,verbosity:"
HOSTS=

ARGS=$(getopt --options $SHORTOPTS  --longoptions $LONGOPTS -- "$@")
[ $? != 0 ] &&  { ERROR "error parsing getopt" ; exit 5 ; }

eval set -- "$ARGS"

while true; do
    case "$1" in
	# -t --tests
        -o|--outdir)
            shift
            outdir="$1"
            ;;
        -v|--verbosity)
            shift
            verbosity="$1"
            ;;
        -B|--badssl)
	    badssl=1
	    ;;
        -D|--nodig)
	    nodig=1
	    ;;
        -T|--tests)
            shift
            tests="$1"
            ;;
        -V|--version)
            usage
            exit 0
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        '--')
            shift
            HOSTS="$*"
            break
            ;;
        *)
	    { ERROR "unrecognized arguments $*" ; exit 6 ; }
            break
            ;;
    esac
    shift
done

[ $badssl -ne 0 ] && HOSTS="${BADSSL_SITES[*]}"
[ -z "$HOSTS" ] && { ERROR "no arguments $*" ; exit 0 ; }
[ -d "$outdir" ] || mkdir -p "$outdir" || { ERROR "mkdir $outdir" ; exit 7 ; }
kill_time=$( expr $timeout + 10 )

[ -z "$tests" ] || \
for elt in $tests ; do
    /usr/local/bin/proxy_ping_test.bash $elt || exit 9$?
done

if which sslscan 2>/dev/null ; then
    sslscan='sslscan'
    SSLSCAN_ARGS="-4 --show-client-cas --show-certificate --bugs --timeout $timeout --tlsall --show-ciphers --no-colour --verbose"
else
    # no proxy support
    sslscan=''
fi
# sslscan --show-client-cas - no proxy

openssl=openssl
OPENSSL_ARGS="-4 -bugs -showcerts"

if [ -e /dev/tcp ] && which testssl.bash 2>/dev/null ; then
    testssl='testssl.bash'
    TESTSSL_ARGS="--connect-timeout $timeout --openssl-timeout $timeout --standard --vulnerable"
    #? --ssl-native
    TESTSSL_ARGS="$TESTSSL_ARGS --add-ca $CAFILE --assume-http --hints --color=0 --append"
else
    # no proxy support
    testssl=''
fi

if [ -f /usr/local/bin/scurl.bash ] ; then
    curl="/usr/local/bin/scurl.bash -- -s -S"
else
    curl='curl -s -S'
fi
CURL_ARGS="-vvv --cacert $CAFILE --cert-status --connect-timeout $timeout"

if [ -f $PREFIX/bin/analyze-ssl.pl.bash ] ; then
    analyze=$PREFIX/bin/analyze-ssl.pl.bash
    ANALYZE_ARGS="-v --timeout $timeout --CApath $CAFILE --all-ciphers"
else
    analyze=""
fi

warns=0
OUTR=$outdir/$prog-$NOW
if [ $nodig -eq 0 ] ; then
    for item in $HOSTS ; do
	i=0
	OUTRF=$OUTR.$item
	if [ $MODE = tor ] ; then
	    torresolve $item > $OUTRF.dig.out 2>&1
	    retval=$?
	    [ $retval -ne 0 ] && ERROR "torresolve $item $? - see $OUTRF.dig.out" && exit 1$?
	elif false ; then
            nslookup $item > $OUTRF.nslookup.out 2>&1
	#?[ $? -eq 0 ] || WARN "nslookup $item $? - see $OUTRF.nslookup.out"
	    #?grep NXDOMAIN "$OUTRF.nslookup.out" && WARN "nslookup $item NXDOMAIN - see $OUTRF.nslookup.out"
	else
	    dig $item > $OUTRF.dig.out 2>&1
	    [ $? -ne 0 ] && ERROR "dig $item $? - see $OUTRF.dig.out" && exit 1$?
	    grep 'ANSWER: 1' "$OUTRF.dig.out" || ERROR "dig $item no ANSWER - see $OUTRF.dig.out" && exit 2$?
	fi
    done
fi

# [ -r /etc/tor/torrc ]
# was https take precedence over socks
if [ -n "$socks_proxy" ] ; then
    SOCKS_HOST=$( echo $socks_proxy|sed  -e 's/.*@//' -e 's@/@@g' -e 's/:/ /g' -e 's/socks5* //'  -e 's/ .*//' )
    SOCKS_PORT=$( echo $socks_proxy|sed -e 's@/@@g' -e 's/:/ /g' -e 's/.* //' )

    openssl='torsocks openssl'

    #  --interface lo --dns-interface lo
    [ -n "$analyze" ] && analyze="torsocks $analyze"
    [ -n "$testssl" ] && testssl="torsocks $testssl"
    [ -n "$sslscan" ] && sslscan="torsocks $sslscan"
    if [  $MODE = tor -o $MODE = selektor ] ; then
	sudo grep -q "SocksPolicy *accept *$PROXY_WLAN_IP" /etc/tor/torrc || \
	    { WARN "need SocksPolicy accept $PROXY_WLAN_IP in /etc/tor/torrc" ; }
    fi
    CURL_ARGS="$CURL_ARGS --proxy $socks_proxy "
    if [ ${HTTPS_HOST} = 127.0.0.1 ] ;  then
	CURL_ARGS="$CURL_ARGS --interface"
    fi
    if netstat -nle4 | grep 127.0.1:53 ; then
	CURL_ARGS="$CURL_ARGS --dns-interface lo"
      fi
  elif [ -n "$https_proxy" ] ; then
    HTTPS_HOST=$( echo $https_proxy|sed -e 's@/@@g' -e 's/:/ /g' -e 's/https* //' -e 's/ .*//' )
    HTTPS_PORT=$( echo $https_proxy|sed -e 's@/@@g' -e 's/:/ /g' -e 's/.* //' )

    OPENSSL_ARGS="$OPENSSL_ARGS -proxy ${HTTPS_HOST}:$HTTPS_PORT"
    TESTSSL_ARGS="$TESTSSL_ARGS --proxy=auto"
    CURL_ARGS="$CURL_ARGS --proxy http://${HTTPS_HOST}:$HTTPS_PORT"
    ANALYZE_ARGS="$ANALYZE_ARGS --starttls http_proxy:${HTTPS_HOST}:$HTTPS_PORT"
    [ -n "$testssl" ] && testssl="torsocks $testssl"
    [ -n "$sslscan" ] && sslscan="torsocks $sslscan"


else
    : direct
fi

TENVS="DNS_VIA_PROXY=true"
errs=0
for CAFILE in /etc/ssl/certs/ca-certificates.crt /usr/local/etc/ssl/cacert-testforge.pem ; do
    [ -f $CAFILE ] || { ERROR "CAfile not found $CAFILE" ; exit 8; }
    for item in $HOSTS ; do
	i=0
	OUTRF=$OUTR.$item

	if [ -n "$openssl" ] ; then
	    INFO "$openssl s_client -connect ${item}:443 -servername $item $OPENSSL_ARGS --CAfile $CAFILE"
	    echo $openssl s_client \
		 -connect ${item}:443 -servername $item $OPENSSL_ARGS  --CAfile $CAFILE \
		 < /dev/null > $OUTRF.s_client.out
	    timeout ${kill_time}s \
	      $openssl s_client \
	      -connect ${item}:443 -servername $item $OPENSSL_ARGS  --CAfile $CAFILE \
	      < /dev/null >> $OUTRF.s_client.out 2>&1
	    # :error:\|
	    if [ $? -eq 124 ] ; then
		echo "DEBUG: timeout openssl s_client failed $? see $OUTRF.s_client.out"
	    elif [ $? -ne 0 ] ; then
		WARN "openssl s_client failed $? see $OUTRF.s_client.out"
		i=$( expr $i + 1 )
	    elif str='unable to get local issuer certificate' && \
	    	 grep "$str" $OUTRF.s_client.out; then
		WARN "openssl s_client failed - $str see $OUTRF.s_client.out"
		i=$( expr $i + 1 )
	    elif str='Cipher is (NONE)' && \
	    	 grep "$str" $OUTRF.s_client.out; then
		WARN "openssl s_client failed - $str see $OUTRF.s_client.out"
		i=$( expr $i + 1 )
	    elif str='SSL handshake has read 0 bytes' && \
	    	 grep "$str" $OUTRF.s_client.out; then
		WARN "openssl s_client failed - $str see $OUTRF.s_client.out"
		i=$( expr $i + 1 )
	    else
		echo "DEBUG: openssl s_client -showcerts $OPENSSL_ARGS --CAfile $CAFILE $item"
		timeout ${kill_time}s \
		  $openssl s_client -showcerts \
		  -connect ${item}:443 -servername $item $OPENSSL_ARGS --CAfile $CAFILE < /dev/null \
		  > $OUTRF.s_client.certs 2>&1
	    fi
	fi

	if [ -n "$sslscan" ] ; then
	    echo $sslscan $SSLSCAN_ARGS  --certs $CAFILE --sni-name $item $item > $OUTRF.sslscan.out
	    timeout ${kill_time}s \
	      $sslscan $SSLSCAN_ARGS --sni-name $item $item >> $OUTRF.sslscan.out 2>&1
	    if [ $? -eq 124 ] ; then
		echo "DEBUG: timeout sslscan failed $? see $OUTRF.sslscan.out "
	    elif [ $? -ne 0 ] ; then
		WARN "sslscan failed $? see $OUTRF.sslscan.out "
		i=$( expr $i + 1 )
	    elif grep 'SSL Certificate' $OUTRF.sslscan.out ; then
		WARN "sslscan failed see $OUTRF.sslscan.out "
		i=$( expr $i + 1 )
	    elif grep 'Connection failed' $OUTRF.sslscan.out ; then
		# tail -1 $OUTRF.sslscan.out | grep 'Supported Server Cipher'
		WARN "sslscan failed see $OUTRF.sslscan.out "
		i=$( expr $i + 1 )
	    else
		INFO "$sslscan $SSLSCAN_ARGS $item"
	    fi
	fi

	if [ -n "$testssl" -a -n "$https_proxy" ] ; then
	    # rDNS (140.82.114.3):    lb-140-82-114-3-iad.github.com.testssl.sh: line 10330: /dev/tcp/140.82.114.3/443: No such file or directory
	    echo $TENVS $testssl $TESTSSL_ARGS $item >  $OUTRF.testssl.out
	    env  $TENVS $testssl $TESTSSL_ARGS $item >> $OUTRF.testssl.out 2>&1
	    if [ $? -ne 0 ] ; then
		WARN "testssl failed $? see $OUTRF.testssl.out"
		i=$( expr $i + 1 )
	    elif grep ': unable to\| error:\|doesn.t seem to be a TLS/SSL enabled server' $OUTRF.testssl.out; then
		WARN "testssl failure see $OUTRF.testssl.out"
		i=$( expr $i + 1 )
	    else
		INFO "$testssl $TESTSSL_ARGS $item"
		echo "DEBUG: $testssl $TESTSSL_ARGS --jsonfile-pretty  $OUTRF.testssl.json $item"
		env $TENVS $testssl $TESTSSL_ARGS --jsonfile-pretty  $OUTRF.testssl.json $item > $OUTRF.testssl-json.out 2>&1
	    fi
	fi

	if [ -n "$curl" ] ; then
	    DBUG $TENVS $curl $CURL_ARGS -o /dev/null https://$item/
	    env $TENVS $curl $CURL_ARGS -o /dev/null https://$item/ > $OUTRF.curl-vvv.out 2>&1
	    if [ $? -eq 0 ] ; then
		grep 'SSL certificate problem:' $OUTRF.curl-vvv.out && \
		    { WARN "curl -vvv failed $? see $OUTRF.curl-vvv.out" ;
			i=$( expr $i + 1 ) ; } || \
		    INFO $curl $CURL_ARGS $item
	    else
		INFO $curl $CURL_ARGS $item
	    fi
	fi

	if [ -n "$analyze" ] ; then
	    timeout ${kill_time}s \
	      $analyze $ANALYZE_ARGS --name $item ${item}:443 \
		    > $OUTRF.analyze-ssl.out 2>&1
	    # certificate verified : ok
	    if [ $? -eq 124 ] ; then
		echo "DEBUG: timeout $analyze $ANALYZE_ARGS $item"
	    elif [ $? -eq 0 ] ; then
		INFO "$analyze $ANALYZE_ARGS $item"
	    else
		WARN "$analyze failed $? see $OUTRF.analyze-ssl.out"
		i=$( expr $i + 1 )
	    fi
	fi

	[ $i -eq 0 ] && continue
	WARN "$i failures for $item"
	errs=$(expr $errs + $i )
	DBUG $OUTRF.*.out
    done
done

find $OUTRF.* -type f -empty -delete

exit $errs
