#!/bin/bash

#
# Name:         rsb-wg-acct.sh
# Version:      1.0.2
# Description:  Track Wireguard sessions.
#
# (c) 2023, Rapidseedbox, alexey@rapidseedbox.com
#

if [ "${DEBUG}" = "yes" ] ; then
    set -x
fi

set -o pipefail

export PATH=/usr/sbin:/usr/bin:/sbin:/bin

SC_NAME="$( basename "${0}" )"
SC_VER="$( grep -m 1 -E '^#\s*Version:\s+' "${0}" | awk '{print $NF}' )"

[ -z "${_wg_dump}" ] || [ -z "${WG_DEV}" ] && exit 0

command -v sqlite3 > /dev/null 2>&1 || exit 0

HOSTNAME="$( hostname )"

STATS_DIR="/var/db/rsb"
[ ! -d "${STATS_DIR}" ] && mkdir -p "${STATS_DIR}"
STATS_DB="${STATS_DIR}/wg.db"

LOGFILE="/var/log/rsb/wg.log"
[ ! -d "/var/log/rsb" ] && mkdir -p "/var/log/rsb"

#env >> "${STATS_DIR}/wg-debug.log"
#set >> "${STATS_DIR}/wg-debug.log"


__debug_log () {
    [ "${DEBUG}" = "yes" ] || return 0
    local _msg="${1}"
    local _ts
    _ts="$( date -u +"%Y-%m-%d %H:%M:%S" )"
    echo "${_ts} ${SC_NAME}: ${_msg}" | tee -a "${LOGFILE}"
    return 0
}


if [ ! -e "${STATS_DB}" ] ; then
    sqlite3 "${STATS_DB}" <<EOF
CREATE TABLE version ( 
    id TEXT
);
INSERT INTO version ( id ) VALUES ( "${SC_VER}" );
CREATE TABLE session(
    id                       INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    peer                     TEXT,
    local_ip                 TEXT,
    remote_ip                TEXT,
    remote_port              INTEGER,
    cc                       TEXT,
    old_bytes_received       INTEGER,
    old_bytes_sent           INTEGER,
    bytes_received           INTEGER,
    bytes_sent               INTEGER,
    SESSION_START            INTEGER,
    SESSION_HANDSHAKE        INTEGER,
    SESSION_END              INTEGER
    );
EOF
fi

# -- Cleanup tasks
_ts="$( date -u '+%s' )"
while read -r _peer ; do
    if ! grep -q "${_peer}" <<< "${_wg_dump}" ; then
         __debug_log "ORPHANED SESSION: peer: ${_peer}"
         sqlite3 \
             "${STATS_DB}" \
             "UPDATE session \
              SET SESSION_END = ${_ts} \
              WHERE     peer = '${_peer}' \
              AND SESSION_END = 0;"
    fi
done < <( sqlite3 \
              "${STATS_DB}" \
              "SELECT peer FROM session \
               WHERE SESSION_END = 0;" \
            | sort \
            | uniq )

