#include "v2g_context.h"

#define DEFAULT_TLS_PORT              64109

int v2g_tls_server_init(v2g_context_t* v2g)
{
    if (v2g)
    {
        mlog_d(LOG_TAG_V2G, "Enter...\n");
        // 创建 SSL 上下文
        v2g->ctx = NULL;
        // v2g->ctx = SSL_CTX_new(SSLv23_client_method());
        if (v2g->ctx)
        {
            // 双向验证
            // SSL_VERIFY_PEER---要求对证书进行认证，没有证书也会放行
            // SSL_VERIFY_FAIL_IF_NO_PEER_CERT---要求客户端需要提供证书，但验证发现单独使用没有证书也会放行
            SSL_CTX_set_verify(v2g->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
            // 设置信任根证书
            if (SSL_CTX_load_verify_locations(v2g->ctx, "/home/root/app_run/ca_v2g/ca.crt", NULL) > 0)
            {
                /* 载入用户的数字证书， 此证书用来发送给客户端。 证书里包含有公钥 */
                if (SSL_CTX_use_certificate_file(v2g->ctx, "/home/root/app_run/ca_v2g/client.crt", SSL_FILETYPE_PEM) == 1) 
                {
                    /* 载入用户私钥 */
                    if (SSL_CTX_use_PrivateKey_file(v2g->ctx, "/home/root/app_run/ca_v2g/client.keyp", SSL_FILETYPE_PEM) == 1) 
                    {
                        /* 检查用户私钥是否正确 */
                        if (SSL_CTX_check_private_key(v2g->ctx)) 
                        {
                            v2g->tls_server_quit = 0;
                            if (get_ipv6_address(v2g->if_name, &v2g->local_tls_addr, NULL, 0) == 0)
                            {
                                v2g->local_tls_addr.sin6_port = htons(DEFAULT_TLS_PORT);
                                v2g->tls_socket = create_listen_socket_bind_ipv6(&v2g->local_tls_addr);
                                mlog_d(LOG_TAG_V2G, "tls_socket:%d\n", v2g->tls_socket);
                                mthread_init(&v2g->tls_thread, v2g_tls_server_loop, v2g);
                                mlog_d(LOG_TAG_V2G, "LEAVE...\n");
                                return 0;
                            }
                        }else{
                            printe("ctx(%p)\n", v2g->ctx);
                            SSL_CTX_free(v2g->ctx);
                            v2g->ctx = NULL;
                            return -6;
                        }
                    }else{
                        printe("ctx(%p)\n", v2g->ctx);
                        SSL_CTX_free(v2g->ctx);
                        v2g->ctx = NULL;
                        return -5;
                    }
                }else{
                    printe("ctx(%p)\n", v2g->ctx);
                    SSL_CTX_free(v2g->ctx);
                    v2g->ctx = NULL;
                    return -4;
                }
            } else {
                printe("ctx(%p)\n", v2g->ctx);
                SSL_CTX_free(v2g->ctx);
                v2g->ctx = NULL;
                return -3;
            }
        } else {
            printe("v2g->ctx(%p)\n", v2g->ctx);
            return -2;
        }
    }
    return -1;
}

void v2g_tls_server_exit(v2g_context_t* v2g)
{
    if (v2g)
    {
        if (v2g->tls_server_quit == 0)
        {
            v2g->tls_server_quit = 1;
            mthread_exit(&v2g->tls_thread);
        }
        if (v2g->tls_socket > 0)
        {
            close(v2g->tls_socket);
            v2g->tls_socket = -1;
            memset(&v2g->local_tls_addr, 0, sizeof(struct sockaddr_in6));
        }
        if (v2g->next_client == &v2g->client[1])
        {
            v2g_connect_t* conn = (v2g_connect_t*)&v2g->client[1];
            v2g_tls_client_exit(conn);
            v2g->next_client = NULL;
        }

        if (v2g->ctx)
        {
            SSL_CTX_free(v2g->ctx);
            v2g->ctx = NULL;
        }
    }
}

void* v2g_tls_server_loop(void* args)
{
    v2g_context_t* v2g = (v2g_context_t*)args;
    while(v2g->tls_server_quit == 0)
    {
        struct sockaddr_in6 addr;
        socklen_t addrlen = sizeof(addr);
        char client_addr[INET6_ADDRSTRLEN];
        if (v2g->next_client == NULL)
        {
            v2g_connect_t* conn = (v2g_connect_t*)&v2g->client[1];
            memset(conn, 0, sizeof(v2g_connect_t));
            printd("wait new connection.\n");
            conn->tcp_socket = accept(v2g->tls_socket, (struct sockaddr*)&addr, &addrlen);
            if (conn->tcp_socket > 0)
            {
                if (inet_ntop(AF_INET6, &addr, client_addr, sizeof(client_addr)) != NULL) {
                    mlog_d(LOG_TAG_V2G, "Incoming connection on %s from [%s]:%u\n", v2g->if_name, client_addr, ntohs(addr.sin6_port));
                } else {
                    mlog_e(LOG_TAG_V2G, "Incoming connection on %s, but inet_ntop failed: %s\n", v2g->if_name, strerror(errno));
                }
                conn->ssl = SSL_new(v2g->ctx);
                if (conn->ssl != NULL)
                {
                    SSL_set_fd(conn->ssl, conn->tcp_socket);
                    if (SSL_accept(conn->ssl) > 0) {
                        if (openSSL_check_certificate(conn->ssl) == 0)
                        {
                            v2g_tls_client_init(conn, v2g);
                            v2g->next_client = conn;
                            continue;
                        }
                        mlog_e(LOG_TAG_V2G, "tls client openSSL_check_certificate(%p) failed\n", conn->ssl);
                        SSL_shutdown(conn->ssl);
                    }
                    mlog_e(LOG_TAG_V2G, "tls server SSL_accept(%p) failed\n", conn->ssl);
                    SSL_free(conn->ssl);
                }
                mlog_e(LOG_TAG_V2G, "tls server SSL_new(%p) failed\n", v2g->ctx);
                close(conn->tcp_socket);
                conn->tcp_socket = -1;
            }
        }else{
            sleep(1);
        }
    }
    return NULL;
}