OpenSSL 的 DTLSv1_Listen() 在运行时暂停程序

OpenSSL's DTLSv1_Listen() pauses program at runtime

我正在尝试通过创建一个程序来测试 OpenSSL DTLS,该程序创建一个客户端和服务器套接字以在套接字之间回显字符串;但是,当我尝试测试 DTLSv1_Listen() 函数时,即使我没有尝试在套接字之间连接或发送数据,我的程序似乎也会暂停。注意:我使用的是 post 1.0.2 OpenSSL,它是 DTLSv1_Listen() 被重写后的版本。

这是我完整的 C++ winsock 特定代码:

#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
//#include <openssl/applink.c>
#include <string>
#pragma comment(lib, "Ws2_32.lib")


struct DTLSStuff { //struct to contain DTLS object instances
    SSL_CTX *ctx;
    SSL *ssl;
    BIO *bio;
};

void DTLSErr() { //DTLS error reporting
    ERR_print_errors_fp(stderr);
    exit(1);
}

int newSocket(sockaddr_in addr) { //creates a socket and returns the file descriptor //TODO expand for multi-platform
    WSADATA wsaData;
    int fd;
    int iResult;

    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);                                                     //Initialize Winsock
    if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); exit(1); }

    fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { perror("Unable to create socket"); exit(1); }    //create socket
    printf("New Socket: %i\n", fd);
    if (bind(fd, (struct sockaddr *)&addr, sizeof(sockaddr)) < 0) { printf("bind failed with error %u\n", WSAGetLastError());   exit(1); }

    return fd;                                                                                          //file descriptor
}

void InitCTX(SSL_CTX *ctx, bool IsClient) { //Takes a ctx object and initializes it for DTLS communication
    if (IsClient) {
        if(SSL_CTX_use_certificate_chain_file(ctx, "client-cert.pem") < 0) { printf("Failed loading client cert");}
        if(SSL_CTX_use_PrivateKey_file(ctx, "client-key.pem", SSL_FILETYPE_PEM) < 0) { printf("Failed loading client key"); }
    }
    else {
        if (SSL_CTX_use_certificate_chain_file(ctx, "server-cert.pem") < 0) { printf("Failed loading client cert"); }
        if (SSL_CTX_use_PrivateKey_file(ctx, "server-key.pem", SSL_FILETYPE_PEM) < 0) { printf("Failed loading client key"); }
    }
    //SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cert);    //omitted for testing
    //SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);     //omitted for testing
    //SSL_CTX_set_cookie_verify_cb(ctx, verify_cookie);         //omitted for testing
    SSL_CTX_set_read_ahead(ctx, 1);
}

int main() { //creates client and server sockets and DTLS objects. TODO: have client complete handshake with server socket and send a message and have the server echo it back to client socket
    BIO_ADDR *faux_addr = BIO_ADDR_new(); // for DTLSv1_listen(), since we are this is both client and server (meaning client address is known) it is only used to satisfy parameters.
    ERR_load_BIO_strings();
    SSL_load_error_strings();   
    SSL_library_init();         

    //Set up addresses
    sockaddr_in client_addr;
    client_addr.sin_family = AF_INET;
    client_addr.sin_port = htons(25501);
    client_addr.sin_addr.s_addr = INADDR_ANY;
    sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(25500);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    //*********CLIENT
    DTLSStuff ClientInf;
    ClientInf.ctx = SSL_CTX_new(DTLSv1_client_method());
    InitCTX(ClientInf.ctx,true);
    int ClientFD = newSocket(client_addr); 
    ClientInf.bio = BIO_new_dgram(ClientFD, BIO_NOCLOSE);
    ClientInf.ssl = SSL_new(ClientInf.ctx);
    //SSL_set_options(ClientInf.ssl, SSL_OP_COOKIE_EXCHANGE); //omitted for testing
    SSL_set_bio(ClientInf.ssl, ClientInf.bio, ClientInf.bio);

    //*********SERVER
    DTLSStuff ServerInf;
    ServerInf.ctx = SSL_CTX_new(DTLSv1_server_method());
    InitCTX(ServerInf.ctx,false);
    int ServerFD = newSocket(server_addr); 
    ServerInf.bio = BIO_new_dgram(ServerFD, BIO_NOCLOSE);
    ServerInf.ssl = SSL_new(ServerInf.ctx);
    //SSL_set_options(ServerInf.ssl, SSL_OP_COOKIE_EXCHANGE); //omitted for testing
    SSL_set_bio(ServerInf.ssl, ServerInf.bio, ServerInf.bio);

    printf("Listen attempt...\n");  
    int ret = DTLSv1_listen(ServerInf.ssl, faux_addr);
    if (ret < 0) { DTLSErr(); }
    printf("this print should occur, but it never does");
    exit(1);
}

我希望结果如下:

NewSocket: 356
NewSocket: 360
Listen attempt...
this print should occur but it never does

但是当 运行 程序从不打印最后一行时。该程序似乎可以响应,因为我可以通过 ctrl+c 取消可执行文件,所以我假设它没有崩溃或冻结,但除此之外我不知所措。我的理解是,如果没有任何反应,该方法应该 return 0,如果听到 clienthello,则应为 >1,如果发生错误,则应为 <0。

此外,还有一个有点相关的问题:由于 DTLSv1_Listen() 需要 BIO_ADDR 来存储传入的请求地址,这是否意味着单独的客户端和服务器程序都需要 2 个套接字(如果需要)能够发送和收听?通常 UDP 客户端和服务器只需要一个套接字,但我似乎无法想出一种设计来保留它与 OpenSSL 的 DTLS。

感谢您的宝贵时间。

我在您的代码中没有看到您将套接字设置为非阻塞的任何地方。在默认的阻塞模式下,当您尝试从套接字读取时,您的程序将暂停,直到数据到达。如果你不想那样,那么确保你设置了适当的选项(我不是 Windows 程序员,但 ioctlsocket 似乎可以完成这项工作:https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573(v=vs.85).aspx

does that mean that separate client and servers programs will both require 2 sockets if they want to be able to both send and listen

当使用 DTLSv1_listen() 时,您正在使用处于未连接状态的套接字,因此您可能会收到来自多个客户端的 UDP 数据包。 DTLS 是基于连接的,所以一旦 DTLSv1_listen() returns 成功,你应该创建一个 "connected" 套接字到客户端地址。因此,您有一个用于侦听新连接的套接字,每个与您的服务器通信的客户端都有一个套接字。