#!/bin/bash

main_cfg='/etc/tproxy/ss-tproxy.conf'

if [ ! -e "${main_cfg}" ]; then
    echo -e "\e[37m${main_cfg}\e[0m  [\e[1;35mnot_exist\e[0m]" 1>&2
    exit 1
else
    source "${main_cfg}"
fi

function pdnsd_update() {
    if ! id -u $pdnsd_global_runuser &> /dev/null; then
        useradd -M -U -d $pdnsd_global_cachedir -s /sbin/nologin $pdnsd_global_runuser
    fi
    if [ ! -d $pdnsd_global_cachedir ]; then
        rm -fr $pdnsd_global_cachedir
        mkdir -p $pdnsd_global_cachedir
    fi
    if [ ! -f $pdnsd_global_cachedir/pdnsd.cache ]; then
        touch $pdnsd_global_cachedir/pdnsd.cache
    fi
    chown -R $pdnsd_global_runuser:$pdnsd_global_runuser $pdnsd_global_cachedir
    cat << EOF > $pdnsd_global_conf
# Generated by ss-tproxy at $(date '+%F %T')
global {
    perm_cache = $pdnsd_global_cachesize;
    cache_dir = "$pdnsd_global_cachedir";
    run_as = "$pdnsd_global_runuser";
    server_ip = $pdnsd_global_bindaddr;
    server_port = $pdnsd_global_bindport;
    tcp_server = $pdnsd_global_bindtcp;
    query_method = $pdnsd_global_querymethod;
    status_ctl = $pdnsd_global_statusctl;
    paranoid = $pdnsd_global_paranoid;
    min_ttl = $pdnsd_global_minttl;
    max_ttl = $pdnsd_global_maxttl;
    timeout = $pdnsd_global_timeout;
    randomize_recs = $pdnsd_global_ramdomize;
    verbosity = $pdnsd_global_loglevel;
}

server {
    label = "$pdnsd_server_label";
    ip = $pdnsd_server_upaddr;
    port = $pdnsd_server_upport;
    proxy_only = $pdnsd_server_proxyonly;
}

source {
    owner = $pdnsd_source_owner;
    file = "$pdnsd_source_file";
}
EOF
}
function pdnsd_flush() {
    if [ $(pgrep -c '^pdnsd$') -eq 0 ]; then
        echo -n > $pdnsd_global_cachedir/pdnsd.cache
    else
        pdnsd-ctl -c $pdnsd_global_cachedir empty-cache &> /dev/null
        pdnsd-ctl -c $pdnsd_global_cachedir config &> /dev/null
    fi
}

function chnip_update() {
    #chnip_url="http://f.ip.cn/rt/chnroutes.txt"
    chnip_url="https://github.com/17mon/china_ip_list/raw/master/china_ip_list.txt"
    curl -4sSkL ${chnip_url} | egrep -v '^\s*$|^\s*#' > ${chinadns_chnroute}
    cat ${chinadns_chnroute} | xargs -n1 echo add chnroute > ${ipset_save_file}

    ipset -L chnroute &> /dev/null
    if [ $? -ne 0 ]; then
        ipset -N chnroute hash:net
    else
        ipset -F chnroute &> /dev/null
    fi

    ipset -R < ${ipset_save_file}
    ipset -S chnroute > ${ipset_save_file}
}

