#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

#=================================================
#       System Required: CentOS/Debian/Ubuntu
#       Description: iptables Global Port forwarding (V4.0)
#       Version: 4.0.0
#       Author: Refactored by AI
#=================================================
sh_ver="4.0.0"

Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Font_color_suffix="\033[0m"
Info="${Green_font_prefix}[信息]${Font_color_suffix}"
Error="${Red_font_prefix}[错误]${Font_color_suffix}"

# --- 防呆校验函数 ---
check_ip() {
    local ip=$1
    if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
        return 0
    else
        return 1
    fi
}

check_port() {
    local port=$1
    if [[ $port =~ ^[0-9]+$ ]]; then
        if [ "$port" -ge 1 ] && [ "$port" -le 65535 ]; then return 0; else return 1; fi
    elif [[ $port =~ ^[0-9]+-[0-9]+$ ]]; then
        local p1=$(echo $port | cut -d'-' -f1)
        local p2=$(echo $port | cut -d'-' -f2)
        if [ "$p1" -ge 1 ] && [ "$p1" -le 65535 ] && [ "$p2" -ge 1 ] && [ "$p2" -le 65535 ] && [ "$p1" -lt "$p2" ]; then 
            return 0
        else 
            return 1
        fi
    else
        return 1
    fi
}
# --------------------

