#!/bin/sh /etc/rc.common
#
# Copyright (C) 2023 Dengfeng Liu <liudf0716@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#

USE_PROCD=1
START=99

CONF="ss-redir"
PROG="/usr/bin/ss-redir"
GO_SS2="/usr/bin/go-ss2"
CONFILE="/tmp/ss-redir.json"

mk_ss_redir_conf() {
    local stream_methods='"table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", "chacha20-ietf"'
    local aead_methods='"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"'

    uci_validate_section ss-redir ss_redir "server" \
        'server:host:127.0.0.1' \
        'server_port:port:12948' \
        'password:string' \
        "encrypt:or($stream_methods, $aead_methods)" \
        'local_address:host:0.0.0.0' \
        'local_port:port:1088' \
        'fast_open:bool:1' \
        'ipv6_first:bool:0' \
        'no_delay:bool:0' \
        'reuse_port:bool:1' \
        'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \
        'mtu:uinteger:1350' \
        'timeout:uinteger:60'

    json_init
    json_add_string server $server
    json_add_int server_port $server_port
    json_add_string method $encrypt
    json_add_string password $password
    json_add_string local_address $local_address
    json_add_int local_port $local_port
    json_add_string mode $mode
    json_add_int mtu $mtu
    json_add_int timeout $timeout
    json_add_boolean fast_open $fast_open
    json_add_boolean ipv6_first $ipv6_first
    json_add_boolean no_delay $no_delay
    json_add_boolean reuse_port $reuse_port
    json_dump -i >"$CONFILE"
}

add_ss_redir_rule() {
    nft add set inet fw4 ss-redir_set { type ipv4_addr\; }
    nft add set inet fw4 ss-redir_set6 { type ipv6_addr\; }
    nft add set inet fw4 all_proxy_set { type ipv4_addr\; }
    nft add set inet fw4 all_proxy_set6 { type ipv6_addr\; }
    nft add chain inet fw4 ss-redir_chain
    nft add rule inet fw4 dstnat iifname br-lan jump ss-redir_chain
    nft add rule inet fw4 ss-redir_chain ip saddr @all_proxy_set meta l4proto tcp redirect to 1080
    nft add rule inet fw4 ss-redir_chain ip6 saddr @all_proxy_set6 meta l4proto tcp redirect to 1080
    nft add rule inet fw4 ss-redir_chain ip daddr @ss-redir_set meta l4proto tcp redirect to 1088
    nft add rule inet fw4 ss-redir_chain ip daddr @ss-redir_set meta l4proto udp redirect to 1088
    nft add rule inet fw4 ss-redir_chain ip6 daddr @ss-redir_set6 meta l4proto udp redirect to 1088
    nft add rule inet fw4 ss-redir_chain ip6 daddr @ss-redir_set6 meta l4proto tcp redirect to 1088

}

clear_ss_redir_rule() {
    if [ -z "$(nft -a list chain inet fw4 dstnat | grep ss-redir_chain)" ]; then
        echo "no ss-redir rule"
        return
    fi
    # get the handle of the ss-redir_chain in dstnat
    handle_num=$(nft -a list chain inet fw4 dstnat | grep ss-redir_chain | awk '{print $7}')
    if [ -z "$handle_num" ]; then
        echo "cant get the handle of ss-redir_chain in dstnat"
        return
    fi
    nft delete rule inet fw4 dstnat handle $handle_num
    nft flush chain inet fw4 ss-redir_chain
    nft delete chain inet fw4 ss-redir_chain
    nft delete set inet fw4 ss-redir_set
    nft delete set inet fw4 ss-redir_set6
    nft delete set inet fw4 all_proxy_set
    nft delete set inet fw4 all_proxy_set6
}

ss_redir_rule() {
    clear_ss_redir_rule
    add_ss_redir_rule
}

get_proxy_command() {
    if [ -x "$GO_SS2" ]; then
        echo "$GO_SS2 -c 'ss://$encrypt:$password@$server:$server_port' -redir :$local_port"
    else
        echo "$PROG -c $CONFILE"
    fi
}

start_service() {
    config_load "$CONF"
    local enabled

    config_get_bool enabled "server" "enabled" "0"
    [ "$enabled" -eq "1" ] || {
        echo "service does not enabled"
        clear_ss_redir_rule
        return 1
    }

    mk_ss_redir_conf
    [ ! -f $CONFILE ] && return 1


    procd_open_instance
    if [ -x "$GO_SS2" ]; then
        procd_set_param command "$GO_SS2" -c "ss://$encrypt:$password@$server:$server_port" -verbose -redir ":$local_port"
        procd_set_param stdout 1
        procd_set_param stderr 1
    else
        procd_set_param command "$PROG" -c "$CONFILE"
    fi
    procd_set_param respawn
    procd_close_instance

    ss_redir_rule
}

reload_service() {
    stop
    start
}

service_triggers() {
    procd_add_reload_trigger "$CONF"
}