#!/usr/bin/env bash

set -o pipefail
export LC_ALL=C

# COLOURS
# standard terminal output hues
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[1;33m'
cyan='\033[0;36m'
nc='\033[0m'

# CONFIGURATION
# defaults to mullvad dns for privacy-respecting resolution
resolver="@194.242.2.2"
timeout_val=2
ua="mozilla/5.0 (x11; linux x86_64) applewebkit/537.36"

# USAGE GUIDE
# displays instructions if arguments are missing
usage() {
    printf "${cyan}usage:${nc} reconctl [-t timeout] <domain>\n"
    printf "  -t    set network timeout in seconds (default: 2)\n"
    printf "  -h    show help menu\n\n"
    exit 1
}

while getopts "t:h" opt; do
    case ${opt} in
        t) timeout_val=$OPTARG ;;
        h) usage ;;
        *) usage ;;
    esac
done
shift $((OPTIND - 1))

domain="$1"
[[ -z "$domain" ]] && usage

# INPUT VALIDATION
# rejects malformed hostnames before they reach dig/curl/openssl/whois
if [[ ! "$domain" =~ ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]]; then
    printf "${red}error: '%s' is not a valid domain${nc}\n" "$domain"
    exit 1
fi

# HELPER FUNCTIONS
# standardises section headers for clean output
section_header() {
    printf "\n${cyan}%s${nc}\n" "$1"
}

# DEPENDENCY AUDIT
# validates required packages before execution
for cmd in dig curl jq openssl whois awk; do
    if ! command -v "$cmd" >/dev/null 2>&1; then
        printf "${red}error: missing dependency '%s'${nc}\n" "$cmd"
        exit 1
    fi
done

# DNS ANALYSIS
# extracts primary records and security configurations
section_header "DNS RECORDS"
ipv4=$(dig $resolver +short A "$domain")
ipv6=$(dig $resolver +short AAAA "$domain")
cname=$(dig $resolver +short CNAME "$domain")
mx=$(dig $resolver +short MX "$domain")
caa=$(dig $resolver +short CAA "$domain")
txt=$(dig $resolver +short TXT "$domain" | grep -Ei "spf|dmarc" || true)
dnskey=$(dig $resolver +short DNSKEY "$domain")

printf "  ipv4:\n%s\n" "$([[ -n "$ipv4" ]] && echo "$ipv4" | sed 's/^/    /' || echo "    n/a")"
printf "  ipv6:\n%s\n" "$([[ -n "$ipv6" ]] && echo "$ipv6" | sed 's/^/    /' || echo "    n/a")"
printf "  cname:\n    %s\n" "${cname:-none detected}"
printf "  nameservers:\n%s\n" "$(dig $resolver +short NS "$domain" | sed 's/^/    /')"
printf "  mail exchangers:\n%s\n" "$([[ -n "$mx" ]] && echo "$mx" | sed 's/^/    /' || echo "    none detected")"
printf "  caa records:\n%s\n" "$([[ -n "$caa" ]] && echo "$caa" | sed 's/^/    /' || echo "    none detected")"
printf "  security txt:\n%s\n" "$([[ -n "$txt" ]] && echo "$txt" | sed 's/^/    /' || echo "    none detected")"
printf "  dnssec:        %s\n" "$([[ -n "$dnskey" ]] && echo "enabled" || echo "not detected")"

# REVERSE LOOKUP
# checks ptr records for resolved v4 and v6 addresses
section_header "REVERSE DNS"
if [[ -n "$ipv4" || -n "$ipv6" ]]; then
    for ip in $ipv4 $ipv6; do
        ptr=$(dig $resolver +short -x "$ip")
        printf "  %s -> %s\n" "$ip" "${ptr:-n/a}"
    done
else
    printf "  n/a\n"
fi

# HTTP PERFORMANCE
# tests routing responses and redirection chains
section_header "HTTP ROUTING"
http_status=$(timeout "$timeout_val" curl -A "$ua" -sL -o /dev/null -w "%{http_code}" "http://$domain" || echo "timeout")
https_status=$(timeout "$timeout_val" curl -A "$ua" -sL -o /dev/null -w "%{http_code}" "https://$domain" 2>/dev/null || echo "timeout")
redirects=$(timeout "$timeout_val" curl -A "$ua" -sI -L "http://$domain" 2>/dev/null | grep -i "^location:" | sed 's/[Ll]ocation: //g' | tr -d '\r')