# -- MAIN task
while read -r _f ; do
    _ts="$( date -u '+%s' )"
    _local_ip="$( awk '$1 == "Address" {print $NF}' "${_f}" | sed -e 's/\/.*$//' )"
    _info_line="$( \
        awk -F '\t' \
            -v ip="${_local_ip}" \
            '$4 == ip"/32" && $3 != "51820" && $3 != "(none)" {print $1" "$3" "$4" "$5" "$6" "$7}' \
           <<< "${_wg_dump}" )"
    [ -z "${_info_line}" ] && continue
    read -ra _info_arr <<< "${_info_line}"
    _cc=""
    _remote_ip=""
    _remote_port=""
    _peer="${_info_arr[0]}"
    _connected_since="${_info_arr[3]}"
    _b_received="${_info_arr[4]}"
    _b_sent="${_info_arr[5]}"
    if [ "${_connected_since}" != "0" ] && [ -n "${_info_arr[1]}" ] ; then
        _remote_ip="$( awk   -F':' '{print $1}' <<< "${_info_arr[1]}" )"
        _remote_port="$( awk -F':' '{print $2}' <<< "${_info_arr[1]}" )"
        _cc="$( whois "${_remote_ip}" | grep -m1 '^country:' | awk '{print $NF}' )"
    fi
    # -- Check if existing "active" session was interrupted somehow (server reboot, etc)
    sqlite3 \
        "${STATS_DB}" \
        "UPDATE session \
         SET SESSION_END = ${_ts} \
         WHERE     peer = '${_peer}' \
               AND old_bytes_received > ${_b_received} \
               AND old_bytes_sent     > ${_b_sent} \
               AND SESSION_END = 0;"
    # --
    _old_session_id="$( \
        sqlite3 \
            "${STATS_DB}" \
            "SELECT id FROM session \
             WHERE     peer = '${_peer}' \
                   AND SESSION_END = 0;" )"
    if [ ! -z "${_old_session_id}" ] ; then
        # --
        _old_bytes_received="$( \
            sqlite3 \
                "${STATS_DB}" \
                "SELECT old_bytes_received FROM session WHERE id = '${_old_session_id}';" )"
        _old_bytes_sent="$( \
            sqlite3 \
                "${STATS_DB}" \
                "SELECT old_bytes_sent FROM session WHERE id = '${_old_session_id}';" )"
        _bytes_received="$(( _b_received - _old_bytes_received ))"
        _bytes_sent="$(( _b_sent - _old_bytes_sent ))"
        # --
        if [ "${_connected_since}" = "0" ] ; then
            # -- SESSION END
            __debug_log "-----"
            __debug_log \
                "SESSION END: id: ${_old_session_id}, peer: ${_peer}"
            __debug_log \
                "SESSION END: id: ${_old_session_id}, bytes_received: ${_bytes_received}"
            __debug_log \
                "SESSION END: id: ${_old_session_id}, bytes_sent: ${_bytes_sent}"
            sqlite3 \
                "${STATS_DB}" \
                "UPDATE session \
                 SET SESSION_END = ${_ts}, \
                     old_bytes_received = ${_old_bytes_received}, \
                     old_bytes_sent = ${_old_bytes_sent}, \
                     bytes_received = ${_bytes_received}, \
                     bytes_sent = ${_bytes_sent} \
                 WHERE id = ${_old_session_id};"
        else
            # -- SESSION CONTINUES
            __debug_log "-----"
            __debug_log \
                "SESSION CONTINUATION: id: ${_old_session_id}, peer: ${_peer}"
            __debug_log \
                "SESSION CONTINUATION: id: ${_old_session_id}, local_ip: ${_local_ip}"
            sqlite3 \
                "${STATS_DB}" \
                "UPDATE session \
                 SET SESSION_HANDSHAKE = ${_connected_since}, \
                     old_bytes_received = ${_old_bytes_received}, \
                     old_bytes_sent = ${_old_bytes_sent}, \
                     bytes_received = ${_bytes_received}, \
                     bytes_sent = ${_bytes_sent} \
                 WHERE id = ${_old_session_id};"
            if [ -n "${_remote_ip}" ] ; then
                __debug_log \
                    "SESSION CONTINUATION: id: ${_old_session_id}, remote_ip: ${_remote_ip}, cc: ${_cc}"
                sqlite3 \
                    "${STATS_DB}" \
                    "UPDATE session \
                     SET remote_ip = '${_remote_ip}'
                     WHERE id = ${_old_session_id};"
            fi
            if [ -n "${_remote_port}" ] ; then
                __debug_log \
                    "SESSION CONTINUATION: id: ${_old_session_id}, remote_port: ${_remote_port}"
                sqlite3 \
                    "${STATS_DB}" \
                    "UPDATE session \
                     SET remote_port = ${_remote_port}
                     WHERE id = ${_old_session_id};"
            fi
            __debug_log \
                "SESSION CONTINUATION: id: ${_old_session_id}, bytes_received: ${_bytes_received}"
            __debug_log \
                "SESSION CONTINUATION: id: ${_old_session_id}, bytes_sent: ${_bytes_sent}"
        fi
    else
        if [ "${_connected_since}" != "0" ] ; then
            # -- SESSION START
            sqlite3 \
                "${STATS_DB}" \
                "INSERT INTO session \
                  ( peer, local_ip, \
                    remote_ip, remote_port, cc, \
                    old_bytes_received, old_bytes_sent, \
                    bytes_received, bytes_sent, \
                    SESSION_START, SESSION_HANDSHAKE, SESSION_END )
                 VALUES \
                  ( '${_peer}', '${_local_ip}', \
                    '${_remote_ip}', ${_remote_port}, '${_cc}', \
                    ${_b_received}, ${_b_sent}, \
                    0, 0, \
                    ${_ts}, ${_connected_since}, 0 );"
            _session_id="$( \
                sqlite3 \
                    "${STATS_DB}" \
                    "SELECT id FROM session \
                     WHERE     peer = '${_peer}' \
                           AND SESSION_END = 0;" )"
            __debug_log "-----"
            __debug_log \
                "SESSION START: id: ${_session_id}, peer: ${_peer}"
            __debug_log \
                "SESSION START: id: ${_session_id}, local_ip: ${_local_ip}"
            __debug_log \
                "SESSION START: id: ${_session_id}, remote_ip: ${_remote_ip}, cc: ${_cc}"
            __debug_log \
                "SESSION START: id: ${_session_id}, remote_port: ${_remote_ip}"
#            __debug_log \
#                "SESSION START: id: ${_session_id}, bytes_received: ${_b_received}"
#            __debug_log \
#                "SESSION START: id: ${_session_id}, bytes_sent: ${_b_sent}"
        fi
    fi
done < <( find "/var/www/html/wireguard" -mindepth 1 -maxdepth 1 -name "*.conf" | sort )

exit 0
