OpenSSL DTLS通信(一)
阅读原文时间:2021年04月20日阅读:1

1. 对于OpenSSL而言,所有的XX_method都是通过类似这样的接口来完成对象的实例化、抽象回调函数的初始化等一些列的操作。如下为源码分析:

IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
                          DTLS_server_method,
                          dtls1_accept,
                          ssl_undefined_function,
                          dtls1_get_server_method, DTLSv1_2_enc_data)

如上宏完成DTLS_server_method函数对应的对象实例化,并将一系列的回调函数以函数指针的方式注册到该对象中。

# define IMPLEMENT_dtls1_meth_func(version, func_name, s_accept, s_connect, \
                                        s_get_meth, enc_data) \
const SSL_METHOD *func_name(void)  \
        { \
        static const SSL_METHOD func_name##_data= { \
                version, \
                dtls1_new, \
                dtls1_clear, \
                dtls1_free, \
                s_accept, \ //该回调完成握手操作
                s_connect, \ //该回调发起主动握手操作(ClientHello请求)
                ssl3_read, \
                ssl3_peek, \
                ssl3_write, \
                dtls1_shutdown, \
                ssl3_renegotiate, \
                ssl3_renegotiate_check, \
                dtls1_get_message, \
                dtls1_read_bytes, \
                dtls1_write_app_data_bytes, \
                dtls1_dispatch_alert, \
                dtls1_ctrl, \
                ssl3_ctx_ctrl, \
                ssl3_get_cipher_by_char, \
                ssl3_put_cipher_by_char, \
                ssl3_pending, \
                ssl3_num_ciphers, \
                dtls1_get_cipher, \
                s_get_meth, \
                dtls1_default_timeout, \
                &enc_data, \
                ssl_undefined_void_function, \
                ssl3_callback_ctrl, \
                ssl3_ctx_callback_ctrl, \
        }; \
        ssl_log(SSL_LOG_VEB, "%s enter …\n", __FUNCTION__); \
        return &func_name##_data; \
        }

-------------------------------------------------------------------------------------------------------------------------------
2. OpenSSL初始化

#include

static void openssl_log_format(int level, const char *file, int line, const char *msg)
{
    int openssl_level = 0;

    switch (level) {
    case SSL_LOG_ERR:
        openssl_level = OPENSSL_LOG_ERR;
        break;
    case SSL_LOG_WAR:
        openssl_level = OPENSSL_LOG_WAR;
        break;
    case SSL_LOG_NOT:
        openssl_level = OPENSSL_LOG_NOT;
        break;
    case SSL_LOG_DEB:
        openssl_level = OPENSSL_LOG_DEB;
        break;
    case SSL_LOG_VEB:
        openssl_level = OPENSSL_LOG_VEB;
        break;
    default:
        openssl_level = OPENSSL_LOG_DEB;
        break;
    }

    openssl_log(openssl_level, "[%s:%04d] %s", file, line, msg);
}

void openssl_log_init(void)
{
    ssl_set_logger_cb(openssl_log_format);
}

void openssl_log_vsprintf(int level, const char *file, int line, const char *format, …)
{
    char *log_buffer = NULL;
    char *file_name = NULL;
    char *level_str = NULL;
    struct timeval tv_now;
    time_t tt;
    struct tm *t = NULL;

    va_list ap;
    va_start(ap, format);
    int ret = vasprintf(&log_buffer, format, ap);
    va_end(ap);

    if (file) {
        file_name = strrchr(file, '/');
    }

    switch (level) {
    case OPENSSL_LOG_ERR:
        level_str = "\033[31;1m  ERROR\33[0m";
        break;
    case OPENSSL_LOG_WAR:
        level_str = "\033[32;31;1mWARRING\33[0m";
        break;
    case OPENSSL_LOG_NOT:
        level_str = "\033[33;1m NOTICE\33[0m";
        break;
    case OPENSSL_LOG_DEB:
        level_str = "\033[32;1m  DEBUG\33[0m";
        break;
    case OPENSSL_LOG_VEB:
        level_str = "\033[32mVERBOSE\33[0m";
        break;
    default:
        level_str = "\033[32;1m  DEBUG\33[0m";
        break;
    }

    tt = time(NULL);
    t = localtime(&tt);
    gettimeofday(&tv_now, NULL);

    fprintf(stderr, "[%4d-%02d-%02d %02d:%02d:%02d:%03ld] %s [%05ld] -- %s:%d %s\n",
        t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv_now.tv_usec,
        level_str, syscall(SYS_gettid), file_name ? ++file_name : file, line, log_buffer);

    if (log_buffer) {
        free(log_buffer);
    }
}