printf "  http status:   %s\n" "$http_status"
printf "  https status:  %s\n" "$https_status"
printf "  redirects:\n%s\n" "$([[ -n "$redirects" ]] && echo "$redirects" | sed 's/^/    -> /' || echo "    none")"

# ENCRYPTION AUDIT
# pulls subject, issuer, and expiration from the tls certificate
section_header "TLS CERTIFICATE"
tls=$(timeout "$timeout_val" openssl s_client -connect "$domain:443" -servername "$domain" -showcerts </dev/null 2>/dev/null)
expiry=$(echo "$tls" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
subject_issuer=$(echo "$tls" | grep -E "subject=|issuer=" | sed 's/^[ \t]*//')
printf "  subject/issuer:\n%s\n" "$([[ -n "$subject_issuer" ]] && echo "$subject_issuer" | sed 's/^/    /' || echo "    unknown")"
printf "  expiry date:   %s\n" "${expiry:-unknown}"

# SECURITY HEADERS
# evaluates presence of critical web security directives
section_header "SECURITY POSTURE"
headers=$(timeout "$timeout_val" curl -A "$ua" -sI "https://$domain" 2>/dev/null | tr -d '\r')
for h in "strict-transport-security" "content-security-policy" "x-frame-options" "x-content-type-options"; do
    val=$(echo "$headers" | grep -i "^$h:" | cut -d: -f2- | xargs)
    if [[ -n "$val" ]]; then
        printf "  %-25s ${green}present${nc}\n" "$h"
    else
        printf "  %-25s ${yellow}missing${nc}\n" "$h"
    fi
done

# INFRASTRUCTURE PROBE
# checks common critical ports on the primary resolved ip, results sorted after concurrent scan
section_header "PORT SCAN"
ports=(21 22 25 53 80 443 3306 8080 8443)
target_ip=$(echo "$ipv4" | head -n1)

if [[ -n "$target_ip" ]]; then
    scan_tmp=$(mktemp)
    exec 3>&2
    exec 2>/dev/null
    for port in "${ports[@]}"; do
        (timeout 1 bash -c "</dev/tcp/$target_ip/$port" &>/dev/null && echo "$port" >> "$scan_tmp") &
    done
    wait
    exec 2>&3
    if [[ -s "$scan_tmp" ]]; then
        sort -n "$scan_tmp" | while read -r port; do
            printf "  port %-5s ${green}open${nc}\n" "$port"
        done
    else
        printf "  no open ports detected\n"
    fi
    rm -f "$scan_tmp"
else
    printf "  ${red}no ipv4 address for scanning.${nc}\n"
fi

# ASSET ENUMERATION
# queries certificate transparency logs for child infrastructure via crt.sh json endpoint
section_header "SUBDOMAINS"
subdomains=$(timeout 15 curl -s "https://crt.sh/?q=%25.$domain&output=json" | jq -r '.[].name_value' 2>/dev/null | tr ',' '\n' | sed 's/\*\.//' | sort -u | head -n 15)
[[ -n "$subdomains" ]] && echo "$subdomains" | sed 's/^/  /' || echo "  enumeration failed or none found"

# REGISTRY AUDIT
# extracts baseline domain registration metadata
section_header "WHOIS DATA"
whois_data=$(timeout "$timeout_val" whois "$domain" 2>/dev/null | grep -Ei "^registrar:|^creation date:|^updated date:" | tr -s ' ' | head -n 5)
[[ -n "$whois_data" ]] && echo "$whois_data" | sed 's/^/  /' || echo "  extraction limited"

# RISK SCORE
# calculates a basic hazard rating based on missing protections and open ports
section_header "RISK SUMMARY"
risk=0
[[ ! "$https_status" =~ ^(2|3) ]] && ((risk += 2))
echo "$headers" | grep -qi "^strict-transport-security:" || ((risk += 1))
echo "$headers" | grep -qi "^content-security-policy:" || ((risk += 1))
[[ -z "$dnskey" ]] && ((risk += 1))
timeout 1 bash -c "</dev/tcp/$target_ip/22" &>/dev/null && ((risk += 1))
timeout 1 bash -c "</dev/tcp/$target_ip/21" &>/dev/null && ((risk += 2))

if [ $risk -le 1 ]; then
    posture_color=$green
    posture_text="low risk"
else
    posture_color=$red
    posture_text="risk identified"
fi

printf "  domain:  %s\n" "$domain"
printf "  ip:      %s\n" "${target_ip:-n/a}"
printf "  posture: ${posture_color}%s${nc} (score: %d)\n\n" "$posture_text" "$risk"