function ipts_update() {
    iptables -t mangle -F PREROUTING
    iptables -t mangle -F SS-UDP &> /dev/null
    iptables -t mangle -X SS-UDP &> /dev/null
    iptables -t nat -F OUTPUT
    iptables -t nat -F PREROUTING
    iptables -t nat -F POSTROUTING
    iptables -t nat -F SS-TCP &> /dev/null
    iptables -t nat -X SS-TCP &> /dev/null

    iptables -t nat -N SS-TCP
    iptables -t nat -A SS-TCP -d 0/8 -j RETURN
    iptables -t nat -A SS-TCP -d 10/8 -j RETURN
    iptables -t nat -A SS-TCP -d 127/8 -j RETURN
    iptables -t nat -A SS-TCP -d 169.254/16 -j RETURN
    iptables -t nat -A SS-TCP -d 172.16/12 -j RETURN
    iptables -t nat -A SS-TCP -d 192.168/16 -j RETURN
    iptables -t nat -A SS-TCP -d 224/4 -j RETURN
    iptables -t nat -A SS-TCP -d 240/4 -j RETURN
    iptables -t nat -A SS-TCP -d ${server_addr} -j RETURN
    iptables -t nat -A SS-TCP -m set --match-set chnroute dst -j RETURN
    iptables -t nat -A SS-TCP -p tcp -j REDIRECT --to-ports ${redir_port}

    iptables -t mangle -N SS-UDP
    iptables -t mangle -A SS-UDP -d 0/8 -j RETURN
    iptables -t mangle -A SS-UDP -d 10/8 -j RETURN
    iptables -t mangle -A SS-UDP -d 127/8 -j RETURN
    iptables -t mangle -A SS-UDP -d 169.254/16 -j RETURN
    iptables -t mangle -A SS-UDP -d 172.16/12 -j RETURN
    iptables -t mangle -A SS-UDP -d 192.168/16 -j RETURN
    iptables -t mangle -A SS-UDP -d 224/4 -j RETURN
    iptables -t mangle -A SS-UDP -d 240/4 -j RETURN
    iptables -t mangle -A SS-UDP -d ${server_addr} -j RETURN
    iptables -t mangle -A SS-UDP -m set --match-set chnroute dst -j RETURN
    iptables -t mangle -A SS-UDP -p udp -j TPROXY --tproxy-mark ${iptables_tproxy_mark} --on-ip ${iptables_tproxy_ip} --on-port ${iptables_tproxy_port}

    iptables -t nat -A OUTPUT -p tcp -j SS-TCP

    for intranet in "${iptables_intranet[@]}"; do
        iptables -t mangle -A PREROUTING -p udp -s $intranet ! -d $intranet --dport 53 -j ACCEPT
        iptables -t nat -A PREROUTING -p udp -s $intranet ! -d $intranet --dport 53 -j REDIRECT --to-ports 53

        iptables -t nat -A PREROUTING -p tcp -s $intranet -j SS-TCP
        iptables -t mangle -A PREROUTING -p udp -s $intranet -j SS-UDP

        iptables -t nat -A POSTROUTING -s $intranet ! -d $intranet -j MASQUERADE
    done
}
function ipts_origin() {
    iptables -t mangle -F PREROUTING
    iptables -t mangle -F SS-UDP &> /dev/null
    iptables -t mangle -X SS-UDP &> /dev/null
    iptables -t nat -F OUTPUT
    iptables -t nat -F PREROUTING
    iptables -t nat -F POSTROUTING
    iptables -t nat -F SS-TCP &> /dev/null
    iptables -t nat -X SS-TCP &> /dev/null

    for intranet in "${iptables_intranet[@]}"; do
        iptables -t nat -A PREROUTING -p udp -s $intranet -d $intranet --dport 53 -j DNAT --to-destination ${dns_original[0]}
        iptables -t nat -A POSTROUTING -s $intranet ! -d $intranet -j MASQUERADE
    done
}

function pbr_add() {
    ip route add local 0/0 dev lo table ${pbr_table}
    ip rule add fwmark ${iptables_tproxy_mark} table ${pbr_table}
}
function pbr_del() {
    ip rule show | grep "fwmark ${iptables_tproxy_mark}" | awk -F':' '{print $1}' | xargs -n1 ip rule del pref &> /dev/null
    ip route flush table ${pbr_table}
}

function ipfwd_on() {
    if [ $(cat /proc/sys/net/ipv4/ip_forward) -ne 1 ]; then
        echo 1 > /proc/sys/net/ipv4/ip_forward
    fi
}

function dns_update() {
    echo "# Generated by ss-tproxy at $(date '+%F %T')" > /etc/resolv.conf
    echo "nameserver 127.0.0.1" >> /etc/resolv.conf
}
function dns_origin() {
    echo "# Generated by ss-tproxy at $(date '+%F %T')" > /etc/resolv.conf
    for dns in "${dns_original[@]}"; do
        echo "nameserver $dns" >> /etc/resolv.conf
    done
}

