/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * srv_lsnr.c
 *
 *
 * IDENTIFICATION
 * src/server/srv_lsnr.c
 *
 * -------------------------------------------------------------------------
 */
#include "srv_module.h"
#include "srv_lsnr.h"
#include "srv_agent.h"
#include "srv_instance.h"
#include "srv_replica.h"
#include "srv_emerg.h"

#ifdef __cplusplus
extern "C" {
#endif

static status_t srv_check_tcp_ip(cs_pipe_t *pipe)
{
    char ipstr[CM_MAX_IP_LEN];
    status_t status;
    bool32 check_res = OG_FALSE;
    (void)cm_inet_ntop((struct sockaddr *)&pipe->link.tcp.remote.addr, ipstr, CM_MAX_IP_LEN);
    status = cm_check_remote_ip(GET_WHITE_CTX, ipstr, &check_res);
    if (status == OG_ERROR || !check_res) {
        OG_THROW_ERROR(ERR_TCP_INVALID_IPADDRESS, ipstr);
        sql_audit_log_ddos(ipstr);
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t srv_tcp_app_connect_action(tcp_lsnr_t *lsnr, cs_pipe_t *pipe)
{
    if (g_instance->kernel.switch_ctrl.request != SWITCH_REQ_NONE) {
        cs_tcp_disconnect(&pipe->link.tcp);
        OG_THROW_ERROR(ERR_SESSION_CLOSED, "server is doing switch request");
        return OG_ERROR;
    }

    if (srv_check_tcp_ip(pipe) != OG_SUCCESS) {
        cs_tcp_disconnect(&pipe->link.tcp);
        return OG_ERROR;
    }

    if (srv_create_session(pipe) != OG_SUCCESS) {
        cs_tcp_disconnect(&pipe->link.tcp);
        OG_THROW_ERROR(ERR_CREATE_AGENT, "agent");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t srv_tcp_replica_connect_action(tcp_lsnr_t *lsnr, cs_pipe_t *pipe)
{
    if (srv_create_replica_session(pipe) != OG_SUCCESS) {
        cs_tcp_disconnect(&pipe->link.tcp);
        OG_THROW_ERROR(ERR_CREATE_AGENT, "replica agent");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t srv_uds_connect_action(uds_lsnr_t *lsnr, cs_pipe_t *pipe)
{
    status_t status;

    if (lsnr->is_emerg) {
        status = srv_create_emerg_session(pipe);
    } else {
        if (g_instance->kernel.switch_ctrl.request != SWITCH_REQ_NONE) {
            cs_uds_disconnect(&pipe->link.uds);
            OG_THROW_ERROR(ERR_SESSION_CLOSED, "server is doing switch request");
            return OG_ERROR;
        }
        status = srv_create_session(pipe);
    }

    if (status != OG_SUCCESS) {
        OG_LOG_RUN_ERR("UDS connect, create %s session failed", lsnr->is_emerg ? "emerg" : "user");
        cs_uds_disconnect(&pipe->link.uds);
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

status_t srv_start_replica_lsnr(void)
{
    status_t status = OG_SUCCESS;

    g_instance->lsnr.tcp_replica.type = LSNR_TYPE_REPLICA;
    if (g_instance->lsnr.tcp_replica.port != 0) {
        status = cs_start_tcp_lsnr(&g_instance->lsnr.tcp_replica, srv_tcp_replica_connect_action, OG_TRUE);
        if (status != OG_SUCCESS) {
            OG_LOG_RUN_ERR("failed to start lsnr for REPL_ADDR");
        }
    }

    return status;
}

status_t srv_start_lsnr(void)
{
    status_t status;

    g_instance->lsnr.tcp_service.type = LSNR_TYPE_SERVICE;
    status = cs_start_tcp_lsnr(&g_instance->lsnr.tcp_service, srv_tcp_app_connect_action, OG_FALSE);
    if (status != OG_SUCCESS) {
        OG_LOG_RUN_ERR("failed to start lsnr for LSNR_ADDR");
        return status;
    }

    status = srv_start_replica_lsnr();
    if (status != OG_SUCCESS) {
        cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_service);
        return status;
    }

    g_instance->lsnr.uds_service.type = LSNR_TYPE_UDS;
    status = cs_start_uds_lsnr(&g_instance->lsnr.uds_service, srv_uds_connect_action);
    if (status != OG_SUCCESS) {
        cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_replica);
        cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_service);
        OG_LOG_RUN_ERR("failed to start lsnr for UDS");
        return status;
    }

    return OG_SUCCESS;
}

void srv_pause_lsnr(lsnr_type_t type)
{
    switch (type) {
        case LSNR_TYPE_SERVICE:
            cs_pause_tcp_lsnr(&g_instance->lsnr.tcp_service);
            break;
        case LSNR_TYPE_REPLICA:
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_pause_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            break;
        case LSNR_TYPE_UDS:
            cs_pause_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
        default:
            cs_pause_tcp_lsnr(&g_instance->lsnr.tcp_service);
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_pause_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            cs_pause_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
    }

    return;
}

void srv_resume_lsnr(lsnr_type_t type)
{
    switch (type) {
        case LSNR_TYPE_SERVICE:
            cs_resume_tcp_lsnr(&g_instance->lsnr.tcp_service);
            break;
        case LSNR_TYPE_REPLICA:
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_resume_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            break;
        case LSNR_TYPE_UDS:
            cs_resume_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
        default:
            cs_resume_tcp_lsnr(&g_instance->lsnr.tcp_service);
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_resume_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            cs_resume_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
    }

    return;
}

void srv_stop_lsnr(lsnr_type_t type)
{
    switch (type) {
        case LSNR_TYPE_SERVICE:
            cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_service);
            break;
        case LSNR_TYPE_REPLICA:
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            break;
        case LSNR_TYPE_UDS:
            cs_stop_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
        default:
            cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_service);
            if (g_instance->lsnr.tcp_replica.port != 0) {
                cs_stop_tcp_lsnr(&g_instance->lsnr.tcp_replica);
            }
            cs_stop_uds_lsnr(&g_instance->lsnr.uds_service);
            break;
    }

    return;
}

#ifdef __cplusplus
}
#endif