static void openssl_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
{
    const char *str_write_p, *str_version, *str_content_type =
        "", *str_details1 = "", *str_details2 = "";

    char msg_buffer[65535] = {0};
    int msg_len = 0;

    str_write_p = write_p ? ">>>" : "<<<";

    switch (version) {
    case SSL2_VERSION:
        str_version = "SSL 2.0";
        break;
    case SSL3_VERSION:
        str_version = "SSL 3.0 ";
        break;
    case TLS1_VERSION:
        str_version = "TLS 1.0 ";
        break;
    case TLS1_1_VERSION:
        str_version = "TLS 1.1 ";
        break;
    case TLS1_2_VERSION:
        str_version = "TLS 1.2 ";
        break;
    case DTLS1_VERSION:
        str_version = "DTLS 1.0 ";
        break;
    case DTLS1_BAD_VER:
        str_version = "DTLS 1.0 (bad) ";
        break;
    default:
        str_version = "???";
    }

    if (version == SSL2_VERSION) {
        str_details1 = "???";

        if (len > 0) {
            switch (((const unsigned char *)buf)[0]) {
            case 0:
                str_details1 = ", ERROR:";
                str_details2 = " ???";
                if (len >= 3) {
                    unsigned err =
                        (((const unsigned char *)buf)[1] << 8) +
                        ((const unsigned char *)buf)[2];

                    switch (err) {
                    case 0x0001:
                        str_details2 = " NO-CIPHER-ERROR";
                        break;
                    case 0x0002:
                        str_details2 = " NO-CERTIFICATE-ERROR";
                        break;
                    case 0x0004:
                        str_details2 = " BAD-CERTIFICATE-ERROR";
                        break;
                    case 0x0006:
                        str_details2 = " UNSUPPORTED-CERTIFICATE-TYPE-ERROR";
                        break;
                    }
                }

                break;
            case 1:
                str_details1 = ", CLIENT-HELLO";
                break;
            case 2:
                str_details1 = ", CLIENT-MASTER-KEY";
                break;
            case 3:
                str_details1 = ", CLIENT-FINISHED";
                break;
            case 4:
                str_details1 = ", SERVER-HELLO";
                break;
            case 5:
                str_details1 = ", SERVER-VERIFY";
                break;
            case 6:
                str_details1 = ", SERVER-FINISHED";
                break;
            case 7:
                str_details1 = ", REQUEST-CERTIFICATE";
                break;
            case 8:
                str_details1 = ", CLIENT-CERTIFICATE";
                break;
            }
        }
    }

    if (version == SSL3_VERSION ||
        version == TLS1_VERSION ||
        version == TLS1_1_VERSION ||
        version == TLS1_2_VERSION ||
        version == DTLS1_VERSION ||
        version == DTLS1_2_VERSION ||
        version == DTLS1_BAD_VER) {
        switch (content_type) {
        case 20:
            str_content_type = "ChangeCipherSpec";
            break;
        case 21:
            str_content_type = "Alert";
            break;
        case 22:
            str_content_type = "Handshake";
            break;
        }

        if (content_type == 21) { /* Alert */
            str_details1 = ", ???";

            if (len == 2) {
                switch (((const unsigned char *)buf)[0]) {
                case 1:
                    str_details1 = ", warning";
                    break;
                case 2:
                    str_details1 = ", fatal";
                    break;
                }

                str_details2 = " ???";
                switch (((const unsigned char *)buf)[1]) {
                case 0:
                    str_details2 = " close_notify";
                    break;
                case 10:
                    str_details2 = " unexpected_message";
                    break;
                case 20:
                    str_details2 = " bad_record_mac";
                    break;
                case 21:
                    str_details2 = " decryption_failed";
                    break;
                case 22:
                    str_details2 = " record_overflow";
                    break;
                case 30:
                    str_details2 = " decompression_failure";
                    break;
                case 40:
                    str_details2 = " handshake_failure";
                    break;
                case 42:
                    str_details2 = " bad_certificate";
                    break;
                case 43:
                    str_details2 = " unsupported_certificate";
                    break;
                case 44:
                    str_details2 = " certificate_revoked";
                    break;
                case 45:
                    str_details2 = " certificate_expired";
                    break;
                case 46:
                    str_details2 = " certificate_unknown";
                    break;
                case 47:
                    str_details2 = " illegal_parameter";
                    break;
                case 48:
                    str_details2 = " unknown_ca";
                    break;
                case 49:
                    str_details2 = " access_denied";
                    break;
                case 50:
                    str_details2 = " decode_error";
                    break;
                case 51:
                    str_details2 = " decrypt_error";
                    break;
                case 60:
                    str_details2 = " export_restriction";
                    break;
                case 70:
                    str_details2 = " protocol_version";
                    break;
                case 71:
                    str_details2 = " insufficient_security";
                    break;
                case 80:
                    str_details2 = " internal_error";
                    break;
                case 90:
                    str_details2 = " user_canceled";
                    break;
                case 100:
                    str_details2 = " no_renegotiation";
                    break;
                case 110:
                    str_details2 = " unsupported_extension";
                    break;
                case 111:
                    str_details2 = " certificate_unobtainable";
                    break;
                case 112:
                    str_details2 = " unrecognized_name";
                    break;
                case 113:
                    str_details2 = " bad_certificate_status_response";
                    break;
                case 114:
                    str_details2 = " bad_certificate_hash_value";
                    break;
                case 115:
                    str_details2 = " unknown_psk_identity";
                    break;
                }
            }
        }

        if (content_type == 22) { /* Handshake */
            str_details1 = "???";

            if (len > 0) {
                switch (((const unsigned char *)buf)[0]) {
                case 0:
                    str_details1 = ", HelloRequest";
                    break;
                case 1:
                    str_details1 = ", ClientHello";
                    break;
                case 2:
                    str_details1 = ", ServerHello";
                    break;
                case 3:
                    str_details1 = ", HelloVerifyRequest";
                    break;
                case 11:
                    str_details1 = ", Certificate";
                    break;
                case 12:
                    str_details1 = ", ServerKeyExchange";
                    break;
                case 13:
                    str_details1 = ", CertificateRequest";
                    break;
                case 14:
                    str_details1 = ", ServerHelloDone";
                    break;
                case 15:
                    str_details1 = ", CertificateVerify";
                    break;
                case 16:
                    str_details1 = ", ClientKeyExchange";
                    break;
                case 20:
                    str_details1 = ", Finished";
                    break;
                }
            }
        }

#ifndef OPENSSL_NO_HEARTBEATS
        if (content_type == 24) { /* Heartbeat */
            str_details1 = ", Heartbeat";

            if (len > 0) {
                switch (((const unsigned char *)buf)[0]) {
                case 1:
                    str_details1 = ", HeartbeatRequest";
                    break;
                case 2:
                    str_details1 = ", HeartbeatResponse";
                    break;
                }
            }
        }
#endif
    }

    msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1,
        "%s %s%s [length %04lx]%s%s\n", str_write_p, str_version,
        str_content_type, (unsigned long)len, str_details1, str_details2);

    if (len > 0) {
        size_t num, i;
        msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", "   ");
        num = len;

        for (i = 0; i < num; i++) {             if (i % 32 == 0 && i > 0) {
                msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", "\n   ");
            }
            msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, " %02x", ((const unsigned char *)buf)[i]);
        }

        if (i < len) {

            msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", " …");
        }

        msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", "\n");
    }

    openssl_log(OPENSSL_LOG_DEB, "\n%s", msg_buffer);
}

