gpt4 book ai didi

c - OpenSSL DTLS 连接从未建立

转载 作者:行者123 更新时间:2023-11-30 14:37:35 32 4
gpt4 key购买 nike

对于这么大的代码块,我感到很抱歉,但我对发生的事情感到非常困惑,我不知道问题可能出在哪里......

我正在尝试让一个非常小的 dtls 服务器运行,但我无法让客户端和服务器完成握手。

这是我的代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/opensslv.h>

int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
{
cookie = "cookie";
cookie_len = 5;

return 1;
}

int verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
{
return 1;
}

int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) {
/* This function should ask the user
* if he trusts the received certificate.
* Here we always trust.
*/
return 1;
}

int main() {
char buff[FILENAME_MAX];
getcwd(buff, FILENAME_MAX);

union {
struct sockaddr_storage ss;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} client_addr;

struct sockaddr_in server_addr;

const int on = 1, off = 0;

memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));

int res;

SSL *ssl;
BIO *bio;
int sock;
struct timeval timeout;

SSL_CTX *ctx = SSL_CTX_new(DTLS_server_method());
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);

if(!SSL_CTX_use_certificate_file(ctx, "/home/matthew/CLionProjects/OpenSSL.Test/cmake-build-debug/certs/cert.crt", SSL_FILETYPE_PEM))
{
perror("cert");
exit(EXIT_FAILURE);
}

if(!SSL_CTX_use_PrivateKey_file(ctx, "/home/matthew/CLionProjects/OpenSSL.Test/cmake-build-debug/certs/key.key", SSL_FILETYPE_PEM))
{
perror("key");
exit(EXIT_FAILURE);
}

SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, dtls_verify_callback);
SSL_CTX_set_read_ahead(ctx, 1);
SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx, &verify_cookie);

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(1114);

if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
exit(EXIT_FAILURE);
}

if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, (socklen_t) sizeof(on)) < 0)
{
perror("set reuse address");
exit(EXIT_FAILURE);
}

if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*) &on, (socklen_t) sizeof(on)) < 0)
{
perror("set reuse port");
exit(EXIT_FAILURE);
}

if(bind(sock, (const struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
perror("bind");
exit(EXIT_FAILURE);
}

memset(&client_addr, 0, sizeof(struct sockaddr_storage));

/* Create BIO */
bio = BIO_new_dgram(sock, BIO_NOCLOSE);

/* Set and activate timeouts */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);

ssl = SSL_new(ctx);

SSL_set_bio(ssl, bio, bio);
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);

if(!SSL_set_fd(ssl, sock))
{
perror("set fd");
exit(EXIT_FAILURE);
}

res = 0;
while(res <= 0)
{
res = DTLSv1_listen(ssl, (BIO_ADDR *) &client_addr);
if(res < 0)
{
perror("dtls listen"); <--- "Destination address required"
exit(EXIT_FAILURE);
}
}

SSL_accept(ssl);

printf("Hello, World!\n");
return 0;
}

我在我能想到的每个角落和缝隙中添加了错误检查......

发生的情况是,它会很好地循环 DTLSv1_accept() 直到尝试连接。一旦客户端尝试连接(发送 Client Hello),DTLSv1_accept() 返回 -1。对 SSL_get_error 的调用给出了 5,这是一个 SSL_ERROR_SYSCALL

所以我执行perror并获取需要目标地址...

据我所知,DTLSv1_listen 的全部意义在于监听任何传入的客户端问候,并在握手完成后用客户端的地址填充 BIO_ADDR完成...

我用它来测试服务器:

openssl s_client -dtls -connect 127.0.0.1:1114

我在这上面花了很多时间。我即将放弃 OpenSSL 并尝试 libressl...

非常感谢任何帮助。

附注代码已经完成,如果您想尝试一下,它应该可以编译并运行。

谢谢!

最佳答案

您的代码示例中有两个重大问题。

首先,您生成 cookie 的代码不正确。你应该创建cookie并将其存储在cookie指向的位置,然后在*cookie_len中填写cookie的长度。所以代码应该是这样的:

int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
{
memcpy(cookie, "cookie", 6);
*cookie_len = 6;

return 1;
}

但是,在设置要使用的 SSL 对象时,最重要的错误出现了。 OpenSSL 具有用于抽象底层传输层的 BIO 概念。为了通过 UDP 执行 DTLS,您需要使用“dgram”BIO。你正确地做到了这一点:

/* Create BIO */
bio = BIO_new_dgram(sock, BIO_NOCLOSE);

/* Set and activate timeouts */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);

ssl = SSL_new(ctx);

SSL_set_bio(ssl, bio, bio);

因此,此时已为 DTLSv1_listen() 调用正确设置 SSL 对象。但是,您可以这样做:

if(!SSL_set_fd(ssl, sock))
{
perror("set fd");
exit(EXIT_FAILURE);
}

SSL_set_fd 函数是 SSL_set_bio 函数的替代函数。它的作用是获取提供的fd,将其包装在“套接字”BIO 中,然后使用结果调用SSL_set_bio()。套接字 BIO 主要用于标准 TLS,不能用于 DTLS。所以上面代码的作用就是扔掉你之前设置的dgram BIO。

在我的测试中,如果我进行了上面建议的 generate_cookie 更改,并删除了 SSL_set_fd 行,那么一切都会按预期开始工作。

关于c - OpenSSL DTLS 连接从未建立,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57213219/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com