#!/bin/bash
################################################################################
# 端口服务检测模块
# 功能: 检测关键服务端口的监听状态
# 作者: Claude Code
# 日期: 2026-05-09
################################################################################

# 获取脚本所在目录并加载依赖
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="${LIB_DIR:-/tmp/check_modules}"

# 加载配置文件和通用函数库
if [ -f "$LIB_DIR/lib/config.sh" ]; then
    source "$LIB_DIR/lib/config.sh"
else
    echo "ERROR: 配置文件不存在: $LIB_DIR/lib/config.sh"
    exit 1
fi

if [ -f "$LIB_DIR/lib/common.sh" ]; then
    source "$LIB_DIR/lib/common.sh"
else
    echo "ERROR: 通用函数库不存在: $LIB_DIR/lib/common.sh"
    exit 1
fi

# ==================== 端口配置 ====================
# 端口格式: "名称:端口:关键标志"
declare -a PORT_CONFIG=(
    "SSH:22:1"
    "MySQL:8306:1"
    "Redis:6379:1"
    "EMQX_MQTT:1883:1"
    "EMQX_SSL:8883:0"
    "EMQX_WS:8083:0"
    "Java_Web:8080:1"
    "HTTPS:443:1"
    "Java_Admin:8999:1"
    "Python:8000:0"
    "FastDFS_Tracker:22122:0"
    "FastDFS_Storage:23000:0"
    "HTTP:80:0"
    "MySQL_Default:3306:0"
    "Python_Web:8081:0"
)

# ==================== 检测函数 ====================

# 检测单个端口状态
check_single_port() {
    local port_name=$1
    local port_num=$2
    local is_critical=$3

    local is_listening=0
    local listen_address=""
    local process_name=""

    # 使用 ss 命令检测
    if command -v ss &> /dev/null; then
        local port_info
        port_info=$(ss -tlnp 2>/dev/null | grep ":$port_num " | grep LISTEN)

        if [ -n "$port_info" ]; then
            is_listening=1
            listen_address=$(echo "$port_info" | awk '{print $4}' | head -1)

            # 尝试获取进程名
            process_name=$(echo "$port_info" | grep -oP 'pid=\K[0-9]+.*' | head -1)
        fi
    # 备用方案：使用 netstat
    elif command -v netstat &> /dev/null; then
        local port_info
        port_info=$(netstat -tln 2>/dev/null | grep ":$port_num " | grep LISTEN)

        if [ -n "$port_info" ]; then
            is_listening=1
            listen_address=$(echo "$port_info" | awk '{print $4}' | head -1)
        fi
    # 最后备用方案：直接检查 /proc/net/tcp
    elif [ -f /proc/net/tcp ]; then
        # 将端口号转换为16进制
        local port_hex=$(printf "%04X" $port_num)
        if grep -q ":$port_hex " /proc/net/tcp 2>/dev/null; then
            is_listening=1
        fi
    fi

    # 确定状态级别
    local status_level
    if [ $is_listening -eq 1 ]; then
        status_level="正常"
    else
        if [ $is_critical -eq 1 ]; then
            status_level="严重"
        else
            status_level="警告"
        fi
    fi

    # 输出结果
    local key_name="PORT_${port_name}"
    output_result "${key_name}_NUMBER" "$port_num"
    output_result "${key_name}_STATUS" "$status_level"

    if [ $is_listening -eq 1 ]; then
        output_result "${key_name}_LISTENING" "是"
        if [ -n "$listen_address" ]; then
            output_result "${key_name}_ADDRESS" "$listen_address"
        fi
    else
        output_result "${key_name}_LISTENING" "否"
        echo "ERROR:端口${port_name}(${port_num})未监听"
    fi
}

# 检测所有配置的端口
check_all_ports() {
    local total_ports=${#PORT_CONFIG[@]}
    local listening_count=0
    local critical_down=0

    for port_info in "${PORT_CONFIG[@]}"; do
        IFS=':' read -r name port critical <<< "$port_info"

        check_single_port "$name" "$port" "$critical"

        # 统计监听中的端口
        local key_name="PORT_${name}_LISTENING"
        # 重新检查端口状态用于统计（简化版）
        if command -v ss &> /dev/null; then
            if ss -tlnp 2>/dev/null | grep -q ":$port "; then
                listening_count=$((listening_count + 1))
            else
                if [ "$critical" -eq 1 ]; then
                    critical_down=$((critical_down + 1))
                fi
            fi
        fi
    done

    output_result "PORT_TOTAL" "$total_ports"
    output_result "PORT_LISTENING" "$listening_count"
    output_result "PORT_DOWN" "$((total_ports - listening_count))"

    if [ $critical_down -gt 0 ]; then
        output_result "PORT_CRITICAL_DOWN" "$critical_down"
        output_result "PORT_OVERALL_STATUS" "严重"
    elif [ $listening_count -lt $((total_ports / 2)) ]; then
        output_result "PORT_OVERALL_STATUS" "警告"
    else
        output_result "PORT_OVERALL_STATUS" "正常"
    fi
}

# 检测端口占用情况
check_port_usage() {
    local total_listening=0
    local total_ports=65535

    if command -v ss &> /dev/null; then
        total_listening=$(ss -tln 2>/dev/null | grep -c LISTEN || echo "0")
    fi

    output_result "PORT_LISTENING_COUNT" "$total_listening"

    if [ "$total_listening" -gt 100 ]; then
        output_result "PORT_USAGE_LEVEL" "警告"
    else
        output_result "PORT_USAGE_LEVEL" "正常"
    fi
}

# 检测特权端口（<1024）
check_privileged_ports() {
    local priv_count=0

    if command -v ss &> /dev/null; then
        priv_count=$(ss -tlnp 2>/dev/null | awk '{print $4}' | grep -oE ':[0-9]+' | sed 's/://' | awk '$1 < 1024' | wc -l)
    fi

    output_result "PRIVILEGED_PORTS" "$priv_count"
}

# 检测端口复用情况
check_port_reuse() {
    local reuse_count=0

    if command -v ss &> /dev/null; then
        # 检查SO_REUSEADDR端口
        reuse_count=$(ss -tlnp 2>/dev/null | grep -c "0.0.0.0:" || echo "0")
    fi

    output_result "PORT_REUSE_COUNT" "$reuse_count"
}

# 检测IPv6端口监听
check_ipv6_ports() {
    local ipv6_count=0

    if command -v ss &> /dev/null; then
        ipv6_count=$(ss -tln6 2>/dev/null | grep -c LISTEN || echo "0")
    fi

    output_result "IPV6_PORTS" "$ipv6_count"
}

# ==================== 主检测流程 ====================
main() {
    log_info "开始端口服务检测..."

    # 执行各项检测
    check_all_ports
    check_port_usage
    check_privileged_ports
    check_port_reuse
    check_ipv6_ports

    log_info "端口服务检测完成"
}

# 执行主函数
main