int openssl_init(void)
{
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
}

#define SERVER_CA   "ServerCAcert.pem"
#define SERVER_KEY  "ServerPrivkey.pem"

#define CLIENT_CA   "ClientCAcert.pem"
#define CLIENT_KEY  "ClientPrivkey.pem"

int openssl_load_cert_file(SSL_CTX *ctx, int csopt)
{
    if (csopt) {
        if (SSL_CTX_use_certificate_file(ctx, OPENSSL_SERVER_CA_PATH "/" SERVER_CA, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stdout);
            return -1;
        }

        if (SSL_CTX_use_PrivateKey_file(ctx, OPENSSL_SERVER_CA_PATH "/" SERVER_KEY, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stdout);
            return -1;
        }
    } else {
        if (SSL_CTX_use_certificate_file(ctx, OPENSSL_CLIENT_CA_PATH "/" CLIENT_CA, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stdout);
            return -1;
        }

        if (SSL_CTX_use_PrivateKey_file(ctx, OPENSSL_CLIENT_CA_PATH "/" CLIENT_KEY, SSL_FILETYPE_PEM) <= 0) {
            ERR_print_errors_fp(stdout);
            return -1;
        }
    }

    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stdout);
        return -1;
    }

    return 0;
}

static void openssl_info_callback(const SSL *s, int where, int ret)
{
    const char *str;
    int w;

    w = where & ~SSL_ST_MASK;

    if (w & SSL_ST_CONNECT) {
        str = "SSL_connect";
    } else if (w & SSL_ST_ACCEPT) {
        str = "SSL_accept";
    } else {
        str = "undefined";
    }

    if (where & SSL_CB_LOOP) {
        openssl_log(SSL_LOG_DEB, "%s: %s\n", str, SSL_state_string_long(s));
    } else if (where & SSL_CB_ALERT) {
        str = (where & SSL_CB_READ) ? "read" : "write";
        openssl_log(SSL_LOG_DEB, "SSL3 alert %s:%s:%s\n", str,
                   SSL_alert_type_string_long(ret),
                   SSL_alert_desc_string_long(ret));
    } else if (where & SSL_CB_EXIT) {
        if (ret == 0) {
            openssl_log(SSL_LOG_ERR, "%s:failed in %s\n", str, SSL_state_string_long(s));
        } else if (ret < 0) {
            openssl_log(SSL_LOG_ERR, "%s:error in %s\n", str, SSL_state_string_long(s));
        }
    }
}

