/* Close an active connection.
 *
 * ctx         The SSL/TLS connection data.
 * threadData  The SSL/TLS connection data for the thread.
 * sslConn     The SSL connection data object.
 */
static void SSLConn_Close(SSLConn_CTX* ctx, ThreadData* threadData,
                          SSLConn* sslConn)
{
    int ret;

    if (sslConn->state == CLOSED)
        return;

    pthread_mutex_lock(&sslConnMutex);
    ret = (ctx->numConnections == 0);
    ctx->numConnections++;
    if (wolfSSL_session_reused(sslConn->ssl))
        ctx->numResumed++;
    pthread_mutex_unlock(&sslConnMutex);

    if (ret) {
        WOLFSSL_CIPHER* cipher;
        cipher = wolfSSL_get_current_cipher(sslConn->ssl);
        printf("SSL cipher suite is %s\n", wolfSSL_CIPHER_get_name(cipher));
    }

    sslConn->state = CLOSED;

    /* Take it out of the double-linked list. */
    if (threadData->sslConn == sslConn)
        threadData->sslConn = sslConn->next;
    if (sslConn->next != NULL)
        sslConn->next->prev = sslConn->prev;
    if (sslConn->prev != NULL)
        sslConn->prev->next = sslConn->next;

    /* Put object at head of free list */
    sslConn->next = threadData->freeSSLConn;
    sslConn->prev = NULL;
    threadData->freeSSLConn = sslConn;

    threadData->cnt--;
}
int main()
{
    int sd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);

    if (sd < 0)
        err_sys("sctp socket error");

    struct sockaddr_in sa;
    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons(12345);

    int ret = bind(sd, (struct sockaddr*)&sa, sizeof(sa));
    if (ret < 0)
        err_sys("sctp bind error");

    listen(sd, 3);

    int client_sd = accept(sd, NULL, NULL);
    if (client_sd < 0)
        err_sys("sctp accept error");

    const char* response = "well hello to you";
    char buffer[80];

    WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfDTLSv1_2_server_method());
    if (ctx == NULL)
        err_sys("ctx new dtls server failed");

    ret = wolfSSL_CTX_dtls_set_sctp(ctx);
    if (ret != SSL_SUCCESS)
        err_sys("set sctp mode failed");

    ret = wolfSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM);
    if (ret != SSL_SUCCESS)
        err_sys("use private key error");

    ret = wolfSSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM);
    if (ret != SSL_SUCCESS)
        err_sys("use cert error");

    WOLFSSL* ssl = wolfSSL_new(ctx);
    if (ssl == NULL)
        err_sys("ssl new dtls server failed");

    wolfSSL_set_fd(ssl, client_sd);

    ret = wolfSSL_accept(ssl);
    if (ret != SSL_SUCCESS)
        err_sys("ssl accept failed");

    printf("TLS version is %s\n", wolfSSL_get_version(ssl));
    printf("Cipher Suite is %s\n",
           wolfSSL_CIPHER_get_name(wolfSSL_get_current_cipher(ssl))); 

    int got = wolfSSL_read(ssl, buffer, sizeof(buffer));
    if (got > 0) {
        buffer[got] = 0;
        printf("client said: %s\n", buffer);
    }
    wolfSSL_write(ssl, response, (int)strlen(response));

    unsigned char bigBuf[4096];

    wolfSSL_read(ssl, bigBuf, sizeof(bigBuf));
    wolfSSL_write(ssl, bigBuf, sizeof(bigBuf));

    wolfSSL_shutdown(ssl);
    wolfSSL_free(ssl);
    wolfSSL_CTX_free(ctx);

    close(sd);

    return 0;
}
/* Main entry point for the program.
 *
 * argc  The count of command line arguments.
 * argv  The command line arguments.
 * returns 0 on success and 1 otherwise.
 */
