libfreerdp-core: replace all OpenSSL built-in BIOs by new full duplex BIOs
This commit is contained in:
parent
1172596d59
commit
04968b18c4
@ -148,7 +148,7 @@ set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE winpr
|
||||
MODULES winpr-registry winpr-utils winpr-interlocked winpr-dsparse winpr-sspi winpr-rpc winpr-wtsapi winpr-handle winpr-crt)
|
||||
MODULES winpr-registry winpr-utils winpr-interlocked winpr-dsparse winpr-sspi winpr-rpc winpr-wtsapi winpr-handle winpr-winsock winpr-crt)
|
||||
|
||||
if(MONOLITHIC_BUILD)
|
||||
set(FREERDP_LIBS ${FREERDP_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
|
||||
|
@ -319,8 +319,7 @@ int rpc_out_read(rdpRpc* rpc, BYTE* data, int length)
|
||||
int status;
|
||||
|
||||
status = BIO_read(rpc->TlsOut->bio, data, length);
|
||||
/* fprintf(stderr, "%s: length=%d => status=%d shouldRetry=%d\n", __FUNCTION__, length,
|
||||
* status, BIO_should_retry(rpc->TlsOut->bio)); */
|
||||
|
||||
if (status > 0) {
|
||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||
VALGRIND_MAKE_MEM_DEFINED(data, status);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/winsock.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h>
|
||||
@ -55,9 +56,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#define close(_fd) closesocket(_fd)
|
||||
#endif
|
||||
|
||||
#include <freerdp/utils/tcp.h>
|
||||
@ -66,6 +64,185 @@
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
/* Simple Socket BIO */
|
||||
|
||||
static int transport_bio_simple_new(BIO* bio);
|
||||
static int transport_bio_simple_free(BIO* bio);
|
||||
|
||||
long transport_bio_simple_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_write(BIO* bio, const char* buf, int size)
|
||||
{
|
||||
int error;
|
||||
int status = 0;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
|
||||
|
||||
status = _send((SOCKET) bio->num, buf, size, 0);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
error = WSAGetLastError();
|
||||
|
||||
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) ||
|
||||
(error == WSAEINPROGRESS) || (error == WSAEALREADY))
|
||||
{
|
||||
BIO_set_flags(bio, (BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY));
|
||||
}
|
||||
else
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_read(BIO* bio, char* buf, int size)
|
||||
{
|
||||
int error;
|
||||
int status = 0;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
BIO_clear_flags(bio, BIO_FLAGS_READ);
|
||||
|
||||
status = _recv((SOCKET) bio->num, buf, size, 0);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
error = WSAGetLastError();
|
||||
|
||||
if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) ||
|
||||
(error == WSAEINPROGRESS) || (error == WSAEALREADY))
|
||||
{
|
||||
BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY));
|
||||
}
|
||||
else
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_puts(BIO* bio, const char* str)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_gets(BIO* bio, char* str, int size)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long transport_bio_simple_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
|
||||
{
|
||||
int status = -1;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case BIO_C_SET_FD:
|
||||
if (arg2)
|
||||
{
|
||||
transport_bio_simple_free(bio);
|
||||
bio->flags = BIO_FLAGS_SHOULD_RETRY;
|
||||
bio->num = *((int*) arg2);
|
||||
bio->shutdown = (int) arg1;
|
||||
bio->init = 1;
|
||||
status = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIO_C_GET_FD:
|
||||
if (bio->init)
|
||||
{
|
||||
if (arg2)
|
||||
*((int*) arg2) = bio->num;
|
||||
status = bio->num;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
status = bio->shutdown;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
bio->shutdown = (int) arg1;
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_DUP:
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_FLUSH:
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_new(BIO* bio)
|
||||
{
|
||||
bio->init = 0;
|
||||
bio->num = 0;
|
||||
bio->ptr = NULL;
|
||||
bio->flags = BIO_FLAGS_SHOULD_RETRY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int transport_bio_simple_free(BIO* bio)
|
||||
{
|
||||
if (!bio)
|
||||
return 0;
|
||||
|
||||
if (bio->shutdown)
|
||||
{
|
||||
if (bio->init)
|
||||
closesocket((SOCKET) bio->num);
|
||||
|
||||
bio->init = 0;
|
||||
bio->flags = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static BIO_METHOD transport_bio_simple_socket_methods =
|
||||
{
|
||||
BIO_TYPE_SIMPLE,
|
||||
"SimpleSocket",
|
||||
transport_bio_simple_write,
|
||||
transport_bio_simple_read,
|
||||
transport_bio_simple_puts,
|
||||
transport_bio_simple_gets,
|
||||
transport_bio_simple_ctrl,
|
||||
transport_bio_simple_new,
|
||||
transport_bio_simple_free,
|
||||
NULL,
|
||||
};
|
||||
|
||||
BIO_METHOD* BIO_s_simple_socket(void)
|
||||
{
|
||||
return &transport_bio_simple_socket_methods;
|
||||
}
|
||||
|
||||
/* Buffered Socket BIO */
|
||||
|
||||
long transport_bio_buffered_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
|
||||
{
|
||||
return 1;
|
||||
@ -80,7 +257,7 @@ static int transport_bio_buffered_write(BIO* bio, const char* buf, int num)
|
||||
|
||||
ret = num;
|
||||
tcp->writeBlocked = FALSE;
|
||||
BIO_clear_retry_flags(bio);
|
||||
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
|
||||
|
||||
/* we directly append extra bytes in the xmit buffer, this could be prevented
|
||||
* but for now it makes the code more simple.
|
||||
@ -99,21 +276,22 @@ static int transport_bio_buffered_write(BIO* bio, const char* buf, int num)
|
||||
while (chunks[i].size)
|
||||
{
|
||||
status = BIO_write(bio->next_bio, chunks[i].data, chunks[i].size);
|
||||
/*fprintf(stderr, "%s: i=%d/%d size=%d/%d status=%d retry=%d\n", __FUNCTION__, i, nchunks,
|
||||
chunks[i].size, ringbuffer_used(&tcp->xmitBuffer), status,
|
||||
BIO_should_retry(bio->next_bio)
|
||||
);*/
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
if (BIO_should_retry(bio->next_bio))
|
||||
if (!BIO_should_retry(bio->next_bio))
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
ret = -1; /* fatal error */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (BIO_should_write(bio->next_bio))
|
||||
{
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE);
|
||||
tcp->writeBlocked = TRUE;
|
||||
goto out; /* EWOULDBLOCK */
|
||||
}
|
||||
|
||||
/* any other is an error, but we still have to commit written bytes */
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
committedBytes += status;
|
||||
@ -133,17 +311,30 @@ static int transport_bio_buffered_read(BIO* bio, char* buf, int size)
|
||||
rdpTcp* tcp = (rdpTcp*) bio->ptr;
|
||||
|
||||
tcp->readBlocked = FALSE;
|
||||
BIO_clear_retry_flags(bio);
|
||||
BIO_clear_flags(bio, BIO_FLAGS_READ);
|
||||
|
||||
status = BIO_read(bio->next_bio, buf, size);
|
||||
/*fprintf(stderr, "%s: size=%d status=%d shouldRetry=%d\n", __FUNCTION__, size, status, BIO_should_retry(bio->next_bio)); */
|
||||
|
||||
if (status <= 0 && BIO_should_retry(bio->next_bio))
|
||||
if (status <= 0)
|
||||
{
|
||||
BIO_set_retry_read(bio);
|
||||
tcp->readBlocked = TRUE;
|
||||
if (!BIO_should_retry(bio->next_bio))
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
status = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
|
||||
if (BIO_should_read(bio->next_bio))
|
||||
{
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ);
|
||||
tcp->readBlocked = TRUE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -165,12 +356,14 @@ static long transport_bio_buffered_ctrl(BIO* bio, int cmd, long arg1, void* arg2
|
||||
{
|
||||
case BIO_CTRL_FLUSH:
|
||||
return 1;
|
||||
|
||||
case BIO_CTRL_WPENDING:
|
||||
return ringbuffer_used(&tcp->xmitBuffer);
|
||||
|
||||
case BIO_CTRL_PENDING:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
/*fprintf(stderr, "%s: passing to next BIO, bio=%p cmd=%d arg1=%d arg2=%p\n", __FUNCTION__, bio, cmd, arg1, arg2); */
|
||||
return BIO_ctrl(bio->next_bio, cmd, arg1, arg2);
|
||||
}
|
||||
|
||||
@ -182,8 +375,7 @@ static int transport_bio_buffered_new(BIO* bio)
|
||||
bio->init = 1;
|
||||
bio->num = 0;
|
||||
bio->ptr = NULL;
|
||||
bio->flags = 0;
|
||||
|
||||
bio->flags = BIO_FLAGS_SHOULD_RETRY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -192,7 +384,6 @@ static int transport_bio_buffered_free(BIO* bio)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static BIO_METHOD transport_bio_buffered_socket_methods =
|
||||
{
|
||||
BIO_TYPE_BUFFERED,
|
||||
@ -214,18 +405,17 @@ BIO_METHOD* BIO_s_buffered_socket(void)
|
||||
|
||||
BOOL transport_bio_buffered_drain(BIO *bio)
|
||||
{
|
||||
rdpTcp *tcp = (rdpTcp *)bio->ptr;
|
||||
int status;
|
||||
rdpTcp* tcp = (rdpTcp*) bio->ptr;
|
||||
|
||||
if (!ringbuffer_used(&tcp->xmitBuffer))
|
||||
return 1;
|
||||
|
||||
status = transport_bio_buffered_write(bio, NULL, 0);
|
||||
|
||||
return status >= 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void tcp_get_ip_address(rdpTcp* tcp)
|
||||
{
|
||||
BYTE* ip;
|
||||
@ -346,6 +536,16 @@ BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout)
|
||||
return FALSE; /* timeout */
|
||||
}
|
||||
}
|
||||
|
||||
BIO_set_close(tcp->socketBio, BIO_NOCLOSE);
|
||||
BIO_free(tcp->socketBio);
|
||||
|
||||
tcp->socketBio = BIO_new(BIO_s_simple_socket());
|
||||
|
||||
if (!tcp->socketBio)
|
||||
return -1;
|
||||
|
||||
BIO_set_fd(tcp->socketBio, tcp->sockfd, BIO_CLOSE);
|
||||
}
|
||||
|
||||
SetEventFileDescriptor(tcp->event, tcp->sockfd);
|
||||
@ -504,9 +704,12 @@ int tcp_attach(rdpTcp* tcp, int sockfd)
|
||||
}
|
||||
else
|
||||
{
|
||||
tcp->socketBio = BIO_new_socket(sockfd, 1);
|
||||
tcp->socketBio = BIO_new(BIO_s_simple_socket());
|
||||
|
||||
if (!tcp->socketBio)
|
||||
return -1;
|
||||
|
||||
BIO_set_fd(tcp->socketBio, sockfd, BIO_CLOSE);
|
||||
}
|
||||
|
||||
if (!tcp->bufferedBio)
|
||||
|
@ -38,7 +38,8 @@
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#define BIO_TYPE_BUFFERED 66
|
||||
#define BIO_TYPE_SIMPLE 66
|
||||
#define BIO_TYPE_BUFFERED 67
|
||||
|
||||
typedef struct rdp_tcp rdpTcp;
|
||||
|
||||
|
@ -133,17 +133,20 @@ static int transport_bio_tsg_write(BIO* bio, const char* buf, int num)
|
||||
|
||||
tsg = (rdpTsg*) bio->ptr;
|
||||
|
||||
BIO_clear_retry_flags(bio);
|
||||
BIO_clear_flags(bio, BIO_FLAGS_WRITE);
|
||||
|
||||
status = tsg_write(tsg, (BYTE*) buf, num);
|
||||
|
||||
if (status == 0)
|
||||
BIO_set_retry_write(bio);
|
||||
if (status < 0)
|
||||
{
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
}
|
||||
else
|
||||
{
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE);
|
||||
}
|
||||
|
||||
if (status > 0)
|
||||
return status;
|
||||
|
||||
return -1;
|
||||
return status >= 0 ? status : -1;
|
||||
}
|
||||
|
||||
static int transport_bio_tsg_read(BIO* bio, char* buf, int size)
|
||||
@ -153,18 +156,17 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size)
|
||||
|
||||
tsg = (rdpTsg*) bio->ptr;
|
||||
|
||||
BIO_clear_flags(bio, BIO_FLAGS_READ);
|
||||
|
||||
status = tsg_read(bio->ptr, (BYTE*) buf, size);
|
||||
|
||||
BIO_clear_retry_flags(bio);
|
||||
|
||||
if (status == 0)
|
||||
if (status < 0)
|
||||
{
|
||||
BIO_set_retry_read(bio);
|
||||
status = -1;
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
}
|
||||
else if (status == -1)
|
||||
else
|
||||
{
|
||||
status = 0;
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ);
|
||||
}
|
||||
|
||||
return status >= 0 ? status : -1;
|
||||
@ -195,8 +197,7 @@ static int transport_bio_tsg_new(BIO* bio)
|
||||
bio->init = 1;
|
||||
bio->num = 0;
|
||||
bio->ptr = NULL;
|
||||
bio->flags = 0;
|
||||
|
||||
bio->flags = BIO_FLAGS_SHOULD_RETRY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -226,8 +227,6 @@ BIO_METHOD* BIO_s_tsg(void)
|
||||
return &transport_bio_tsg_methods;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOL transport_connect_tls(rdpTransport* transport)
|
||||
{
|
||||
rdpSettings *settings = transport->settings;
|
||||
@ -708,7 +707,6 @@ static int transport_wait_for_write(rdpTransport* transport)
|
||||
return select(tcpOut->sockfd + 1, rsetPtr, wsetPtr, NULL, &tv);
|
||||
}
|
||||
|
||||
|
||||
int transport_read_layer(rdpTransport* transport, BYTE* data, int bytes)
|
||||
{
|
||||
int read = 0;
|
||||
@ -724,13 +722,7 @@ int transport_read_layer(rdpTransport* transport, BYTE* data, int bytes)
|
||||
{
|
||||
status = BIO_read(transport->frontBio, data + read, bytes - read);
|
||||
|
||||
if (!status)
|
||||
{
|
||||
transport->layer = TRANSPORT_LAYER_CLOSED;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
if (status <= 0)
|
||||
{
|
||||
if (!transport->frontBio || !BIO_should_retry(transport->frontBio))
|
||||
{
|
||||
@ -763,8 +755,6 @@ int transport_read_layer(rdpTransport* transport, BYTE* data, int bytes)
|
||||
return read;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int transport_read(rdpTransport* transport, wStream* s)
|
||||
{
|
||||
int status;
|
||||
|
@ -33,6 +33,445 @@
|
||||
#include <freerdp/crypto/tls.h>
|
||||
#include "../core/tcp.h"
|
||||
|
||||
struct _BIO_RDP_TLS
|
||||
{
|
||||
SSL* ssl;
|
||||
};
|
||||
typedef struct _BIO_RDP_TLS BIO_RDP_TLS;
|
||||
|
||||
long bio_rdp_tls_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_write(BIO* bio, const char* buf, int size)
|
||||
{
|
||||
int status;
|
||||
BIO_RDP_TLS* tls = (BIO_RDP_TLS*) bio->ptr;
|
||||
|
||||
if (!buf || !tls)
|
||||
return 0;
|
||||
|
||||
BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL);
|
||||
|
||||
status = SSL_write(tls->ssl, buf, size);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
switch (SSL_get_error(tls->ssl, status))
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_READ:
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = BIO_RR_SSL_X509_LOOKUP;
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = BIO_RR_CONNECT;
|
||||
break;
|
||||
|
||||
case SSL_ERROR_SYSCALL:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_SSL:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_read(BIO* bio, char* buf, int size)
|
||||
{
|
||||
int status;
|
||||
BIO_RDP_TLS* tls = (BIO_RDP_TLS*) bio->ptr;
|
||||
|
||||
if (!buf || !tls)
|
||||
return 0;
|
||||
|
||||
BIO_clear_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL);
|
||||
|
||||
status = SSL_read(tls->ssl, buf, size);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
switch (SSL_get_error(tls->ssl, status))
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_READ:
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = BIO_RR_SSL_X509_LOOKUP;
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = BIO_RR_ACCEPT;
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = BIO_RR_CONNECT;
|
||||
break;
|
||||
|
||||
case SSL_ERROR_SSL:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_SYSCALL:
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_puts(BIO* bio, const char* str)
|
||||
{
|
||||
int size;
|
||||
int status;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
size = strlen(str);
|
||||
status = BIO_write(bio, str, size);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_gets(BIO* bio, char* str, int size)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long bio_rdp_tls_ctrl(BIO* bio, int cmd, long num, void* ptr)
|
||||
{
|
||||
BIO* rbio;
|
||||
int status = -1;
|
||||
BIO_RDP_TLS* tls = (BIO_RDP_TLS*) bio->ptr;
|
||||
|
||||
if (!tls)
|
||||
return 0;
|
||||
|
||||
if (!tls->ssl && (cmd != BIO_C_SET_SSL))
|
||||
return 0;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case BIO_CTRL_RESET:
|
||||
SSL_shutdown(tls->ssl);
|
||||
|
||||
if (tls->ssl->handshake_func == tls->ssl->method->ssl_connect)
|
||||
SSL_set_connect_state(tls->ssl);
|
||||
else if (tls->ssl->handshake_func == tls->ssl->method->ssl_accept)
|
||||
SSL_set_accept_state(tls->ssl);
|
||||
|
||||
SSL_clear(tls->ssl);
|
||||
|
||||
if (bio->next_bio)
|
||||
status = BIO_ctrl(bio->next_bio, cmd, num, ptr);
|
||||
else if (tls->ssl->rbio)
|
||||
status = BIO_ctrl(tls->ssl->rbio, cmd, num, ptr);
|
||||
else
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_C_GET_FD:
|
||||
status = BIO_ctrl(tls->ssl->rbio, cmd, num, ptr);
|
||||
break;
|
||||
|
||||
case BIO_CTRL_INFO:
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_SET_CALLBACK:
|
||||
status = 0;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_GET_CALLBACK:
|
||||
*((ULONG_PTR*) ptr) = (ULONG_PTR) SSL_get_info_callback(tls->ssl);
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_C_SSL_MODE:
|
||||
if (num)
|
||||
SSL_set_connect_state(tls->ssl);
|
||||
else
|
||||
SSL_set_accept_state(tls->ssl);
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
status = bio->shutdown;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
bio->shutdown = (int) num;
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_WPENDING:
|
||||
status = BIO_ctrl(tls->ssl->wbio, cmd, num, ptr);
|
||||
break;
|
||||
|
||||
case BIO_CTRL_PENDING:
|
||||
status = SSL_pending(tls->ssl);
|
||||
if (status == 0)
|
||||
status = BIO_pending(tls->ssl->rbio);
|
||||
break;
|
||||
|
||||
case BIO_CTRL_FLUSH:
|
||||
BIO_clear_retry_flags(bio);
|
||||
status = BIO_ctrl(tls->ssl->wbio, cmd, num, ptr);
|
||||
BIO_copy_next_retry(bio);
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_PUSH:
|
||||
if (bio->next_bio && (bio->next_bio != tls->ssl->rbio))
|
||||
{
|
||||
SSL_set_bio(tls->ssl, bio->next_bio, bio->next_bio);
|
||||
CRYPTO_add(&(bio->next_bio->references), 1, CRYPTO_LOCK_BIO);
|
||||
}
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_CTRL_POP:
|
||||
if (bio == ptr)
|
||||
{
|
||||
if (tls->ssl->rbio != tls->ssl->wbio)
|
||||
BIO_free_all(tls->ssl->wbio);
|
||||
|
||||
if (bio->next_bio)
|
||||
CRYPTO_add(&(bio->next_bio->references), -1, CRYPTO_LOCK_BIO);
|
||||
|
||||
tls->ssl->wbio = tls->ssl->rbio = NULL;
|
||||
}
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_C_GET_SSL:
|
||||
if (ptr)
|
||||
{
|
||||
*((SSL**) ptr) = tls->ssl;
|
||||
status = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIO_C_SET_SSL:
|
||||
bio->shutdown = (int) num;
|
||||
|
||||
if (ptr)
|
||||
tls->ssl = (SSL*) ptr;
|
||||
|
||||
rbio = SSL_get_rbio(tls->ssl);
|
||||
|
||||
if (rbio)
|
||||
{
|
||||
if (bio->next_bio)
|
||||
BIO_push(rbio, bio->next_bio);
|
||||
|
||||
bio->next_bio = rbio;
|
||||
CRYPTO_add(&(rbio->references), 1, CRYPTO_LOCK_BIO);
|
||||
}
|
||||
|
||||
bio->init = 1;
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
case BIO_C_DO_STATE_MACHINE:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_WRITE | BIO_FLAGS_IO_SPECIAL);
|
||||
bio->retry_reason = 0;
|
||||
|
||||
status = SSL_do_handshake(tls->ssl);
|
||||
|
||||
if (status <= 0)
|
||||
{
|
||||
switch (SSL_get_error(tls->ssl, status))
|
||||
{
|
||||
case SSL_ERROR_WANT_READ:
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
BIO_set_flags(bio, BIO_FLAGS_IO_SPECIAL | BIO_FLAGS_SHOULD_RETRY);
|
||||
bio->retry_reason = bio->next_bio->retry_reason;
|
||||
break;
|
||||
|
||||
default:
|
||||
BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
status = BIO_ctrl(tls->ssl->rbio, cmd, num, ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_new(BIO* bio)
|
||||
{
|
||||
BIO_RDP_TLS* tls;
|
||||
|
||||
bio->init = 0;
|
||||
bio->num = 0;
|
||||
bio->flags = BIO_FLAGS_SHOULD_RETRY;
|
||||
bio->next_bio = NULL;
|
||||
|
||||
tls = calloc(1, sizeof(BIO_RDP_TLS));
|
||||
|
||||
if (!tls)
|
||||
return 0;
|
||||
|
||||
bio->ptr = (void*) tls;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bio_rdp_tls_free(BIO* bio)
|
||||
{
|
||||
BIO_RDP_TLS* tls;
|
||||
|
||||
if (!bio)
|
||||
return 0;
|
||||
|
||||
tls = (BIO_RDP_TLS*) bio->ptr;
|
||||
|
||||
if (!tls)
|
||||
return 0;
|
||||
|
||||
if (bio->shutdown)
|
||||
{
|
||||
if (bio->init && tls->ssl)
|
||||
{
|
||||
SSL_shutdown(tls->ssl);
|
||||
SSL_free(tls->ssl);
|
||||
}
|
||||
|
||||
bio->init = 0;
|
||||
bio->flags = 0;
|
||||
}
|
||||
|
||||
free(tls);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long bio_rdp_tls_callback_ctrl(BIO* bio, int cmd, bio_info_cb* fp)
|
||||
{
|
||||
int status = 0;
|
||||
BIO_RDP_TLS* tls;
|
||||
|
||||
if (!bio)
|
||||
return 0;
|
||||
|
||||
tls = (BIO_RDP_TLS*) bio->ptr;
|
||||
|
||||
if (!tls)
|
||||
return 0;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case BIO_CTRL_SET_CALLBACK:
|
||||
SSL_set_info_callback(tls->ssl, (void (*)(const SSL *, int, int)) fp);
|
||||
status = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
status = BIO_callback_ctrl(tls->ssl->rbio, cmd, fp);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#define BIO_TYPE_RDP_TLS 68
|
||||
|
||||
static BIO_METHOD bio_rdp_tls_methods =
|
||||
{
|
||||
BIO_TYPE_RDP_TLS,
|
||||
"RdpTls",
|
||||
bio_rdp_tls_write,
|
||||
bio_rdp_tls_read,
|
||||
bio_rdp_tls_puts,
|
||||
bio_rdp_tls_gets,
|
||||
bio_rdp_tls_ctrl,
|
||||
bio_rdp_tls_new,
|
||||
bio_rdp_tls_free,
|
||||
bio_rdp_tls_callback_ctrl,
|
||||
};
|
||||
|
||||
BIO_METHOD* BIO_s_rdp_tls(void)
|
||||
{
|
||||
return &bio_rdp_tls_methods;
|
||||
}
|
||||
|
||||
BIO* BIO_new_rdp_tls(SSL_CTX* ctx, int client)
|
||||
{
|
||||
BIO* bio;
|
||||
SSL* ssl;
|
||||
|
||||
bio = BIO_new(BIO_s_rdp_tls());
|
||||
|
||||
if (!bio)
|
||||
return NULL;
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
|
||||
if (!ssl)
|
||||
{
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (client)
|
||||
SSL_set_connect_state(ssl);
|
||||
else
|
||||
SSL_set_accept_state(ssl);
|
||||
|
||||
BIO_set_ssl(bio, ssl, BIO_CLOSE);
|
||||
|
||||
return bio;
|
||||
}
|
||||
|
||||
static CryptoCert tls_get_certificate(rdpTls* tls, BOOL peer)
|
||||
{
|
||||
CryptoCert cert;
|
||||
@ -127,7 +566,8 @@ BOOL tls_prepare(rdpTls* tls, BIO *underlying, const SSL_METHOD *method, int opt
|
||||
SSL_CTX_set_options(tls->ctx, options);
|
||||
SSL_CTX_set_read_ahead(tls->ctx, 1);
|
||||
|
||||
tls->bio = BIO_new_ssl(tls->ctx, clientMode);
|
||||
tls->bio = BIO_new_rdp_tls(tls->ctx, clientMode);
|
||||
|
||||
if (BIO_get_ssl(tls->bio, &tls->ssl) < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: unable to retrieve the SSL of the connection\n", __FUNCTION__);
|
||||
@ -135,6 +575,7 @@ BOOL tls_prepare(rdpTls* tls, BIO *underlying, const SSL_METHOD *method, int opt
|
||||
}
|
||||
|
||||
BIO_push(tls->bio, underlying);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -150,8 +591,10 @@ int tls_do_handshake(rdpTls* tls, BOOL clientMode)
|
||||
int fd;
|
||||
|
||||
status = BIO_do_handshake(tls->bio);
|
||||
|
||||
if (status == 1)
|
||||
break;
|
||||
|
||||
if (!BIO_should_retry(tls->bio))
|
||||
return -1;
|
||||
|
||||
@ -160,6 +603,7 @@ int tls_do_handshake(rdpTls* tls, BOOL clientMode)
|
||||
FD_ZERO(&rset);
|
||||
|
||||
fd = BIO_get_fd(tls->bio, NULL);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: unable to retrieve BIO fd\n", __FUNCTION__);
|
||||
@ -171,6 +615,7 @@ int tls_do_handshake(rdpTls* tls, BOOL clientMode)
|
||||
tv.tv_usec = 10 * 1000; /* 10ms */
|
||||
|
||||
status = select(fd + 1, &rset, NULL, NULL, &tv);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
fprintf(stderr, "%s: error during select()\n", __FUNCTION__);
|
||||
@ -399,7 +844,7 @@ int tls_write_all(rdpTls* tls, const BYTE* data, int length)
|
||||
do
|
||||
{
|
||||
status = BIO_write(bio, data, length);
|
||||
/*fprintf(stderr, "%s: BIO_write(len=%d) = %d (retry=%d)\n", __FUNCTION__, length, status, BIO_should_retry(bio));*/
|
||||
|
||||
if (status > 0)
|
||||
break;
|
||||
|
||||
@ -408,6 +853,7 @@ int tls_write_all(rdpTls* tls, const BYTE* data, int length)
|
||||
|
||||
/* we try to handle SSL want_read and want_write nicely */
|
||||
rsetPtr = wsetPtr = 0;
|
||||
|
||||
if (tcp->writeBlocked)
|
||||
{
|
||||
wsetPtr = &wset;
|
||||
@ -431,6 +877,7 @@ int tls_write_all(rdpTls* tls, const BYTE* data, int length)
|
||||
tv.tv_usec = 100 * 1000;
|
||||
|
||||
status = select(tcp->sockfd + 1, rsetPtr, wsetPtr, NULL, &tv);
|
||||
|
||||
if (status < 0)
|
||||
return -1;
|
||||
}
|
||||
@ -447,6 +894,7 @@ int tls_write_all(rdpTls* tls, const BYTE* data, int length)
|
||||
while (chunks[i].size)
|
||||
{
|
||||
status = BIO_write(tcp->socketBio, chunks[i].data, chunks[i].size);
|
||||
|
||||
if (status > 0)
|
||||
{
|
||||
chunks[i].size -= status;
|
||||
@ -457,12 +905,14 @@ int tls_write_all(rdpTls* tls, const BYTE* data, int length)
|
||||
|
||||
if (!BIO_should_retry(tcp->socketBio))
|
||||
goto out_fail;
|
||||
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(tcp->sockfd, &rset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100 * 1000;
|
||||
|
||||
status = select(tcp->sockfd + 1, &rset, NULL, NULL, &tv);
|
||||
|
||||
if (status < 0)
|
||||
goto out_fail;
|
||||
}
|
||||
@ -478,8 +928,6 @@ out_fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tls_set_alert_code(rdpTls* tls, int level, int description)
|
||||
{
|
||||
tls->alertLevel = level;
|
||||
|
@ -255,6 +255,10 @@ PCSTR inet_ntop(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize)
|
||||
#include <netinet/tcp.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData)
|
||||
{
|
||||
ZeroMemory(lpWSAData, sizeof(WSADATA));
|
||||
@ -423,6 +427,17 @@ int WSAGetLastError(void)
|
||||
iError = WSAEREMOTE;
|
||||
break;
|
||||
|
||||
/* Special cases */
|
||||
|
||||
#if (EAGAIN != EWOULDBLOCK)
|
||||
case EAGAIN:
|
||||
iError = WSAEWOULDBLOCK;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EPROTO:
|
||||
iError = WSAECONNRESET;
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,7 +460,7 @@ int WSAGetLastError(void)
|
||||
* WSAEREFUSED
|
||||
*/
|
||||
|
||||
return 0;
|
||||
return iError;
|
||||
}
|
||||
|
||||
SOCKET _accept(SOCKET s, struct sockaddr* addr, int* addrlen)
|
||||
@ -607,6 +622,8 @@ int _send(SOCKET s, const char* buf, int len, int flags)
|
||||
int status;
|
||||
int fd = (int) s;
|
||||
|
||||
flags |= MSG_NOSIGNAL;
|
||||
|
||||
status = (int) send(fd, (void*) buf, (size_t) len, flags);
|
||||
|
||||
return status;
|
||||
|
Loading…
Reference in New Issue
Block a user