function current_ip() {
    ip_info=$(curl -4skL ip.chinaz.com/getip.aspx | sed -r "s/^\{ip:'(.*)',address:'(.*)'\}$/\1|\2/g")
    my_ip=$(echo ${ip_info} | awk -F'|' '{print $1}')
    my_loc=$(echo ${ip_info} | awk -F'|' '{print $2}')
    echo -e "\e[37mIP:\e[0m ${my_ip}\t\e[37mLocation:\e[0m ${my_loc}"
}

function start() {
    dns_origin

    if [ ! -e ${chinadns_chnroute} ]; then
        echo -e "\e[37m${chinadns_chnroute}\e[0m  [\e[1;35mnot_exist\e[0m]"
        exit 1
    fi

    if [ "${server_use_ssr}" = 'false' ]; then
        redir_params="-s '${server_addr}' -p '${server_port}' -m '${server_method}' -k '${server_passwd}' -b '${redir_addr}' -l '${redir_port}'"
        if [ "${redir_udp_relay}" = 'true' ]; then redir_params+=" -u"; fi
        if [ "${redir_verbose}" = 'true' ]; then redir_params+=" -v"; fi
        if [ "${redir_no_delay}" = 'true' ]; then redir_params+=" --no-delay"; fi
        if [ "${redir_reuse_port}" = 'true' ]; then redir_params+=" --reuse-port"; fi
        if [ "${redir_fast_open}" = 'true' ]; then redir_params+=" --fast-open"; fi
        eval "nohup ss-redir $redir_params < /dev/null &>> '${redir_log}' &"
    else
        redir_params="-s '${server_addr}' -p '${server_port}' -m '${server_method}' -k '${server_passwd}' -O '${server_protocol}' -o '${server_obfs}' -b '${redir_addr}' -l '${redir_port}'"
        if [ -n "${server_protocol_param}" ]; then redir_params+=" -G '${server_protocol_param}'"; fi
        if [ -n "${server_obfs_param}" ]; then redir_params+=" -g '${server_obfs_param}'"; fi
        if [ "${redir_udp_relay}" = 'true' ]; then redir_params+=" -u"; fi
        if [ "${redir_verbose}" = 'true' ]; then redir_params+=" -v"; fi
        eval "nohup ssr-redir $redir_params < /dev/null &>> '${redir_log}' &"
    fi

    if [ "${server_use_ssr}" = 'false' ]; then
        tunnel_params="-s '${server_addr}' -p '${server_port}' -m '${server_method}' -k '${server_passwd}' -b '${tunnel_addr}' -l '${tunnel_port}' -L '${tunnel_upstream}'"
        if [ "${tunnel_udp_relay}" = 'true' ]; then tunnel_params+=" -u"; fi
        if [ "${tunnel_verbose}" = 'true' ]; then tunnel_params+=" -v"; fi
        if [ "${tunnel_no_delay}" = 'true' ]; then tunnel_params+=" --no-delay"; fi
        if [ "${tunnel_reuse_port}" = 'true' ]; then tunnel_params+=" --reuse-port"; fi
        eval "nohup ss-tunnel $tunnel_params < /dev/null &>> '${tunnel_log}' &"
    else
        tunnel_params="-s '${server_addr}' -p '${server_port}' -m '${server_method}' -k '${server_passwd}' -O '${server_protocol}' -o '${server_obfs}' -b '${tunnel_addr}' -l '${tunnel_port}' -L '${tunnel_upstream}'"
        if [ -n "${server_protocol_param}" ]; then tunnel_params+=" -G '${server_protocol_param}'"; fi
        if [ -n "${server_obfs_param}" ]; then tunnel_params+=" -g '${server_obfs_param}'"; fi
        if [ "${tunnel_udp_relay}" = 'true' ]; then tunnel_params+=" -u"; fi
        if [ "${tunnel_verbose}" = 'true' ]; then tunnel_params+=" -v"; fi
        eval "nohup ssr-tunnel $tunnel_params < /dev/null &>> '${tunnel_log}' &"
    fi

    chinadns_params="-b ${chinadns_addr} -p ${chinadns_port} -s ${chinadns_upstream} -c ${chinadns_chnroute}"
    if [ "${chinadns_mutation}" = 'true' ]; then chinadns_params+=" -m"; fi
    if [ "${chinadns_verbose}" = 'true' ]; then chinadns_params+=" -v"; fi
    nohup chinadns $chinadns_params < /dev/null &>> "${chinadns_log}" &

    pdnsd_update
    nohup pdnsd -c $pdnsd_global_conf < /dev/null &>> $pdnsd_global_logfile &

    ipset -X chnroute &> /dev/null
    ipset -R < ${ipset_save_file}

    ipts_update
    pbr_add
    ipfwd_on

    dns_update
}