check_sys(){
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        release=$ID
    elif [[ -f /usr/lib/os-release ]]; then
        source /usr/lib/os-release
        release=$ID
    else
        echo -e "${Error} 不支持的操作系统，请使用 CentOS/Ubuntu/Debian" && exit 1
    fi
    if [[ "${release}" == "centos" || "${release}" == "rhel" || "${release}" == "fedora" || "${release}" == "almalinux" || "${release}" == "rocky" ]]; then
        release="centos"
        pkg_manager="yum"
    elif [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        pkg_manager="apt-get"
    else
        echo -e "${Error} 未知的操作系统发行版" && exit 1
    fi
}

check_iptables(){
    if ! command -v iptables &> /dev/null; then
        echo -e "${Error} 没有安装 iptables，请先在菜单中选择安装 !" && exit 1
    fi
}

install_iptables(){
    echo -e "${Info} 开始安装和配置 iptables..."
    if [[ ${release} == "centos" ]]; then
        ${pkg_manager} update -y
        ${pkg_manager} install -y iptables iptables-services curl
        systemctl enable iptables
        systemctl start iptables
    else
        ${pkg_manager} update -y
        echo iptables-persistent iptables-persistent/autosave_v4 boolean true | debconf-set-selections
        echo iptables-persistent iptables-persistent/autosave_v6 boolean true | debconf-set-selections
        ${pkg_manager} install -y iptables iptables-persistent curl
        systemctl enable netfilter-persistent
        systemctl start netfilter-persistent
    fi
    
    echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-iptables-forward.conf
    sysctl -p /etc/sysctl.d/99-iptables-forward.conf
    echo -e "${Info} iptables 安装和内核配置完毕 !"
}

Save_iptables(){
    if [[ ${release} == "centos" ]]; then
        service iptables save
    else
        if command -v netfilter-persistent &> /dev/null; then
            netfilter-persistent save >/dev/null 2>&1
        else
            mkdir -p /etc/iptables
            iptables-save > /etc/iptables/rules.v4
            ip6tables-save > /etc/iptables/rules.v6
        fi
    fi
    echo -e "${Info} iptables 规则已保存，重启依然生效 !"
}

View_forwarding(){
    check_iptables
    # 获取所有包含 DNAT 目标的规则（适用于全局）
    mapfile -t rule_list < <(iptables -t nat -S PREROUTING | grep "DNAT")
    
    if [[ ${#rule_list[@]} -eq 0 ]]; then
        echo -e "${Error} 当前系统内未发现任何 DNAT 端口转发规则 !\n"
        return 1
    fi
    
    echo -e "\n====================================== 全局 DNAT 转发规则 ======================================"
    printf " %-6s | %-8s | %-16s | %-25s\n" "序号" "协议" "本地监听端口" "目标 IP:端口"
    echo -e "------------------------------------------------------------------------------------------------"
    
    local i=1
    for rule in "${rule_list[@]}"; do
        local proto=$(echo "$rule" | grep -oP '(?<=-p )\w+')
        local dport=$(echo "$rule" | grep -oP '(?<=--dport )\S+')
        local to_dest=$(echo "$rule" | grep -oP '(?<=--to-destination )\S+')
        
        [[ -z "$proto" ]] && proto="ALL"
        [[ -z "$dport" ]] && dport="ALL"
        proto=${proto^^}
        
        printf " [%-4d] | %-8s | %-16s | %-25s\n" "$i" "$proto" "$dport" "$to_dest"
        ((i++))
    done
    echo -e "================================================================================================\n"
    return 0
}

Add_forwarding_core(){
    local type=$1
    local local_p=$2
    local remote_i=$3
    local remote_p=$4
    local local_i=$5
    
    local local_p_colon=$(echo ${local_p} | sed 's/-/:/g')
    local remote_p_colon=$(echo ${remote_p} | sed 's/-/:/g')
    
    if [[ "$type" == "TCP" || "$type" == "TCP+UDP" ]]; then
        iptables -t nat -A PREROUTING -p tcp --dport ${local_p_colon} -j DNAT --to-destination ${remote_i}:${remote_p_colon}
        iptables -t nat -A POSTROUTING -p tcp -d ${remote_i} --dport ${remote_p_colon} -j SNAT --to-source ${local_i}
        iptables -I INPUT -p tcp --dport ${local_p_colon} -j ACCEPT
    fi
    if [[ "$type" == "UDP" || "$type" == "TCP+UDP" ]]; then
        iptables -t nat -A PREROUTING -p udp --dport ${local_p_colon} -j DNAT --to-destination ${remote_i}:${remote_p_colon}
        iptables -t nat -A POSTROUTING -p udp -d ${remote_i} --dport ${remote_p_colon} -j SNAT --to-source ${local_i}
        iptables -I INPUT -p udp --dport ${local_p_colon} -j ACCEPT
    fi
}

Add_forwarding(){
    check_iptables
    
    while true; do
        read -e -p "1. 请输入欲转发到的 远程IP(目标服务器IP): " forwarding_ip
        [[ -z "${forwarding_ip}" ]] && echo "取消..." && exit 1
        if check_ip "${forwarding_ip}"; then break; else echo -e "${Error} IP 格式错误，请重新输入真实的 IPv4 地址!"; fi
    done
    
    while true; do
        read -e -p "2. 请输入欲转发到的 远程端口 [1-65535] (支持段: 1000-2000): " forwarding_port
        [[ -z "${forwarding_port}" ]] && echo "取消..." && exit 1
        if check_port "${forwarding_port}"; then break; else echo -e "${Error} 端口格式错误，请输入 1-65535 的数字或规范的端口段!"; fi
    done
    
    while true; do
        read -e -p "3. 请输入本地监听端口 [1-65535] (默认与远程端口一致: ${forwarding_port}): " local_port
        [[ -z "${local_port}" ]] && local_port="${forwarding_port}"
        if check_port "${local_port}"; then break; else echo -e "${Error} 端口格式错误，请输入 1-65535 的数字或规范的端口段!"; fi
    done
    
    while true; do
        read -e -p "4. 请输入本服务器的外网网卡IP (留空自动检测): " local_ip
        if [[ -z "${local_ip}" ]]; then
            local_ip=$(curl -s4 ip.sb || curl -s4 ipinfo.io/ip)
            if [[ -z "${local_ip}" ]] || ! check_ip "${local_ip}"; then
                echo -e "${Error} 无法检测外网IP，请手动输入!"
                continue
            fi
            echo -e "${Info} 自动检测到本机IP: ${local_ip}"
            break
        else
            if check_ip "${local_ip}"; then break; else echo -e "${Error} 本机 IP 格式错误，请重新输入!"; fi
        fi
    done
    
    echo -e "5. 请选择转发协议:\n 1. TCP\n 2. UDP\n 3. TCP+UDP (默认)"
    read -e -p "请输入数字 [1-3]: " forwarding_type_num
    case "$forwarding_type_num" in
        1) forwarding_type="TCP" ;;
        2) forwarding_type="UDP" ;;
        *) forwarding_type="TCP+UDP" ;;
    esac

    echo -e "${Info} 开始添加规则..."
    Add_forwarding_core "$forwarding_type" "$local_port" "$forwarding_ip" "$forwarding_port" "$local_ip"
    Save_iptables
    echo -e "${Info} 端口转发添加成功！"
    View_forwarding
}

Del_forwarding_core(){
    local select_idx=$1
    mapfile -t prerouting_rules < <(iptables -t nat -S PREROUTING | grep "DNAT")
    local target_rule="${prerouting_rules[$((select_idx-1))]}"
    
    if [[ -z "$target_rule" ]]; then
        echo -e "${Error} 获取规则失败！"
        return 1
    fi
    
    # 提取规则特征
    local proto=$(echo "$target_rule" | grep -oP '(?<=-p )\w+')
    local dport=$(echo "$target_rule" | grep -oP '(?<=--dport )\S+')
    local to_dest=$(echo "$target_rule" | grep -oP '(?<=--to-destination )\S+')
    local remote_ip=$(echo "$to_dest" | cut -d':' -f1)
    
    # 直接将选中的规则从 -A 替换为 -D 并执行删除 (百分百精准删除该 PREROUTING 规则)
    local del_prerouting=$(echo "$target_rule" | sed 's/-A /-D /')
    iptables -t nat $del_prerouting
    
    # 尝试连带删除关联的 POSTROUTING (SNAT) 和 INPUT 规则
    if [[ -n "$proto" && -n "$remote_ip" ]]; then
        iptables -t nat -S POSTROUTING | grep "SNAT" | grep -- "-p $proto" | grep -- "-d $remote_ip" | sed 's/-A /-D /g' | while read -r line; do iptables -t nat $line 2>/dev/null; done
        if [[ -n "$dport" ]]; then
            iptables -S INPUT | grep -- "-p $proto" | grep -- "--dport $dport" | sed 's/-A /-D /g' | while read -r line; do iptables $line 2>/dev/null; done
        fi
    fi
}

Del_forwarding(){
    View_forwarding || exit 1
    read -e -p "请输入要删除的规则序号 (默认取消): " del_num
    [[ -z "${del_num}" ]] && echo "取消..." && exit 1
    if [[ "$del_num" =~ ^[0-9]+$ ]] && [ "$del_num" -ge 1 ] && [ "$del_num" -le "${#rule_list[@]}" ]; then
        Del_forwarding_core "$del_num"
        Save_iptables
        echo -e "${Info} 规则删除成功！"
    else
        echo -e "${Error} 输入有误，请输入列表中的正确序号！"
    fi
}

Uninstall_forwarding(){
    check_iptables
    echo -e "${Error} 警告：此操作将清空系统中所有的 DNAT 和 SNAT 规则！\n如果服务器有 Docker 等服务可能会导致网络异常！"
    echo -e "确定要清空【所有】全局端口转发规则 ? [y/N]"
    read -e -p "(默认: n): " unyn
    [[ -z ${unyn} ]] && unyn="n"
    if [[ ${unyn} == [Yy] ]]; then
        iptables -t nat -S PREROUTING | grep "DNAT" | sed 's/-A /-D /g' | while read -r line; do iptables -t nat $line; done
        iptables -t nat -S POSTROUTING | grep "SNAT" | sed 's/-A /-D /g' | while read -r line; do iptables -t nat $line; done
        Save_iptables
        echo -e "${Info} 所有全局 DNAT/SNAT 规则已清空 !"
    else
        echo "取消清空..."
    fi
}

check_sys
echo && echo -e " iptables 全局端口转发一键管理脚本 ${Red_font_prefix}[全局接管版 v${sh_ver}]${Font_color_suffix}
————————————
 ${Green_font_prefix}1.${Font_color_suffix} 安装/初始化 iptables 环境
 ${Green_font_prefix}2.${Font_color_suffix} 清空【所有】全局转发规则 (高危)
————————————
 ${Green_font_prefix}3.${Font_color_suffix} 查看全局转发规则
 ${Green_font_prefix}4.${Font_color_suffix} 添加转发规则
 ${Green_font_prefix}5.${Font_color_suffix} 删除转发规则
————————————
注意：首次使用请务必先执行 ${Green_font_prefix}1. 安装/初始化 iptables 环境${Font_color_suffix}" && echo
read -e -p " 请输入数字 [1-5]: " num
case "$num" in
    1) install_iptables ;;
    2) Uninstall_forwarding ;;
    3) View_forwarding ;;
    4) Add_forwarding ;;
    5) Del_forwarding ;;
    *) echo "请输入正确数字 [1-5]" ;;
esac