SSL_CTX *openssl_ctx_new(const SSL_METHOD *method)
{
    SSL_CTX *ctx = NULL;

    ctx = SSL_CTX_new(method);
    if (NULL == ctx) {
        ERR_print_errors_fp(stdout);
        return NULL;
    }

    SSL_CTX_set_info_callback(ctx, openssl_info_callback);

    return ctx;
}

SSL *openssl_ssl_new(SSL_CTX *ctx)
{
    SSL *ssl = NULL;

    ssl = SSL_new(ctx);
    if (NULL == ssl) {
        return NULL;
    }

    SSL_set_msg_callback(ssl, openssl_msg_cb);
    SSL_set_msg_callback_arg(ssl, NULL);

    return ssl;
}

int openssl_set_fd(SSL *ssl, int sockfd)
{
    return SSL_set_fd(ssl, sockfd);
}

int openssl_accept(SSL *ssl)
{
    return SSL_accept(ssl);
}

-------------------------------------------------------------------------------------------------------------------------------

3. Socket初始化

#include
#include
#include
#include
#include
#include
#include

#include
#include

int init_nonblock(int fd)
{
    int flags = -1;

    if(flags = fcntl(fd, F_GETFL, 0) < 0) {
        openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
        return -1;
    }

    flags |= O_NONBLOCK;
    if(fcntl(fd, F_SETFL, flags) < 0) {
        openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
        return -1;
    }

    return 0;
}

int init_sock(inet_sock_t sock, int type)
{
    return socket(sock, type, 0);
}

int init_sockaddr(struct sockaddr *sockaddr, inet_sock_t sock, int sockfd, int utopt, int csopt)
{
    struct sockaddr_in addr;
    int on = 1;

    memset(&addr, 0, sizeof(addr));

    addr.sin_family = sock;

    if (csopt) {
        addr.sin_port = htons(7838);
        addr.sin_addr.s_addr = INADDR_ANY;
    } else {
        addr.sin_port = htons(7838);
        if (!inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr.s_addr)) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }
    }

    if (sockaddr) {
        memcpy(sockaddr, &addr, sizeof(addr));
    }

    if (csopt) {
        if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }

        if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }
        openssl_log(OPENSSL_LOG_NOT, "Bind sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    }

    if (utopt) {
        if (listen(sockfd, 100)) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }
    } else {
        if (!csopt) {
            if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr))) {
                openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
                return -1;
            }
            openssl_log(OPENSSL_LOG_NOT, "Connect sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
        }
    }

    return 0;
}

int init_udp_sockaddr(struct sockaddr *sockaddr, inet_sock_t sock, int sockfd, int csopt)
{
    struct sockaddr_in addr;
    int on = 1;

    memset(&addr, 0, sizeof(addr));

    addr.sin_family = sock;

    if (csopt) {
        addr.sin_port = htons(7838);
        addr.sin_addr.s_addr = INADDR_ANY;
    } else {
        addr.sin_port = htons(7838);
        if (!inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr.s_addr)) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }
    }

    if (sockaddr) {
        memcpy(sockaddr, &addr, sizeof(addr));
    }

    if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
        openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
        return -1;
    }

    if (csopt) {
        if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            return -1;
        }
        openssl_log(OPENSSL_LOG_NOT, "Bind sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    }

    return 0;
}

int init_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
    return accept(sockfd, addr, addrlen);
}-------------------------------------------------------------------------------------------------------------------------------

4. OpenSSL DTLS Client端

#include
#include

