【发布时间】:2014-05-01 12:36:07
【问题描述】:
有一个原始套接字的api:
int sz;
ioctlsocket(sock, FIONREAD, &sz);
::recv(sock, buff, sz, 0);
ioctlsocket可以用来获取传入的缓冲区大小,但是如何用openssl获取:
int sz = what?
SSL_read(pSSL, buff, sz);
【问题讨论】:
有一个原始套接字的api:
int sz;
ioctlsocket(sock, FIONREAD, &sz);
::recv(sock, buff, sz, 0);
ioctlsocket可以用来获取传入的缓冲区大小,但是如何用openssl获取:
int sz = what?
SSL_read(pSSL, buff, sz);
【问题讨论】:
但是如何使用 openssl 获取 [传入缓冲区大小]?
OpenSSL 使用BIOs。写缓冲区有一个BIO_get_write_buf_size,但读缓冲区没有相应的控制。您可以使用BIO_set_read_buffer_size 设置读取缓冲区大小。但我相信这会为BIO 设置缓冲区,而不是套接字上的 I/O 缓冲区。请参阅crypto/bio/bss_bio.c 中的struct bio_bio_st 以及BIO_f_buffer(3) 和BIO_s_bio(3) 中的文档。
您应该能够在BIO 上调用BIO_get_fd 以获取底层套接字,然后使用ioctl 或ioctlsocket。您可以在SSL* 中找到BIO。 OpenSSL 方便地具有SSL_get_rbio 和SSL_get_wbio。来自ssl/ssl.h:
struct ssl_st
{
/* protocol version
* (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, DTLS1_VERSION)
*/
int version;
int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */
const SSL_METHOD *method; /* SSLv3 */
/* There are 2 BIO's even though they are normally both the
* same. This is so data can be read and written to different
* handlers */
#ifndef OPENSSL_NO_BIO
BIO *rbio; /* used by SSL_read */
BIO *wbio; /* used by SSL_write */
BIO *bbio; /* used during session-id reuse to concatenate messages */
#else
char *rbio; /* used by SSL_read */
char *wbio; /* used by SSL_write */
char *bbio;
#endif
/* This holds a variable that indicates what we were doing
* when a 0 or -1 is returned. This is needed for
* non-blocking IO so we know what request needs re-doing when
* in SSL_accept or SSL_connect */
int rwstate;
...
};
【讨论】:
感谢 jww 提供代码和标题更正。
这是我的代码
// create ssl connection
ctx = SSL_CTX_new(SSLv23_client_method());
pSSL = SSL_new(ctx);
sbio = BIO_new_socket(raw_socket, BIO_NOCLOSE);
SSL_set_bio(pSSL, sbio, sbio);
SSL_set_fd(pSSL, raw_socket);
SSL_connect(pSSL)
// send
SSL_write(pSSL, s.c_str(), s.length());
// receive
int sock_fd;
int r = BIO_get_fd(sbio, &sock_fd); // <------------------
cout << r << endl; // output -2
甚至使用 BIO_new_socket 和 SSL_set_fd 中使用的 raw_socket
ulong sz = 0;
while(sz == 0) { // <--------- it loops forever, even server echoes data
if(ioctlsocket(raw_socket, FIONREAD, &sz) != 0) {
throw "get_recv_size";
}
Sleep(0);
}
raw_socket和BIO中使用的底层socket是不同的吗?
【讨论】:
OP 本质上是问“传递给 SSL_read 的缓冲区应该有多大?”以 jww 的回答为基础,并且 - 这是关键 - 如果您不介意使用可能在未来版本的 OpenSSL 中更改的内容,那么很容易获得读取缓冲区大小。
注意:这是基于 openssl 1.0.2e
使用 SSL_get_rbio() 获取 jww 描述的 BIO。 BIO 大小存储在 BIO 内的 BIO_F_BUFFER_CTX 中。这可以在文件 crypto/bio/bf_cuff.c 中看到:
static int buffer_new(BIO *bi)
{
BIO_F_BUFFER_CTX *ctx;
ctx = (BIO_F_BUFFER_CTX *)OPENSSL_malloc(sizeof(BIO_F_BUFFER_CTX));
if (ctx == NULL)
return (0);
ctx->ibuf = (char *)OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
if (ctx->ibuf == NULL) {
OPENSSL_free(ctx);
return (0);
}
ctx->obuf = (char *)OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
if (ctx->obuf == NULL) {
OPENSSL_free(ctx->ibuf);
OPENSSL_free(ctx);
return (0);
}
ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
ctx->obuf_size = DEFAULT_BUFFER_SIZE;
ctx->ibuf_len = 0;
ctx->ibuf_off = 0;
ctx->obuf_len = 0;
ctx->obuf_off = 0;
bi->init = 1;
bi->ptr = (char *)ctx;
bi->flags = 0;
return (1);
}
获取读取缓冲区大小可以通过以下方式完成:
((BIO_F_BUFFER_CTX*)bio->ptr)->ibuf_size;
其中bio 是一个代表要查询的BIO 的变量。 ptr 变量需要从 char* 中回滚。
请注意,这适用于任何 BIO,例如从 BIO_new_ssl_connect( ctx ) 返回的那个,等等。
【讨论】: