#! /bin/bash
### BEGIN INIT INFO
# Provides:          nperf-server
# Required-Start:    $all
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:
# Short-Description: nPerfServer
### END INIT INFO
# Manages the services to run nPerfServer
# Script for Unix/Linux. Version : 2.1
# https://www.nperf.com/

OPT_ARGS=""
BIND_IP=""
IPTABLES_ARGS=""
IP6TABLES_ARGS=""
IPV6=""
NB_THREAD=""
DAEMON_START=""
PORT="8080"
TLS_PORT="8443"
REDIRECT_PORT_80=0
REDIRECT_PORT_443=0
IPV4_BIND=""
IPV6_BIND=""
IPV4_ADDRESSES=""
IPV6_ADDRESSES=""
LOG_CONNECTIONS=0

KERNEL_VERSION_MAJ=$(uname -r | cut -d '-' -f 1 | cut -d '.' -f 1)
KERNEL_VERSION_MIN=$(uname -r | cut -d '-' -f 1 | cut -d '.' -f 2)
if [ "$KERNEL_VERSION_MAJ" -ge "4" ]; then
    IPV6_REDIR=true
elif [ "$KERNEL_VERSION_MAJ" -ge "3" ] && [ "$KERNEL_VERSION_MIN" -ge "9" ]; then
    IPV6_REDIR=true
else
    IPV6_REDIR=false
fi

set -e

. "/etc/nperf/nperf-server.conf"

test -x $DAEMON || exit 1
test -z $1 && arg="empty" || arg=$1

set -u
DESC="`basename $DAEMON` daemon"

DAEMON_START="$DAEMON_START -p $PORT -t $TLS_PORT --uuidfile=$UUID"
if [ "$LOG_CONNECTIONS" -eq 1 ]; then
    DAEMON_START="$DAEMON_START -a"
fi
if [ -n "$OPT_ARGS" ]; then
    DAEMON_START="$DAEMON_START $OPT_ARGS"
fi


if [[ $BIND_IP = *":"* ]]; then
    IPV6=true
fi

TESTIP="localhost"
if [ -n "$BIND_IP" ]; then
    DAEMON_START="$DAEMON_START -i $BIND_IP"
    for address in $BIND_IP
    do
        if [[ $address = *":"* ]] ; then
            if [ "$address" != "::" ] ; then
                IPV6_ADDRESSES="$IPV6_ADDRESSES,$address/128"
                if [ -z "$TESTIP" ]; then 
                    TESTIP=$address
                fi
            fi
        elif [ "$address" != "0.0.0.0" ]; then
            IPV4_ADDRESSES="$IPV4_ADDRESSES,$address/32"
            if [ -z "$TESTIP" ]; then 
                TESTIP=$address
            fi
        fi
    done
    if [ -n "$IPV4_ADDRESSES" ] ; then
        IPTABLES_ARGS=" -d ${IPV4_ADDRESSES:1}"
        IPV4_BIND=" (binded to ${IPV4_ADDRESSES:1})"
    fi
    if [ -n "$IPV6_ADDRESSES" ] ; then
        IP6TABLES_ARGS=" -d ${IPV6_ADDRESSES:1}"
        IPV6_BIND=" (binded to ${IPV6_ADDRESSES:1})"
    fi
fi

isrunning() {
        curl -s -m 5 -i $TESTIP:$PORT | grep "Server: nPerf" >/dev/null 2>&1 && return 0
        return 1
}

initnet() {
    echo -n "Activating IPv4 Anti-DOS limitation..."
    CHECK_DOS_IPV4=$(/sbin/iptables-save | grep nPerfServer | grep "connlimit" | cat)
    if [ -z "$CHECK_DOS_IPV4" ]; then
        /sbin/iptables -A INPUT -p tcp --syn -m connlimit --connlimit-above 50 -j REJECT -m comment --comment nPerfServer
        echo " OK."
    else
        echo " Already set!"
    fi

    echo -n "Flushing existing IPv4 nPerfServer rules..."
    while : ; do
        EXISTING_RULE_ID=$(/sbin/iptables -nL -v --line-numbers -t nat | grep nPerfServer | grep "tcp dpt:80 " | head -1 | cut -d ' ' -f 1 )
        if [ -n "$EXISTING_RULE_ID" ]; then
            /sbin/iptables -t nat -D PREROUTING $EXISTING_RULE_ID
        else 
            break
        fi
    done
    echo " OK."


    if [ "$IPV6" = true ]; then
        echo -n "Activating IPv6 Anti-DOS limitation..."
        CHECK_DOS_IPV6=$(/sbin/ip6tables-save | grep nPerfServer | grep "connlimit" | cat)
        if [ -z "$CHECK_DOS_IPV6" ]; then
            /sbin/ip6tables -A INPUT -p tcp --syn -m connlimit --connlimit-above 50 --connlimit-mask 64 -j REJECT -m comment --comment nPerfServer
            echo " OK."
        else
            echo " Already set!"
        fi
        
        echo -n "Flushing existing IPv6 nPerfServer rules..."
        while : ; do
            EXISTING_RULE_ID=$(/sbin/ip6tables -nL -v --line-numbers -t nat | grep nPerfServer | grep "tcp dpt:80 " | head -1 | cut -d ' ' -f 1)
            if [ -n "$EXISTING_RULE_ID" ]; then
                /sbin/ip6tables -t nat -D PREROUTING $EXISTING_RULE_ID
            else 
                break
            fi
        done
        echo " OK."
    fi

    if [ "$REDIRECT_PORT_80" -eq 1 ]; then
        echo -n "Activating IPv4 redirection from TCP port 80$IPV4_BIND to port $PORT..."
        /sbin/iptables -t nat -A PREROUTING -p tcp $IPTABLES_ARGS --dport 80 -j REDIRECT --to $PORT -m comment --comment nPerfServer
        echo " OK."
        if [ "$IPV6" = true ]; then
            if [ -n "$(/usr/bin/which ip6tables)" ]; then
                echo -n "Activating IPv6 redirection from TCP port 80$IPV6_BIND to port $PORT..."
                if [ "$IPV6_REDIR" = true ]; then
                    /sbin/ip6tables -t nat -A PREROUTING $IP6TABLES_ARGS -p tcp --dport 80 -j REDIRECT --to $PORT -m comment --comment nPerfServer
                    echo " OK."
                else
                    echo " Kernel must be >= 3.9, upgrade kernel or use port $PORT."
                fi
            else
                echo 1>&2 "Error: ip6tables is not available, please install ip6tables OR disable IPv6 for nPerfServer OR disable port redirection."
                exit 1
            fi
        fi
    fi

    if [ "$REDIRECT_PORT_443" -eq 1 ]; then
        echo -n "Activating IPv4 redirection from TCP port 443$IPV4_BIND to port $TLS_PORT..."
        /sbin/iptables -t nat -A PREROUTING -p tcp $IPTABLES_ARGS --dport 443 -j REDIRECT --to $TLS_PORT -m comment --comment nPerfServer
        echo " OK."
        if [ "$IPV6" = true ]; then
            if [ -n "$(/usr/bin/which ip6tables)" ]; then
                echo -n "Activating IPv6 redirection from TCP port 443$IPV6_BIND to port $TLS_PORT..."
                if [ "$IPV6_REDIR" = true ]; then
                    /sbin/ip6tables -t nat -A PREROUTING -p tcp $IP6TABLES_ARGS --dport 443 -j REDIRECT --to $TLS_PORT -m comment --comment nPerfServer
                    echo " OK."
                else
                    echo " Kernel must be >= 3.9, upgrade kernel or use port $TLS_PORT."
                fi
            else
                echo 1>&2 "Error: ip6tables is not available, please install ip6tables OR disable IPv6 for nPerfServer OR disable port redirection."
                exit 1
            fi
        fi
    fi
}

start() {
    if [ -z "$NB_THREAD" ]; then
        echo -n "Starting $DESC ... "
    else
        echo -n "Starting $DESC with $NB_THREAD threads ... "
        DAEMON_START="$DAEMON_START -w $NB_THREAD"
    fi
    DAEMON_START="-x --pidfile=$PID $DAEMON_START"
    rm -f $PID
    set +e
    [ "`id -u`" -eq "0" ] && su $USER -s /bin/sh -c "${DAEMON} ${DAEMON_START}" >/dev/null
    [ "`id -u`" -eq "`id -u $USER`" ] && eval "${DAEMON} ${DAEMON_START}"
    set -e
    sleep 0.5 ; test -e $PID && echo "[ ok ]" || echo "[fail]"
}

stop() {
    echo -n "Stopping $DESC : "
    pkill `basename $DAEMON` || true
    sleep 0.5 ; test -e $PID && echo "[fail]" || echo "[ ok ]"
}
case "$arg" in
    initnet)
        initnet
    ;;
    startcmd)
        if [ -n "$NB_THREAD" ]; then
            DAEMON_START="$DAEMON_START -w $NB_THREAD"
        fi
        echo "$DAEMON $DAEMON_START"
    ;;
    start-systemd)
        if [ -z "$NB_THREAD" ]; then
            echo -n "Starting $DESC ... "
        else
            echo -n "Starting $DESC with $NB_THREAD threads ... "
            DAEMON_START="$DAEMON_START -w $NB_THREAD"
        fi
        DAEMON_START="-x --pidfile=$PID $DAEMON_START"
        rm -f $PID
        set +e
        ${DAEMON} ${DAEMON_START}
        set -e
        exit $?
    ;;
    start)
        if isrunning; then
            echo "$DESC is already running. Use \"restart\" if you want to restart it."
        else
            initnet
            start
        fi
    ;;
    stop)
        if isrunning; then
            stop
        else
            echo "$DESC is not running. No need to stop it."
        fi
    ;;
    status)
        isrunning && echo "$DESC is running." || echo "$DESC is stopped."
    ;;
    version)
        $DAEMON -v || true
    ;;
    restart)
        if isrunning; then
            stop
            initnet
            start
        else
            echo "$DESC is not running. Use \"start\" if you want to start it."
        fi
    ;;
    setup-initd-autostart)
        echo -n "Setting up init.d to auto-start nPerfServer : "
        chkconfig --level 345 nperf-server on
        cat <<EOF > /etc/cron.d/nperf-server
# Persistent run (auto restart nPerfServer on crash)
*/2 * * * *    root   /etc/init.d/nperf-server status | grep "nPerfServer daemon is running" >/dev/null || /etc/init.d/nperf-server start
EOF
        echo "[ ok ]"
    ;;
    *)
        echo "Usage: $0 {start|stop|restart|status|version|setup-initd-autostart}" >&2
        exit 1
    ;;
esac

exit 0