function stop() {
    pkill '^pdnsd$' &> /dev/null
    pkill '^chinadns$' &> /dev/null
    pkill '^ss-redir$' &> /dev/null
    pkill '^ss-tunnel$' &> /dev/null
    pkill '^ssr-redir$' &> /dev/null
    pkill '^ssr-tunnel$' &> /dev/null

    ipts_origin
    ipset -X chnroute &> /dev/null
    pbr_del

    dns_origin
}

function status() {
    if [ $(pgrep -c '^pdnsd$') -ge 1 ]; then
        echo -e "\e[37mpdnsd\e[0m\t\t[\e[1;32mrunning\e[0m]"
    else
        echo -e "\e[37mpdnsd\e[0m\t\t[\e[1;35mstopped\e[0m]"
    fi

    if [ $(pgrep -c '^chinadns$') -ge 1 ]; then
        echo -e "\e[37mchinadns\e[0m\t[\e[1;32mrunning\e[0m]"
    else
        echo -e "\e[37mchinadns\e[0m\t[\e[1;35mstopped\e[0m]"
    fi

    if [ "${server_use_ssr}" = 'false' ]; then
        if [ $(pgrep -c '^ss-redir$') -ge 1 ]; then
            echo -e "\e[37mss-redir\e[0m\t[\e[1;32mrunning\e[0m]"
        else
            echo -e "\e[37mss-redir\e[0m\t[\e[1;35mstopped\e[0m]"
        fi
    else
        if [ $(pgrep -c '^ssr-redir$') -ge 1 ]; then
            echo -e "\e[37mssr-redir\e[0m\t[\e[1;32mrunning\e[0m]"
        else
            echo -e "\e[37mssr-redir\e[0m\t[\e[1;35mstopped\e[0m]"
        fi
    fi

    if [ "${server_use_ssr}" = 'false' ]; then
        if [ $(pgrep -c '^ss-tunnel$') -ge 1 ]; then
            echo -e "\e[37mss-tunnel\e[0m\t[\e[1;32mrunning\e[0m]"
        else
            echo -e "\e[37mss-tunnel\e[0m\t[\e[1;35mstopped\e[0m]"
        fi
    else
        if [ $(pgrep -c '^ssr-tunnel$') -ge 1 ]; then
            echo -e "\e[37mssr-tunnel\e[0m\t[\e[1;32mrunning\e[0m]"
        else
            echo -e "\e[37mssr-tunnel\e[0m\t[\e[1;35mstopped\e[0m]"
        fi
    fi
}

function usage() {
    echo -e "\e[37mUsage:\e[0m \e[36m${0}\e[0m \e[1;37mCOMMAND\e[0m"
    echo -e "\e[1;37mCOMMAND\e[0m := {"
    echo -e "\t\e[33mstart\e[0m\t\tstart ss-tproxy"
    echo -e "\t\e[33mstop\e[0m\t\tstop ss-tproxy"
    echo -e "\t\e[33mrestart\e[0m\t\trestart ss-tproxy"
    echo -e "\t\e[33mstatus\e[0m\t\tstatus of ss-tproxy"
    echo -e "\t\e[33mcurrent_ip\e[0m\tget current ipaddr info"
    echo -e "\t\e[33mflush_dnsche\e[0m\tflush dns cache for pdnsd"
    echo -e "\t\e[33mupdate_chnip\e[0m\tupdate chnroute for ipset/chinadns"
    echo -e "}"
}

case $1 in
star*)  # start
    start
    status
    ;;
stat*)  # status
    status
    ;;
sto*)   # stop
    stop
    status
    ;;
r*)     # restart
    stop
    status
    echo
    start
    status
    ;;

c*)     # current_ip
    current_ip
    ;;
f*)     # flush_dnsche
    pdnsd_flush
    ;;
u*)     # update_chnip
    chnip_update
    ;;

h*)     # help
    usage
    ;;
*)
    usage 1>&2
    exit 1
    ;;
esac
