如何为BIO_do_connect设置超时?
How to set a timeout for BIO_do_connect?
我找到了一个 SSL/TLS 客户端示例 here,它运行良好。
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/ssl.h>
#include <openssl/conf.h>
void connect(const char* host, int port) {
BIO* sbio, * out;
int len;
char tmpbuf[1024];
SSL_CTX* ctx;
SSL* ssl;
char server[200];
snprintf(server, sizeof(server), "%s:%d", host, port);
/* XXX Seed the PRNG if needed. */
ctx = SSL_CTX_new(TLS_client_method());
/* XXX Set verify paths and mode here. */
sbio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(sbio, &ssl);
if (ssl == NULL) {
fprintf(stderr, "Can't locate SSL pointer\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* Don't want any retries */
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* XXX We might want to do other things with ssl here */
/* An empty host part means the loopback address */
BIO_set_conn_hostname(sbio, server);
out = BIO_new_fp(stdout, BIO_NOCLOSE);
if (BIO_do_connect(sbio) <= 0) {
fprintf(stderr, "Error connecting to server\n");
ERR_print_errors_fp(stderr);
exit(1);
}
int ret = 0;
if ((ret = BIO_do_handshake(sbio)) <= 0) {
fprintf(stderr, "Error establishing SSL connection\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* XXX Could examine ssl here to get connection info */
BIO_puts(sbio, "Hi, this message is from client c++");
for (;;) {
len = BIO_read(sbio, tmpbuf, 1024);
if (len <= 0) {
break;
}
BIO_write(out, tmpbuf, len);
}
BIO_free_all(sbio);
BIO_free(out);
}
int main() {
connect("127.0.0.1", 5555);
}
但我需要为此连接设置超时。然后我找到了How to set connection timeout and operation timeout in OpenSSL。
所以我更改代码
if (BIO_do_connect(sbio) <= 0) {
fprintf(stderr, "Error connecting to server\n");
ERR_print_errors_fp(stderr);
exit(1);
}
到
{
BIO_set_nbio(sbio, 1);
if (1 > BIO_do_connect(sbio)) {
if (!BIO_should_retry(sbio)) {
fprintf(stderr, "Error: should not retry\n");
ERR_print_errors_fp(stderr);
exit(1);
}
int fdSocket = 0;
if (BIO_get_fd(sbio, &fdSocket) < 0) {
fprintf(stderr, "Error: can not get socket\n");
ERR_print_errors_fp(stderr);
exit(1);
}
struct timeval timeout;
fd_set connectionfds;
FD_ZERO(&connectionfds);
FD_SET(fdSocket, &connectionfds);
timeout.tv_usec = 0;
timeout.tv_sec = 4;
if (0 == select(fdSocket + 1, NULL, &connectionfds, NULL, &timeout)) {
fprintf(stderr, "Error: timeout\n");
ERR_print_errors_fp(stderr);
exit(1);
}
}
}
now BIO_do_handshake returns -1 程序退出。
如何为我的 ssl 连接正确设置超时?
请多多指教!帮帮我!
我将分两步进行:
- 我会自己处理连接设置。这样你就可以使用非阻塞套接字,
connect(2)
和 select(2)
并完全控制这部分的时间。
- 我也会用自己的BIO实现。您可以使用现有的 BIO 并仅实现
read
、write
和 puts
方法。这将允许您控制套接字访问。
有了这个,您可以完全控制自己花费的时间。您可以为会话设置、重新协商、正常操作实施不同的超时...
BIO_set_nbio 的问题是您将 I/O 设置为非阻塞模式。所以你必须在非阻塞模式下处理进一步的步骤。
我举例说明了如何使用 sleep
和非阻塞模式处理请求。也许它有点丑。但这对我有用。
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <unistd.h>
#include <stdio.h>
void connect(const char* host, int port) {
const long timeout_nsec = 4 * (long)1000000000, dt_nsec = 100000;
char tmpbuf[1024];
char server[200];
snprintf(server, sizeof(server), "%s:%d", host, port);
struct timespec dt;
dt.tv_sec = 0;
dt.tv_nsec = dt_nsec;
/* XXX Seed the PRNG if needed. */
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
/* XXX Set verify paths and mode here. */
BIO *sbio = BIO_new_ssl_connect(ctx);
SSL* ssl = nullptr;
BIO_get_ssl(sbio, &ssl);
if (ssl == NULL) {
fprintf(stderr, "Can't locate SSL pointer\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* Don't want any retries */
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* XXX We might want to do other things with ssl here */
/* An empty host part means the loopback address */
BIO_set_conn_hostname(sbio, server);
BIO *out = BIO_new_fp(stdout, BIO_NOCLOSE);
BIO_set_nbio(sbio, 1);
{
long time_remained = timeout_nsec;
while(1) {
int res = BIO_do_connect(sbio);
if (res <= 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (res <= 0) {
fprintf(stderr, "BIO_do_connect error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
{
long time_remained = timeout_nsec;
while(1) {
int res = BIO_do_handshake(sbio);
if (res <= 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (res <= 0) {
fprintf(stderr, "BIO_do_handshake error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
/* XXX Could examine ssl here to get connection info */
int a = BIO_puts(sbio, "Hi, this message is from client c++");
for (;;) {
int len = -1;
{
long time_remained = timeout_nsec;
while(1) {
len = BIO_read(sbio, tmpbuf, 1024);
if (len < 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (len < 0) {
fprintf(stderr, "BIO_read error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
if (len == 0) {
break;
}
BIO_write(out, tmpbuf, len);
}
BIO_free_all(sbio);
BIO_free(out);
}
int main() {
connect("127.0.0.1", 5555);
}
我认为你应该为握手设置超时,而不是连接。在您的代码中,连接没有问题,因为“select”返回了非零值。事实上 BIO_do_connect 在连接可用后进行握手。 BIO_do_connect 和 BIO_do_handshake 在头文件中是相同的。
# define BIO_do_connect(b) BIO_do_handshake(b)
所以我认为这个问题是握手问题。例如。您连接到使用不带 ssl 的普通 tcp 套接字的服务器。服务器不会发送“server_hallo”和证书。然后客户端将等待这些“server_hallo”和证书。 BIO_do_handshake returns -1 如果握手进度还没有结束。
也许您可以使用 BIO_set_ssl_renegotiate_timeout 设置超时。
我找到了一个 SSL/TLS 客户端示例 here,它运行良好。
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/ssl.h>
#include <openssl/conf.h>
void connect(const char* host, int port) {
BIO* sbio, * out;
int len;
char tmpbuf[1024];
SSL_CTX* ctx;
SSL* ssl;
char server[200];
snprintf(server, sizeof(server), "%s:%d", host, port);
/* XXX Seed the PRNG if needed. */
ctx = SSL_CTX_new(TLS_client_method());
/* XXX Set verify paths and mode here. */
sbio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(sbio, &ssl);
if (ssl == NULL) {
fprintf(stderr, "Can't locate SSL pointer\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* Don't want any retries */
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* XXX We might want to do other things with ssl here */
/* An empty host part means the loopback address */
BIO_set_conn_hostname(sbio, server);
out = BIO_new_fp(stdout, BIO_NOCLOSE);
if (BIO_do_connect(sbio) <= 0) {
fprintf(stderr, "Error connecting to server\n");
ERR_print_errors_fp(stderr);
exit(1);
}
int ret = 0;
if ((ret = BIO_do_handshake(sbio)) <= 0) {
fprintf(stderr, "Error establishing SSL connection\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* XXX Could examine ssl here to get connection info */
BIO_puts(sbio, "Hi, this message is from client c++");
for (;;) {
len = BIO_read(sbio, tmpbuf, 1024);
if (len <= 0) {
break;
}
BIO_write(out, tmpbuf, len);
}
BIO_free_all(sbio);
BIO_free(out);
}
int main() {
connect("127.0.0.1", 5555);
}
但我需要为此连接设置超时。然后我找到了How to set connection timeout and operation timeout in OpenSSL。 所以我更改代码
if (BIO_do_connect(sbio) <= 0) {
fprintf(stderr, "Error connecting to server\n");
ERR_print_errors_fp(stderr);
exit(1);
}
到
{
BIO_set_nbio(sbio, 1);
if (1 > BIO_do_connect(sbio)) {
if (!BIO_should_retry(sbio)) {
fprintf(stderr, "Error: should not retry\n");
ERR_print_errors_fp(stderr);
exit(1);
}
int fdSocket = 0;
if (BIO_get_fd(sbio, &fdSocket) < 0) {
fprintf(stderr, "Error: can not get socket\n");
ERR_print_errors_fp(stderr);
exit(1);
}
struct timeval timeout;
fd_set connectionfds;
FD_ZERO(&connectionfds);
FD_SET(fdSocket, &connectionfds);
timeout.tv_usec = 0;
timeout.tv_sec = 4;
if (0 == select(fdSocket + 1, NULL, &connectionfds, NULL, &timeout)) {
fprintf(stderr, "Error: timeout\n");
ERR_print_errors_fp(stderr);
exit(1);
}
}
}
now BIO_do_handshake returns -1 程序退出。
如何为我的 ssl 连接正确设置超时?
请多多指教!帮帮我!
我将分两步进行:
- 我会自己处理连接设置。这样你就可以使用非阻塞套接字,
connect(2)
和select(2)
并完全控制这部分的时间。 - 我也会用自己的BIO实现。您可以使用现有的 BIO 并仅实现
read
、write
和puts
方法。这将允许您控制套接字访问。
有了这个,您可以完全控制自己花费的时间。您可以为会话设置、重新协商、正常操作实施不同的超时...
BIO_set_nbio 的问题是您将 I/O 设置为非阻塞模式。所以你必须在非阻塞模式下处理进一步的步骤。
我举例说明了如何使用 sleep
和非阻塞模式处理请求。也许它有点丑。但这对我有用。
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <unistd.h>
#include <stdio.h>
void connect(const char* host, int port) {
const long timeout_nsec = 4 * (long)1000000000, dt_nsec = 100000;
char tmpbuf[1024];
char server[200];
snprintf(server, sizeof(server), "%s:%d", host, port);
struct timespec dt;
dt.tv_sec = 0;
dt.tv_nsec = dt_nsec;
/* XXX Seed the PRNG if needed. */
SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
/* XXX Set verify paths and mode here. */
BIO *sbio = BIO_new_ssl_connect(ctx);
SSL* ssl = nullptr;
BIO_get_ssl(sbio, &ssl);
if (ssl == NULL) {
fprintf(stderr, "Can't locate SSL pointer\n");
ERR_print_errors_fp(stderr);
exit(1);
}
/* Don't want any retries */
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* XXX We might want to do other things with ssl here */
/* An empty host part means the loopback address */
BIO_set_conn_hostname(sbio, server);
BIO *out = BIO_new_fp(stdout, BIO_NOCLOSE);
BIO_set_nbio(sbio, 1);
{
long time_remained = timeout_nsec;
while(1) {
int res = BIO_do_connect(sbio);
if (res <= 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (res <= 0) {
fprintf(stderr, "BIO_do_connect error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
{
long time_remained = timeout_nsec;
while(1) {
int res = BIO_do_handshake(sbio);
if (res <= 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (res <= 0) {
fprintf(stderr, "BIO_do_handshake error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
/* XXX Could examine ssl here to get connection info */
int a = BIO_puts(sbio, "Hi, this message is from client c++");
for (;;) {
int len = -1;
{
long time_remained = timeout_nsec;
while(1) {
len = BIO_read(sbio, tmpbuf, 1024);
if (len < 0 && BIO_should_retry(sbio)) {
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
time_remained -= dt_nsec;
if (time_remained <= 0) {
fprintf(stderr, "Timeout\n");
exit(1);
}
continue;
}
if (len < 0) {
fprintf(stderr, "BIO_read error\n");
ERR_print_errors_fp(stderr);
exit(1);
}
break;
}
}
if (len == 0) {
break;
}
BIO_write(out, tmpbuf, len);
}
BIO_free_all(sbio);
BIO_free(out);
}
int main() {
connect("127.0.0.1", 5555);
}
我认为你应该为握手设置超时,而不是连接。在您的代码中,连接没有问题,因为“select”返回了非零值。事实上 BIO_do_connect 在连接可用后进行握手。 BIO_do_connect 和 BIO_do_handshake 在头文件中是相同的。
# define BIO_do_connect(b) BIO_do_handshake(b)
所以我认为这个问题是握手问题。例如。您连接到使用不带 ssl 的普通 tcp 套接字的服务器。服务器不会发送“server_hallo”和证书。然后客户端将等待这些“server_hallo”和证书。 BIO_do_handshake returns -1 如果握手进度还没有结束。 也许您可以使用 BIO_set_ssl_renegotiate_timeout 设置超时。