int main(int argc, char* argv[])
{
    socklen_t    socketfd = -1;
    int          ch;
    WOLFSSL_CTX* ctx = NULL;
    SSLConn_CTX* sslConnCtx;
    word16       port = wolfSSLPort;
    int          resumeSession = 0;
    char*        cipherList = NULL;
    char*        ourCert       = CLI_CERT;
    char*        ourKey        = CLI_KEY;
    char*        verifyCert    = CA_CERT;
    int          version       = SERVER_DEFAULT_VERSION;
    int          numConns      = SSL_NUM_CONN;
    int          numBytesRead  = NUM_READ_BYTES;
    int          numBytesWrite = NUM_WRITE_BYTES;
    int          maxBytes      = MAX_BYTES;
    int          maxConns      = MAX_CONNECTIONS;
    int          i;

    /* Parse the command line arguments. */
    while ((ch = mygetopt(argc, argv, OPTIONS)) != -1) {
        switch (ch) {
            /* Help with command line options. */
            case '?':
                Usage();
                exit(EXIT_SUCCESS);

            /* Port number to connect to. */
            case 'p':
                port = (word16)atoi(myoptarg);
                break;

            /* Version of SSL/TLS to use. */
            case 'v':
                version = atoi(myoptarg);
                if (version < 0 || version > 3) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* List of cipher suites to use. */
            case 'l':
                cipherList = myoptarg;
                break;

            /* File name of client certificate for client authentication. */
            case 'c':
                ourCert = myoptarg;
                break;

            /* File name of client private key for client authentication. */
            case 'k':
                ourKey = myoptarg;
                break;

            /* File name of server certificate/CA for peer verification. */
            case 'A':
                verifyCert = myoptarg;
                break;

            /* Resume sessions. */
            case 'r':
                resumeSession = 1;
                break;

            /* Number of connections to make. */
            case 'n':
                maxConns = atoi(myoptarg);
                if (maxConns < 0 || maxConns > 1000000) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                maxBytes = 0;
                break;

            /* Number of conncurrent connections to use. */
            case 'N':
                numConns  = atoi(myoptarg);
                if (numConns < 0 || numConns > 1000000) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Number of bytes to read each call. */
            case 'R':
                numBytesRead = atoi(myoptarg);
                if (numBytesRead <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Number of bytes to write each call. */
            case 'W':
                numBytesWrite = atoi(myoptarg);
                if (numBytesWrite <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                break;

            /* Maximum number of read and write bytes (separate counts). */
            case 'B':
                maxBytes = atoi(myoptarg);
                if (maxBytes <= 0) {
                    Usage();
                    exit(MY_EX_USAGE);
                }
                maxConns = 0;
                break;

            /* Unrecognized command line argument. */
            default:
                Usage();
                exit(MY_EX_USAGE);
        }
    }


#ifdef DEBUG_WOLFSSL
    wolfSSL_Debugging_ON();
#endif

    /* Initialize wolfSSL */
    wolfSSL_Init();

    /* Initialize wolfSSL and create a context object. */
    if (WolfSSLCtx_Init(version, ourCert, ourKey, verifyCert, cipherList, &ctx)
            == EXIT_FAILURE)
        exit(EXIT_FAILURE);

    /* Create SSL/TLS connection data object. */
    sslConnCtx = SSLConn_New(numConns, numBytesRead, numBytesWrite,
                             maxConns, maxBytes, resumeSession);
    if (sslConnCtx == NULL)
        exit(EXIT_FAILURE);

    /* Keep handling connections until all done. */
    for (i = 0; !SSLConn_Done(sslConnCtx); i = (i + 1) % numConns) {
        SSLConn* sslConn = &sslConnCtx->sslConn[i];

        /* Perform close if in CLOSE state. */
        if (sslConn->state == CLOSE) {
            if (sslConnCtx->numConnections == 0) {
                WOLFSSL_CIPHER* cipher;
                cipher = wolfSSL_get_current_cipher(sslConn->ssl);
                printf("SSL cipher suite is %s\n",
                       wolfSSL_CIPHER_get_name(cipher));
            }
            SSLConn_Close(sslConnCtx, sslConn);
        }

        /* Create TCP connection and connect if in INIT state. */
        if ((sslConn->state == INIT) &&
            ((sslConnCtx->maxConnections <= 0) ||
             (sslConnCtx->numCreated < sslConnCtx->maxConnections))) {
            if (CreateSocketConnect(port, &socketfd) == EXIT_FAILURE) {
                printf("ERROR: failed to connect to server\n");
                exit(EXIT_FAILURE);
            }

            SSLConn_Connect(sslConnCtx, ctx, socketfd, sslConn);
        }

#ifdef WOLFSSL_ASYNC_CRYPT
        if (sslConn->err == 4) {
            int ret;
            double start;

            start = current_time(1);
            ret = wolfSSL_AsyncPoll(sslConn->ssl, WOLF_POLL_FLAG_CHECK_HW);
            sslConnCtx->asyncTime += current_time(0) - start;
            if (ret < 0) {
                printf("ERROR: failed in async polling\n");
                break;
            }
            if (ret == 0)
                continue;
        }
        sslConn->err = 0;
#endif

        /* Handle other SSL states. */
        if (sslConnCtx->totalTime == 0)
            sslConnCtx->totalTime = current_time(1);
        if (SSLConn_ReadWrite(sslConnCtx, sslConn) == EXIT_FAILURE) {
            if (sslConnCtx->maxConnections > 0)
                sslConn->state = CLOSE;
        }
    }

    sslConnCtx->totalTime = current_time(0) - sslConnCtx->totalTime;

    SSLConn_PrintStats(sslConnCtx);
    SSLConn_Free(sslConnCtx);

    WolfSSLCtx_Final(ctx);

    wolfSSL_Cleanup();

    exit(EXIT_SUCCESS);
}