int main(int argc, char **argv)
{
    SSL_CTX * ctx = NULL;
    int sockfd = -1;
    socklen_t socklen = 0;
    struct sockaddr_in their_addr;
    struct sockaddr peer;
    SSL *ssl = NULL;
    int retval = -1;
    char readbuf[65535] = {0};
    BIO *sbio = NULL;
    int try = 0;

    openssl_log_init();
    openssl_init();

    ctx = openssl_ctx_new(DTLS_method());
    if (NULL == ctx) {
        goto error;
    }

    if (openssl_load_cert_file(ctx, 0)) {
        goto error;
    }

    sockfd = init_sock(SOCK_AF_INET, SOCK_DGRAM);
    if (sockfd < 0) {
        goto error;
    }

    if (init_sockaddr((struct sockaddr *)&their_addr, SOCK_AF_INET, sockfd, 0, 0)) {
        goto error;
    }

    ssl = openssl_ssl_new(ctx);
    if (NULL == ssl) {
        goto error;
    }

    sbio = BIO_new_dgram(sockfd, BIO_NOCLOSE);
    if (NULL == sbio) {
        goto error;
    }

    BIO_ctrl_set_connected(sbio, 1, &peer);
    SSL_set_bio(ssl, sbio, sbio);
    SSL_set_connect_state(ssl);

    while (try++ < 10) {         snprintf(readbuf, sizeof(readbuf) - 1, "%s", "This is a DTLS client!\n");         retval = SSL_write(ssl, readbuf, strlen(readbuf));         if (retval > 0) {
            openssl_log(OPENSSL_LOG_DEB, "Write '%d' bytes to '%s:%d'\n", retval, inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
            sleep(1);
        } else {
            openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
            sleep(1);
        }
    }

    return 0;

error:
    if (sockfd > 0) {
        close(sockfd);
    }

    if (ctx) {
        SSL_CTX_free(ctx);
    }
}

-------------------------------------------------------------------------------------------------------------------------------

5. OpenSSL DTLS Server端

#include
#include
#include

#include
#include

int main(int argc, char **argv)
{
    SSL_CTX * ctx = NULL;
    int sockfd = -1;
    socklen_t socklen = 0;
    struct sockaddr_in my_addr, their_addr;
    SSL *ssl = NULL;
    int retval = -1;
    char readbuf[65535] = {0};
    BIO * sbio = NULL;
    int read_from_sslcon = -1;
    struct pollfd fds = {0};
    struct timeval timeout;

    openssl_log_init();
    openssl_init();

    ctx = openssl_ctx_new(DTLS_server_method());
    if (NULL == ctx) {
        goto error;
    }

    if (openssl_load_cert_file(ctx, 1)) {
        goto error;
    }

    sockfd = init_sock(SOCK_AF_INET, SOCK_DGRAM);
    if (sockfd < 0) {
        goto error;
    }

    if (init_sockaddr((struct sockaddr *)&my_addr, SOCK_AF_INET, sockfd, 0, 1)) {
        goto error;
    }

    ssl = openssl_ssl_new(ctx);
    if (NULL == ssl) {
        goto error;
    }

    sbio = BIO_new_dgram(sockfd, BIO_NOCLOSE);
    if (NULL == sbio) {
        goto error;
    }

    timeout.tv_sec = 0;
    timeout.tv_usec = 250000;
    BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);

    timeout.tv_sec = 0;
    timeout.tv_usec = 250000;
    BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);

    SSL_set_bio(ssl, sbio, sbio);
    SSL_set_accept_state(ssl);

    while (1) {
        memset(&fds, 0, sizeof(fds));
        fds.fd = sockfd;
        fds.events = POLLIN;

        retval = poll(&fds, 1, 10000);

        if (retval && (fds.revents & POLLIN)) {
            read_from_sslcon = SSL_read(ssl, readbuf, sizeof(readbuf) - 1);
            if (read_from_sslcon > 0) {
                if (!SSL_is_init_finished(ssl)) {
                    openssl_log(OPENSSL_LOG_WAR, "SSL init failure …\n");
                    continue;
                }

                BIO_dgram_get_peer(sbio, &my_addr);

                openssl_log(OPENSSL_LOG_DEB, "Read %d bytes from '%s:%d', %s\n", read_from_sslcon,
                    inet_ntoa(my_addr.sin_addr), ntohs(my_addr.sin_port), readbuf);
            }
        }
    }

    return 0;

error:
    if (sockfd > 0) {
        close(sockfd);
    }

    if (ctx) {
        SSL_CTX_free(ctx);
    }
}

-------------------------------------------------------------------------------------------------------------------------------