#!/bin/sh
# Freezer router guard v1. Read-only. One JSON object per line. BusyBox only.
# A missing command omits that field. Unknown is not reported as a failure.
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
LC_ALL=C
export LC_ALL

if [ "${1:-}" != guard ] || [ "$#" -ne 1 ]; then
    printf '%s\n' 'Usage: freezer-router-guard guard' >&2
    exit 64
fi

openwrt=0
if [ -f /etc/openwrt_release ]; then
    openwrt=1
fi
if [ -r /etc/os-release ]; then
    release_id=$(sed -n 's/^ID=//p' /etc/os-release | head -n 1 | tr -d '"')
    case "$release_id" in
        openwrt|immortalwrt|qwrt|istoreos|lede) openwrt=1 ;;
    esac
fi
if [ "$openwrt" != 1 ]; then
    printf '%s\n' '{"v":1,"type":"unsupported","reason":"not-openwrt"}'
    exit 0
fi

if command -v ubus >/dev/null 2>&1; then
    ubus listen 2>/dev/null | while IFS= read -r _; do
        printf '%s\n' '{"v":1,"type":"notice","source":"ubus"}'
    done &
fi
if command -v logread >/dev/null 2>&1; then
    logread -f 2>/dev/null | while IFS= read -r line; do
        case "$line" in
            *[Cc]lash*|*[Mm]ihomo*|*sing-box*|*xray*|*firewall*|*dnsmasq*|*passwall*)
                printf '%s\n' '{"v":1,"type":"notice","source":"log"}' ;;
        esac
    done &
fi
trap 'kill $(jobs -p) 2>/dev/null' EXIT INT TERM

flag() {
    if [ "$1" = 1 ]; then printf '1'; else printf '0'; fi
}

while :; do
    boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null | tr -d '\n')
    uptime=$(awk '{print $1; exit}' /proc/uptime 2>/dev/null)
    memory=$(awk '/^MemAvailable:/ {print $2; exit}' /proc/meminfo 2>/dev/null)
    if [ -z "$memory" ]; then
        memory=$(awk '/^MemFree:/ {print $2; exit}' /proc/meminfo 2>/dev/null)
    fi
    overlay=""
    if [ -d /overlay ] && command -v df >/dev/null 2>&1; then
        overlay=$(df -k /overlay 2>/dev/null | awk 'NR==2 {print $4; exit}')
    fi
    load=$(awk '{print $1; exit}' /proc/loadavg 2>/dev/null)

    proxy=""
    if command -v pidof >/dev/null 2>&1; then
        proxy=0
        for process_name in mihomo clash clash_meta clash_tun xray sing-box v2ray sslocal ssr-local; do
            if pidof "$process_name" >/dev/null 2>&1; then
                proxy=1
                break
            fi
        done
    fi

    tunnel=""
    if command -v ip >/dev/null 2>&1; then
        tunnel=0
        if ip link show 2>/dev/null | awk '
            $1 ~ /^[0-9]+:$/ {
                name = $2
                sub(":$", "", name)
                if ($0 ~ /state UP/ && name ~ /^(tun[0-9]+|utun[0-9]*|wg[0-9]+)$/) found = 1
            }
            END { exit found ? 0 : 1 }
        '; then
            tunnel=1
        fi
    fi

    redirect=""
    if command -v iptables >/dev/null 2>&1 || command -v nft >/dev/null 2>&1; then
        redirect=0
        if command -v iptables >/dev/null 2>&1; then
            if iptables -t nat -S 2>/dev/null | grep -E -q -i 'TPROXY|clash|mihomo|passwall|sing-box|openclash|REDIRECT --to-ports (7874|7890|7891|7892|7893|7895)'; then
                redirect=1
            fi
            if iptables -t mangle -S 2>/dev/null | grep -E -q -i 'TPROXY|clash|mihomo|openclash'; then
                redirect=1
            fi
        fi
        if command -v nft >/dev/null 2>&1; then
            if nft list ruleset 2>/dev/null | grep -E -q -i 'tproxy|redirect to'; then
                redirect=1
            fi
        fi
    fi

    ipv6=""
    dns=""
    direct=""
    if [ "$proxy" = 1 ]; then
        if command -v ip >/dev/null 2>&1; then
            if ip -6 route show default 2>/dev/null | grep -q '[^[:space:]]'; then
                ipv6=1
                if command -v ip6tables >/dev/null 2>&1; then
                    if ip6tables -t nat -S 2>/dev/null | grep -E -q -i 'TPROXY|REDIRECT'; then
                        ipv6=0
                    fi
                    if ip6tables -t mangle -S 2>/dev/null | grep -E -q -i 'TPROXY'; then
                        ipv6=0
                    fi
                fi
            else
                ipv6=0
            fi
        fi
        checked_dns=0
        proxied_dns=0
        if command -v uci >/dev/null 2>&1; then
            checked_dns=1
            if uci -q get 'dhcp.@dnsmasq[0].server' 2>/dev/null | grep -E -q '127\.0\.0\.1|::1|#'; then
                proxied_dns=1
            fi
        fi
        if command -v iptables >/dev/null 2>&1; then
            checked_dns=1
            if iptables -t nat -S 2>/dev/null | grep -E -q -- '--dport 53'; then
                proxied_dns=1
            fi
        fi
        if [ "$checked_dns" = 1 ]; then
            dns=$proxied_dns
        fi
        if [ -n "$tunnel" ] && [ -n "$redirect" ]; then
            if [ "$tunnel" = 0 ] && [ "$redirect" = 0 ]; then
                direct=1
            else
                direct=0
            fi
        fi
    fi

    printf '{'
    printf '"v":1,"type":"sample"'
    case "$boot" in
        [0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])
            printf ',"boot":"%s"' "$boot" ;;
    esac
    case "$uptime" in
        [0-9]*|[0-9]*.[0-9]*) printf ',"uptime":%s' "$uptime" ;;
    esac
    case "$memory" in
        [0-9]*) printf ',"mem_kib":%s' "$memory" ;;
    esac
    case "$overlay" in
        [0-9]*) printf ',"overlay_kib":%s' "$overlay" ;;
    esac
    case "$load" in
        [0-9]*|[0-9]*.[0-9]*) printf ',"load1":%s' "$load" ;;
    esac
    if [ -n "$proxy" ]; then printf ',"proxy":%s' "$(flag "$proxy")"; fi
    if [ -n "$tunnel" ]; then printf ',"tunnel":%s' "$(flag "$tunnel")"; fi
    if [ -n "$redirect" ]; then printf ',"redirect":%s' "$(flag "$redirect")"; fi
    if [ -n "$ipv6" ]; then printf ',"ipv6":%s' "$(flag "$ipv6")"; fi
    if [ -n "$dns" ]; then printf ',"dns":%s' "$(flag "$dns")"; fi
    if [ -n "$direct" ]; then printf ',"direct":%s' "$(flag "$direct")"; fi
    printf '}\n'
    sleep 5
done
