1364 lines
42 KiB
C++
1364 lines
42 KiB
C++
#define SECURITY_WIN32
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include <windows.h>
|
|
#include <schnlsp.h>
|
|
#include <sspi.h>
|
|
|
|
#include "util/logging.h"
|
|
|
|
#include "ssl.h"
|
|
#include "core.h"
|
|
|
|
#ifndef SP_PROT_TLS1_1_CLIENT
|
|
#define SP_PROT_TLS1_1_CLIENT 0x00000200
|
|
#endif
|
|
|
|
#ifndef SP_PROT_TLS1_2_CLIENT
|
|
#define SP_PROT_TLS1_2_CLIENT 0x00000800
|
|
#endif
|
|
|
|
#define IO_BUF_INITIAL_CAPACITY 524288
|
|
|
|
namespace avs {
|
|
|
|
// These structures are initialized by AVS and our functions are handed pointers to
|
|
// them, do not use C++ objects in them!
|
|
|
|
struct io_buf {
|
|
uint8_t *bytes;
|
|
size_t capacity;
|
|
size_t pos;
|
|
size_t limit;
|
|
};
|
|
|
|
struct core::avs_net_proto_desc_work {
|
|
void *proto_desc_work;
|
|
uint32_t padding[16];
|
|
};
|
|
struct core::avs_net_sock_desc_work {
|
|
void *proto_desc_work;
|
|
int sock_fd;
|
|
uint32_t address;
|
|
avs_net_port_t port;
|
|
const char *hostname;
|
|
uint32_t send_timeout;
|
|
uint32_t recv_timeout;
|
|
bool non_blocking;
|
|
CtxtHandle security_context;
|
|
CredHandle credentials_handle;
|
|
SecPkgContext_StreamSizes sizes;
|
|
struct io_buf send_buf;
|
|
struct io_buf recv_buf;
|
|
uint8_t *recv_plaintext_tail;
|
|
size_t recv_plaintext_count;
|
|
uint8_t *recv_ciphertext_tail;
|
|
size_t recv_ciphertext_count;
|
|
bool got_shutdown;
|
|
uint32_t padding[16];
|
|
};
|
|
|
|
namespace ssl {
|
|
using avs::core::avs_iovec;
|
|
using avs::core::avs_net_port_t;
|
|
using avs::core::avs_net_poll_fd;
|
|
using avs::core::avs_net_poll_fd_opaque;
|
|
using avs::core::avs_net_pollfds_size_t;
|
|
using avs::core::avs_net_proto_desc_work;
|
|
using avs::core::avs_net_size_t;
|
|
using avs::core::avs_net_sock_desc_work;
|
|
using avs::core::avs_net_timeout_t;
|
|
using avs::core::AVS_ERROR_CLASS_NET;
|
|
using avs::core::AVS_ERROR_SUBCLASS_NET_TIMEOUT;
|
|
using avs::core::AVS_ERROR_SUBCLASS_SC_BADMSG;
|
|
using avs::core::AVS_ERROR_SUBCLASS_SC_INVAL;
|
|
using avs::core::AVS_NET_POLL_POLLIN;
|
|
using avs::core::AVS_NET_PROTOCOL_SSL_TLS_V1_1;
|
|
using avs::core::AVS_SO_SNDTIMEO;
|
|
using avs::core::AVS_SO_RCVTIMEO;
|
|
using avs::core::AVS_SO_NONBLOCK;
|
|
using avs::core::AVS_SO_SSL_PROTOCOL;
|
|
using avs::core::AVS_SO_SSL_VERIFY_CN;
|
|
using avs::core::T_NET_PROTO_ID_DEFAULT;
|
|
|
|
enum tls_recv_payload {
|
|
TLS_RECV_PAYLOAD_NONE,
|
|
TLS_RECV_PAYLOAD_DATA,
|
|
TLS_RECV_PAYLOAD_SHUTDOWN_TOKEN,
|
|
};
|
|
|
|
static constexpr size_t alignment = 16;
|
|
|
|
static int io_buf_init(struct io_buf *buf, size_t initial_capacity) {
|
|
uint8_t *tmp_bytes = nullptr;
|
|
int err = 0;
|
|
|
|
if (buf == nullptr) {
|
|
err = -1;
|
|
goto arg_fail;
|
|
}
|
|
|
|
tmp_bytes = reinterpret_cast<uint8_t *>(_aligned_malloc(initial_capacity, alignment));
|
|
|
|
if (tmp_bytes == nullptr) {
|
|
err = -1;
|
|
|
|
goto alloc_fail;
|
|
}
|
|
|
|
buf->bytes = tmp_bytes;
|
|
buf->capacity = initial_capacity;
|
|
buf->pos = 0;
|
|
buf->limit = initial_capacity;
|
|
|
|
return 0;
|
|
|
|
alloc_fail:
|
|
memset(buf, 0, sizeof(*buf));
|
|
|
|
arg_fail:
|
|
return err;
|
|
}
|
|
|
|
static inline int io_buf_validate(const struct io_buf *buf) {
|
|
if (buf == nullptr || buf->bytes == nullptr) {
|
|
return -1;
|
|
}
|
|
|
|
if (buf->pos > buf->limit || buf->limit > buf->capacity) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int io_buf_grow_to(struct io_buf *buf, size_t min_capacity) {
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
auto tmp_capacity = buf->capacity;
|
|
|
|
while (tmp_capacity < min_capacity) {
|
|
tmp_capacity *= 2;
|
|
}
|
|
|
|
if (tmp_capacity <= buf->capacity) {
|
|
return -1;
|
|
}
|
|
|
|
auto tmp_bytes = reinterpret_cast<uint8_t *>(_aligned_malloc(tmp_capacity, alignment));
|
|
|
|
if (tmp_bytes == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy(tmp_bytes, buf->bytes, buf->pos);
|
|
|
|
_aligned_free(buf->bytes);
|
|
|
|
buf->bytes = tmp_bytes;
|
|
buf->capacity = tmp_capacity;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int io_buf_grow(struct io_buf *buf) {
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
return io_buf_grow_to(buf, buf->capacity * 2);
|
|
}
|
|
|
|
static int io_buf_append(struct io_buf *buf, const void *src, size_t *nbytes) {
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (src == nullptr || nbytes == nullptr) {
|
|
return -1;
|
|
}
|
|
|
|
if (*nbytes > buf->limit - buf->pos) {
|
|
*nbytes = buf->limit - buf->pos;
|
|
}
|
|
|
|
memcpy(&buf->bytes[buf->pos], src, *nbytes);
|
|
|
|
buf->pos += *nbytes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int io_buf_flip(struct io_buf *buf) {
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
buf->limit = buf->pos;
|
|
buf->pos = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void io_buf_finish(struct io_buf *buf) {
|
|
if (buf != nullptr && buf->bytes != nullptr) {
|
|
_aligned_free(buf->bytes);
|
|
|
|
buf->bytes = nullptr;
|
|
buf->capacity = 0;
|
|
buf->pos = 0;
|
|
buf->limit = 0;
|
|
}
|
|
}
|
|
|
|
static int impl_socket_recv(struct avs_net_sock_desc_work *work, struct io_buf *buf) {
|
|
int result = 0;
|
|
int err = 0;
|
|
|
|
if (work == nullptr || work->sock_fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (buf->pos == buf->limit) {
|
|
return -1;
|
|
}
|
|
|
|
result = core::avs_net_recv(work->sock_fd, &buf->bytes[buf->pos], buf->limit - buf->pos);
|
|
|
|
if (result < 0) {
|
|
log_warning("avs::ssl", "avs_net_recv failed: 0x{:08x}", result);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (result == 0) {
|
|
log_misc("avs::ssl", "connection closed");
|
|
|
|
return -2;
|
|
}
|
|
|
|
buf->pos += result;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int impl_socket_recv_all(
|
|
struct avs_net_sock_desc_work *work,
|
|
struct io_buf *buf,
|
|
uint32_t recv_timeout)
|
|
{
|
|
uint8_t old_non_blocking_value = 0;
|
|
uint8_t non_blocking = 0;
|
|
avs_net_size_t old_non_blocking_value_size = 0;
|
|
struct avs_net_poll_fd poll_fds[1] {};
|
|
int ret = 0;
|
|
|
|
if (work == nullptr || work->sock_fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (buf->pos == buf->limit) {
|
|
return -1;
|
|
}
|
|
|
|
poll_fds[0].socket = work->sock_fd;
|
|
poll_fds[0].events = AVS_NET_POLL_POLLIN;
|
|
|
|
non_blocking = 1;
|
|
old_non_blocking_value_size = sizeof(old_non_blocking_value);
|
|
|
|
core::avs_net_getsockopt(work->sock_fd, AVS_SO_NONBLOCK, &old_non_blocking_value, &old_non_blocking_value_size);
|
|
core::avs_net_setsockopt(work->sock_fd, AVS_SO_NONBLOCK, &non_blocking, sizeof(non_blocking));
|
|
|
|
while (true) {
|
|
auto result = core::avs_net_poll(poll_fds, std::size(poll_fds), recv_timeout);
|
|
|
|
if (result < 0) {
|
|
log_warning("avs::ssl", "avs_net_poll failed: 0x{:08x}", result);
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
}
|
|
|
|
if (!result) {
|
|
#if 0
|
|
log_warning("avs::ssl", "socket timeout, no data received after {} milliseconds", recv_timeout);
|
|
#endif
|
|
|
|
goto poll_succeeded_or_empty;
|
|
}
|
|
|
|
if (poll_fds[0].r_events & AVS_NET_POLL_POLLIN) {
|
|
result = core::avs_net_recv(work->sock_fd, &buf->bytes[buf->pos], buf->limit - buf->pos);
|
|
|
|
if (result < 0) {
|
|
break;
|
|
}
|
|
|
|
if (result == 0) {
|
|
log_misc("avs::ssl", "connection closed");
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
}
|
|
|
|
#if 0
|
|
log_warning("avs::ssl", "socket({}) got {} bytes during handshake", work->sock_fd, result);
|
|
#endif
|
|
|
|
buf->pos += result;
|
|
}
|
|
}
|
|
|
|
poll_succeeded_or_empty:
|
|
ret = 0;
|
|
|
|
out:
|
|
core::avs_net_setsockopt(work->sock_fd, AVS_SO_NONBLOCK, &old_non_blocking_value, sizeof(old_non_blocking_value));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int impl_socket_send(struct avs_net_sock_desc_work *work, struct io_buf *buf) {
|
|
if (work == nullptr || work->sock_fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
auto err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return -1;
|
|
}
|
|
|
|
auto result = core::avs_net_send(work->sock_fd, &buf->bytes[buf->pos], buf->limit - buf->pos);
|
|
|
|
#if 0
|
|
log_warning("avs::ssl", "socket({}) sending {} bytes", work->sock_fd, buf->limit - buf->pos);
|
|
#endif
|
|
|
|
if (result != static_cast<int>(buf->limit - buf->pos)) {
|
|
log_warning("avs::ssl", "avs_net_send failed: 0x{:08x}", result);
|
|
|
|
return -1;
|
|
}
|
|
|
|
buf->pos = buf->limit;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_begin_buffer(struct avs_net_sock_desc_work *work) {
|
|
auto status = QueryContextAttributes(&work->security_context, SECPKG_ATTR_STREAM_SIZES, &work->sizes);
|
|
|
|
if (status != SEC_E_OK) {
|
|
log_warning("avs::ssl", "QueryContextAttributes failed: {}", FMT_HRESULT(status));
|
|
|
|
return -1;
|
|
}
|
|
|
|
auto total = work->sizes.cbHeader + work->sizes.cbTrailer + work->sizes.cbMaximumMessage;
|
|
|
|
auto err = io_buf_grow_to(&work->send_buf, total);
|
|
|
|
if (err != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_send_chunk(struct avs_net_sock_desc_work *work, const uint8_t *bytes, size_t nbytes) {
|
|
int err = 0;
|
|
SecBuffer send_bufs[4];
|
|
SecBufferDesc send_vec;
|
|
SECURITY_STATUS status = 0;
|
|
|
|
send_vec.ulVersion = SECBUFFER_VERSION;
|
|
send_vec.pBuffers = send_bufs;
|
|
send_vec.cBuffers = std::size(send_bufs);
|
|
|
|
send_bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
|
|
send_bufs[0].pvBuffer = &work->send_buf.bytes[0];
|
|
send_bufs[0].cbBuffer = work->sizes.cbHeader;
|
|
|
|
send_bufs[1].BufferType = SECBUFFER_DATA;
|
|
send_bufs[1].pvBuffer = &work->send_buf.bytes[work->sizes.cbHeader];
|
|
send_bufs[1].cbBuffer = nbytes;
|
|
|
|
send_bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
|
|
send_bufs[2].pvBuffer = &work->send_buf.bytes[work->sizes.cbHeader + nbytes];
|
|
send_bufs[2].cbBuffer = work->sizes.cbTrailer;
|
|
|
|
send_bufs[3].BufferType = SECBUFFER_EMPTY;
|
|
send_bufs[3].pvBuffer = nullptr;
|
|
send_bufs[3].cbBuffer = 0;
|
|
|
|
memcpy(send_bufs[1].pvBuffer, bytes, nbytes);
|
|
|
|
status = EncryptMessage(&work->security_context, 0, &send_vec, 0);
|
|
|
|
if (status != SEC_E_OK) {
|
|
log_warning("avs::ssl", "EncryptMessage failed: {}", FMT_HRESULT(status));
|
|
|
|
return -1;
|
|
}
|
|
|
|
work->send_buf.pos = 0;
|
|
work->send_buf.limit = send_bufs[0].cbBuffer + send_bufs[1].cbBuffer + send_bufs[2].cbBuffer;
|
|
|
|
err = impl_socket_send(work, &work->send_buf);
|
|
|
|
if (err != 0) {
|
|
log_warning("avs::ssl", "impl_socket_send failed: {}", err);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_send(struct avs_net_sock_desc_work *work, struct io_buf *buf) {
|
|
size_t chunk_size = 0;
|
|
int err = 0;
|
|
|
|
err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (buf->pos == buf->limit) {
|
|
return -1;
|
|
}
|
|
|
|
while (buf->pos < buf->limit) {
|
|
chunk_size = buf->limit - buf->pos;
|
|
|
|
if (chunk_size > work->sizes.cbMaximumMessage) {
|
|
chunk_size = work->sizes.cbMaximumMessage;
|
|
}
|
|
|
|
err = tls_send_chunk(work, &buf->bytes[buf->pos], chunk_size);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
buf->pos += chunk_size;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tls_recv_dequeue_plaintext(struct avs_net_sock_desc_work *work, struct io_buf *buf) {
|
|
size_t tail_nbytes = 0;
|
|
int err = 0;
|
|
|
|
tail_nbytes = work->recv_plaintext_count;
|
|
|
|
err = io_buf_append(buf, work->recv_plaintext_tail, &tail_nbytes);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
SecureZeroMemory(work->recv_plaintext_tail, tail_nbytes);
|
|
|
|
work->recv_plaintext_count -= tail_nbytes;
|
|
work->recv_plaintext_tail += tail_nbytes;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_recv_common(struct avs_net_sock_desc_work *work, enum tls_recv_payload *payload) {
|
|
int err = 0;
|
|
SecBuffer recv_bufs[4];
|
|
SecBufferDesc recv_vec;
|
|
SECURITY_STATUS status = SEC_E_OK;
|
|
|
|
// Consolidate any leftover ciphertext at the start of the buffer
|
|
memmove(work->recv_buf.bytes, work->recv_ciphertext_tail, work->recv_ciphertext_count);
|
|
|
|
work->recv_buf.pos = work->recv_ciphertext_count;
|
|
work->recv_buf.limit = work->recv_buf.capacity;
|
|
work->recv_ciphertext_tail = nullptr;
|
|
work->recv_ciphertext_count = 0;
|
|
|
|
recv_vec.ulVersion = SECBUFFER_VERSION;
|
|
recv_vec.cBuffers = std::size(recv_bufs);
|
|
recv_vec.pBuffers = recv_bufs;
|
|
|
|
while (true) {
|
|
recv_bufs[0].BufferType = SECBUFFER_DATA;
|
|
recv_bufs[0].pvBuffer = work->recv_buf.bytes;
|
|
recv_bufs[0].cbBuffer = work->recv_buf.pos;
|
|
|
|
for (size_t i = 1; i < std::size(recv_bufs); i++) {
|
|
recv_bufs[i].BufferType = SECBUFFER_EMPTY;
|
|
recv_bufs[i].pvBuffer = nullptr;
|
|
recv_bufs[i].cbBuffer = 0;
|
|
}
|
|
|
|
status = DecryptMessage(&work->security_context, &recv_vec, 0, nullptr);
|
|
|
|
if (status != SEC_E_INCOMPLETE_MESSAGE) {
|
|
break;
|
|
}
|
|
|
|
err = impl_socket_recv(work, &work->recv_buf);
|
|
|
|
if (err != 0) {
|
|
if (err != -2) {
|
|
log_warning("avs::ssl", "impl_socket_recv failed: {}", err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
}
|
|
|
|
// Deal with whatever it is we received
|
|
switch (status) {
|
|
case SEC_E_OK:
|
|
|
|
// Walk buffers and mark up the plaintext and ciphertext span within our
|
|
// own io_buf as appropriate
|
|
for (size_t i = 0; i < std::size(recv_bufs); i++) {
|
|
switch (recv_bufs[i].BufferType) {
|
|
case SECBUFFER_DATA:
|
|
work->recv_plaintext_tail = reinterpret_cast<uint8_t *>(recv_bufs[i].pvBuffer);
|
|
work->recv_plaintext_count = recv_bufs[i].cbBuffer;
|
|
break;
|
|
|
|
case SECBUFFER_EXTRA:
|
|
work->recv_ciphertext_tail = reinterpret_cast<uint8_t *>(recv_bufs[i].pvBuffer);
|
|
work->recv_ciphertext_count = recv_bufs[i].cbBuffer;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
*payload = TLS_RECV_PAYLOAD_DATA;
|
|
|
|
return 0;
|
|
|
|
case SEC_I_CONTEXT_EXPIRED:
|
|
*payload = TLS_RECV_PAYLOAD_SHUTDOWN_TOKEN;
|
|
|
|
return 0;
|
|
|
|
default:
|
|
log_warning("avs::ssl", "DecryptMessage failed: {}", FMT_HRESULT(status));
|
|
|
|
work->recv_buf.pos = 0;
|
|
work->recv_buf.limit = 0;
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int tls_recv(struct avs_net_sock_desc_work *work, struct io_buf *buf) {
|
|
enum tls_recv_payload payload = TLS_RECV_PAYLOAD_NONE;
|
|
int err = 0;
|
|
|
|
err = io_buf_validate(buf);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (buf->pos == buf->limit) {
|
|
return -1;
|
|
}
|
|
|
|
// Try to drain any leftover plaintext in the receive buffer
|
|
if (work->recv_plaintext_count > 0) {
|
|
return tls_recv_dequeue_plaintext(work, buf);
|
|
}
|
|
|
|
err = tls_recv_common(work, &payload);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
switch (payload) {
|
|
case TLS_RECV_PAYLOAD_DATA:
|
|
return tls_recv_dequeue_plaintext(work, buf);
|
|
|
|
case TLS_RECV_PAYLOAD_SHUTDOWN_TOKEN:
|
|
work->got_shutdown = true;
|
|
|
|
return -2;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int tls_recv_shutdown(struct avs_net_sock_desc_work *work) {
|
|
enum tls_recv_payload payload = TLS_RECV_PAYLOAD_NONE;
|
|
|
|
if (work->recv_plaintext_count > 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (work->got_shutdown) {
|
|
return 0;
|
|
}
|
|
|
|
auto err = tls_recv_common(work, &payload);
|
|
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
if (payload != TLS_RECV_PAYLOAD_SHUTDOWN_TOKEN) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tls_send_shutdown(struct avs_net_sock_desc_work *work) {
|
|
static uint32_t tls_shutdown_token = SCHANNEL_SHUTDOWN;
|
|
ULONG attrs = 0;
|
|
SecBuffer cmd_buf;
|
|
SecBufferDesc cmd_vec;
|
|
SECURITY_STATUS status = SEC_E_OK;
|
|
|
|
cmd_vec.ulVersion = SECBUFFER_VERSION;
|
|
cmd_vec.pBuffers = &cmd_buf;
|
|
cmd_vec.cBuffers = 1;
|
|
|
|
cmd_buf.BufferType = SECBUFFER_TOKEN;
|
|
cmd_buf.pvBuffer = static_cast<void *>(&tls_shutdown_token);
|
|
cmd_buf.cbBuffer = sizeof(tls_shutdown_token);
|
|
|
|
status = ApplyControlToken(&work->security_context, &cmd_vec);
|
|
|
|
if (status != SEC_E_OK) {
|
|
log_warning("avs::ssl", "{}: ApplyControlToken failed: {}", __func__, FMT_HRESULT(status));
|
|
|
|
return -1;
|
|
}
|
|
|
|
cmd_vec.ulVersion = SECBUFFER_VERSION;
|
|
cmd_vec.pBuffers = &cmd_buf;
|
|
cmd_vec.cBuffers = 1;
|
|
|
|
cmd_buf.BufferType = SECBUFFER_TOKEN;
|
|
cmd_buf.pvBuffer = work->send_buf.bytes;
|
|
cmd_buf.cbBuffer = work->send_buf.capacity;
|
|
|
|
log_info("avs::ssl", "calling InitializeSecurityContextA to generate token");
|
|
|
|
status = InitializeSecurityContextA(
|
|
&work->credentials_handle,
|
|
&work->security_context,
|
|
const_cast<SEC_CHAR *>(work->hostname),
|
|
0,
|
|
0,
|
|
0,
|
|
nullptr,
|
|
0,
|
|
nullptr,
|
|
&cmd_vec,
|
|
&attrs,
|
|
nullptr);
|
|
|
|
if (status != SEC_E_OK) {
|
|
log_warning("avs::ssl", "{}: InitializeSecurityContextA failed: {}", __func__, FMT_HRESULT(status));
|
|
|
|
return -1;
|
|
}
|
|
|
|
work->send_buf.pos = 0;
|
|
work->send_buf.limit = cmd_buf.cbBuffer;
|
|
|
|
auto err = impl_socket_send(work, &work->send_buf);
|
|
|
|
if (err != 0) {
|
|
log_warning("avs::ssl", "impl_socket_send failed: {}", err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ssl_protocol_initialize(struct avs_net_proto_desc_work *work) {
|
|
return 0;
|
|
}
|
|
|
|
static int ssl_protocol_finalize(struct avs_net_proto_desc_work *work) {
|
|
return 0;
|
|
}
|
|
|
|
static int ssl_allocate_socket(struct avs_net_sock_desc_work *work) {
|
|
SCHANNEL_CRED credentials;
|
|
CredHandle credentials_handle;
|
|
|
|
memset(work, 0, sizeof(*work));
|
|
memset(&credentials, 0, sizeof(credentials));
|
|
|
|
credentials.dwVersion = SCHANNEL_CRED_VERSION;
|
|
credentials.cCreds = 0;
|
|
credentials.paCred = nullptr;
|
|
credentials.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT | SCH_CRED_IGNORE_REVOCATION_OFFLINE;
|
|
credentials.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
|
|
|
|
auto status = AcquireCredentialsHandleA(
|
|
nullptr,
|
|
const_cast<SEC_CHAR *>(UNISP_NAME),
|
|
SECPKG_CRED_OUTBOUND,
|
|
nullptr,
|
|
&credentials,
|
|
nullptr,
|
|
nullptr,
|
|
&credentials_handle,
|
|
nullptr);
|
|
|
|
if (status != SEC_E_OK) {
|
|
log_warning("avs::ssl", "AcquireCredentialsHandleA failed: {}", status);
|
|
|
|
return core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_SC_INVAL);
|
|
}
|
|
|
|
work->credentials_handle = credentials_handle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ssl_free_socket(struct avs_net_sock_desc_work *work) {
|
|
if (work != nullptr) {
|
|
if (work->hostname != nullptr) {
|
|
free(const_cast<void *>(reinterpret_cast<const void *>(work->hostname)));
|
|
|
|
work->hostname = nullptr;
|
|
}
|
|
|
|
FreeCredentialsHandle(&work->credentials_handle);
|
|
}
|
|
}
|
|
|
|
static int ssl_initialize_socket(struct avs_net_sock_desc_work *work) {
|
|
auto sock_fd = core::avs_net_socket(T_NET_PROTO_ID_DEFAULT);
|
|
|
|
if (sock_fd > 0) {
|
|
work->sock_fd = sock_fd;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ssl_finalize_socket(struct avs_net_sock_desc_work *work) {
|
|
}
|
|
|
|
static int ssl_setsockopt(
|
|
struct avs_net_sock_desc_work *work,
|
|
unsigned int option_name,
|
|
const void *option_value,
|
|
avs_net_size_t option_len)
|
|
{
|
|
// SSL specific options here
|
|
switch (option_name) {
|
|
case AVS_SO_SSL_PROTOCOL:
|
|
log_info("avs::ssl", "AVS_SO_SSL_PROTOCOL = {}", *reinterpret_cast<const int *>(option_value));
|
|
|
|
return 0;
|
|
|
|
case AVS_SO_SSL_VERIFY_CN:
|
|
log_info("avs::ssl", "AVS_SO_SSL_VERIFY_CN = {}", *reinterpret_cast<const int *>(option_value));
|
|
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Generic network options here
|
|
auto result = core::avs_net_setsockopt(work->sock_fd, option_name, option_value, option_len);
|
|
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
|
|
switch (option_name) {
|
|
case AVS_SO_SNDTIMEO:
|
|
work->send_timeout = *reinterpret_cast<const avs_net_timeout_t *>(option_value);
|
|
|
|
break;
|
|
|
|
case AVS_SO_RCVTIMEO:
|
|
work->recv_timeout = *reinterpret_cast<const avs_net_timeout_t *>(option_value);
|
|
|
|
break;
|
|
|
|
case AVS_SO_NONBLOCK:
|
|
work->non_blocking = *reinterpret_cast<const uint8_t *>(option_value);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ssl_socket_getsockopt(
|
|
struct avs_net_sock_desc_work *work,
|
|
unsigned int option_name,
|
|
void *option_value,
|
|
avs_net_size_t *option_len)
|
|
{
|
|
switch (option_name) {
|
|
case AVS_SO_SNDTIMEO:
|
|
*reinterpret_cast<avs_net_timeout_t *>(option_value) = work->send_timeout;
|
|
*option_len = sizeof(avs_net_timeout_t);
|
|
|
|
break;
|
|
|
|
case AVS_SO_RCVTIMEO:
|
|
*reinterpret_cast<avs_net_timeout_t *>(option_value) = work->recv_timeout;
|
|
*option_len = sizeof(avs_net_timeout_t);
|
|
|
|
break;
|
|
|
|
case AVS_SO_NONBLOCK:
|
|
*reinterpret_cast<uint8_t *>(option_value) = work->non_blocking;
|
|
*option_len = sizeof(uint8_t);
|
|
|
|
break;
|
|
|
|
case AVS_SO_SSL_PROTOCOL:
|
|
*reinterpret_cast<uint32_t *>(option_value) = AVS_NET_PROTOCOL_SSL_TLS_V1_1;
|
|
*option_len = sizeof(uint32_t);
|
|
|
|
break;
|
|
|
|
case AVS_SO_SSL_VERIFY_CN:
|
|
*reinterpret_cast<uint8_t *>(option_value) = 0;
|
|
*option_len = sizeof(uint8_t);
|
|
|
|
break;
|
|
|
|
default:
|
|
return core::avs_net_getsockopt(work->sock_fd, option_name, option_value, option_len);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ssl_socket_bind(
|
|
struct avs_net_sock_desc_work *work,
|
|
uint32_t address,
|
|
avs_net_port_t port)
|
|
{
|
|
auto result = core::avs_net_bind(work->sock_fd, address, port);
|
|
|
|
if (result > 0) {
|
|
work->address = address;
|
|
work->port = port;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int ssl_socket_connect(
|
|
struct avs_net_sock_desc_work *work,
|
|
uint32_t address,
|
|
avs_net_port_t port)
|
|
{
|
|
constexpr uint32_t security_context_flags = ISC_REQ_CONFIDENTIALITY |
|
|
ISC_REQ_INTEGRITY |
|
|
ISC_REQ_STREAM |
|
|
ISC_REQ_SEQUENCE_DETECT |
|
|
ISC_REQ_REPLAY_DETECT;
|
|
|
|
SecBuffer send_bufs[2] {};
|
|
SecBufferDesc send_vec {};
|
|
SecBuffer recv_bufs[2] {};
|
|
SecBufferDesc recv_vec {};
|
|
CtxtHandle security_context;
|
|
ULONG attributes = 0;
|
|
SECURITY_STATUS status = SEC_E_OK;
|
|
int result = 0;
|
|
int err = 0;
|
|
|
|
char hostname[256];
|
|
|
|
memset(&security_context, 0, sizeof(security_context));
|
|
|
|
result = core::avs_net_addrinfobyaddr(address, hostname, sizeof(hostname), 1);
|
|
|
|
if (result < 0) {
|
|
log_warning("avs::ssl", "avs_net_addrinfobyaddr failed: {}", FMT_HRESULT(result));
|
|
|
|
return result;
|
|
}
|
|
|
|
work->hostname = strdup(hostname);
|
|
|
|
err = io_buf_init(&work->send_buf, IO_BUF_INITIAL_CAPACITY);
|
|
|
|
if (err != 0) {
|
|
goto send_buf_fail;
|
|
}
|
|
|
|
err = io_buf_init(&work->recv_buf, IO_BUF_INITIAL_CAPACITY);
|
|
|
|
if (err != 0) {
|
|
goto recv_buf_fail;
|
|
}
|
|
|
|
result = core::avs_net_connect(work->sock_fd, address, port);
|
|
|
|
if (result < 0) {
|
|
log_warning("avs::ssl", "avs_net_connect failed: {}", FMT_HRESULT(result));
|
|
|
|
err = result;
|
|
|
|
goto connect_fail;
|
|
}
|
|
|
|
send_vec.ulVersion = SECBUFFER_VERSION;
|
|
send_vec.pBuffers = send_bufs;
|
|
send_vec.cBuffers = std::size(send_bufs);
|
|
|
|
recv_vec.ulVersion = SECBUFFER_VERSION;
|
|
recv_vec.pBuffers = recv_bufs;
|
|
recv_vec.cBuffers = std::size(recv_bufs);
|
|
|
|
send_bufs[0].BufferType = SECBUFFER_TOKEN;
|
|
send_bufs[0].pvBuffer = work->send_buf.bytes;
|
|
send_bufs[0].cbBuffer = work->send_buf.capacity;
|
|
|
|
send_bufs[1].BufferType = SECBUFFER_EMPTY;
|
|
send_bufs[1].pvBuffer = nullptr;
|
|
send_bufs[1].cbBuffer = 0;
|
|
|
|
status = InitializeSecurityContextA(
|
|
&work->credentials_handle,
|
|
nullptr,
|
|
const_cast<SEC_CHAR *>(work->hostname),
|
|
security_context_flags,
|
|
0,
|
|
0,
|
|
nullptr,
|
|
0,
|
|
&security_context,
|
|
&send_vec,
|
|
&attributes,
|
|
nullptr);
|
|
|
|
if (status != SEC_I_CONTINUE_NEEDED) {
|
|
log_warning("ssl", "{}: InitializeSecurityContextA failed: {}", __func__, FMT_HRESULT(status));
|
|
|
|
err = core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_SC_BADMSG);
|
|
|
|
goto first_isc_fail;
|
|
}
|
|
|
|
while (status != SEC_E_OK) {
|
|
switch (status) {
|
|
case SEC_I_CONTINUE_NEEDED:
|
|
work->send_buf.pos = 0;
|
|
work->send_buf.limit = send_bufs[0].cbBuffer;
|
|
|
|
// Only send data if we need to
|
|
if (send_bufs[0].cbBuffer > 0) {
|
|
err = impl_socket_send(work, &work->send_buf);
|
|
|
|
if (err != 0) {
|
|
goto loop_fail;
|
|
}
|
|
}
|
|
|
|
work->recv_buf.pos = 0;
|
|
work->recv_buf.limit = work->recv_buf.capacity;
|
|
|
|
err = impl_socket_recv_all(work, &work->recv_buf, 100);
|
|
|
|
if (err != 0) {
|
|
goto loop_fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case SEC_E_INCOMPLETE_MESSAGE:
|
|
if (recv_bufs[1].BufferType != SECBUFFER_MISSING) {
|
|
err = core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_SC_BADMSG);
|
|
|
|
goto loop_fail;
|
|
}
|
|
|
|
err = io_buf_grow_to(&work->recv_buf, work->recv_buf.pos + recv_bufs[1].cbBuffer);
|
|
|
|
if (err != 0) {
|
|
goto loop_fail;
|
|
}
|
|
|
|
work->recv_buf.limit = work->recv_buf.capacity;
|
|
|
|
err = impl_socket_recv(work, &work->recv_buf);
|
|
|
|
if (err != 0) {
|
|
goto loop_fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case SEC_E_BUFFER_TOO_SMALL:
|
|
err = io_buf_grow(&work->send_buf);
|
|
|
|
if (err != 0) {
|
|
goto loop_fail;
|
|
}
|
|
|
|
break;
|
|
|
|
case SEC_E_WRONG_PRINCIPAL:
|
|
case SEC_E_CERT_EXPIRED:
|
|
case SEC_E_UNTRUSTED_ROOT:
|
|
case SEC_E_ALGORITHM_MISMATCH:
|
|
case SEC_E_INCOMPLETE_CREDENTIALS:
|
|
log_warning("avs::ssl", "unable to verify server/client certificate: {}", FMT_HRESULT(status));
|
|
|
|
err = core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_SC_INVAL);
|
|
|
|
goto loop_fail;
|
|
|
|
default:
|
|
log_warning("avs::ssl", "TLS handshake failed with status: {}", FMT_HRESULT(status));
|
|
|
|
err = core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_SC_BADMSG);
|
|
|
|
goto loop_fail;
|
|
}
|
|
|
|
send_bufs[0].BufferType = SECBUFFER_TOKEN;
|
|
send_bufs[0].pvBuffer = work->send_buf.bytes;
|
|
send_bufs[0].cbBuffer = work->send_buf.capacity;
|
|
|
|
send_bufs[1].BufferType = SECBUFFER_EMPTY;
|
|
send_bufs[1].pvBuffer = nullptr;
|
|
send_bufs[1].cbBuffer = 0;
|
|
|
|
recv_bufs[0].BufferType = SECBUFFER_TOKEN;
|
|
recv_bufs[0].pvBuffer = work->recv_buf.bytes;
|
|
recv_bufs[0].cbBuffer = work->recv_buf.pos;
|
|
|
|
recv_bufs[1].BufferType = SECBUFFER_EMPTY;
|
|
recv_bufs[1].pvBuffer = nullptr;
|
|
recv_bufs[1].cbBuffer = 0;
|
|
|
|
status = InitializeSecurityContextA(
|
|
&work->credentials_handle,
|
|
&security_context,
|
|
reinterpret_cast<SEC_CHAR *>(hostname),
|
|
security_context_flags,
|
|
0,
|
|
0,
|
|
&recv_vec,
|
|
0,
|
|
nullptr,
|
|
&send_vec,
|
|
&attributes,
|
|
nullptr);
|
|
}
|
|
|
|
log_misc("avs::ssl", "TLS handshake complete");
|
|
|
|
work->security_context = security_context;
|
|
|
|
tls_begin_buffer(work);
|
|
|
|
return 0;
|
|
|
|
loop_fail:
|
|
DeleteSecurityContext(&security_context);
|
|
|
|
connect_fail:
|
|
first_isc_fail:
|
|
io_buf_finish(&work->recv_buf);
|
|
|
|
recv_buf_fail:
|
|
io_buf_finish(&work->send_buf);
|
|
|
|
send_buf_fail:
|
|
return err;
|
|
}
|
|
|
|
static int ssl_socket_listen(struct avs_net_sock_desc_work *work, int backlog) {
|
|
return -1;
|
|
}
|
|
|
|
static int ssl_socket_accept(
|
|
struct avs_net_sock_desc_work *work,
|
|
void *new_sock,
|
|
uint32_t *address,
|
|
avs_net_port_t *port)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
static int ssl_socket_close(struct avs_net_sock_desc_work *work) {
|
|
return core::avs_net_close(work->sock_fd);
|
|
}
|
|
|
|
static int ssl_socket_shutdown(struct avs_net_sock_desc_work *work, int how) {
|
|
auto err = tls_send_shutdown(work);
|
|
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
err = tls_recv_shutdown(work);
|
|
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
fail:
|
|
io_buf_finish(&work->recv_buf);
|
|
|
|
return core::avs_net_shutdown(work->sock_fd, how);
|
|
}
|
|
|
|
static int ssl_socket_sendtov(
|
|
struct avs_net_sock_desc_work *work,
|
|
const struct avs_iovec *iovec,
|
|
int iov_count,
|
|
uint32_t address,
|
|
avs_net_port_t port)
|
|
{
|
|
struct io_buf send_io_buf;
|
|
int err = 0;
|
|
int result = -1;
|
|
int bytes_sent = 0;
|
|
|
|
for (int i = 0; i < iov_count; i++) {
|
|
auto iovp = &iovec[i];
|
|
auto iov_len = iovp->iov_len;
|
|
|
|
err = io_buf_init(&send_io_buf, IO_BUF_INITIAL_CAPACITY);
|
|
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
err = io_buf_append(&send_io_buf, iovp->iov_base, &iov_len);
|
|
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
err = io_buf_flip(&send_io_buf);
|
|
|
|
if (err != 0) {
|
|
goto fail;
|
|
}
|
|
|
|
result = tls_send(work, &send_io_buf);
|
|
|
|
if (result < 0) {
|
|
log_warning("avs::ssl", "tls_send failed: {}", result);
|
|
|
|
return result;
|
|
}
|
|
|
|
// Use the original length
|
|
bytes_sent += iovp->iov_len;
|
|
|
|
io_buf_finish(&send_io_buf);
|
|
}
|
|
|
|
result = bytes_sent;
|
|
|
|
fail:
|
|
return result;
|
|
}
|
|
|
|
static int ssl_socket_recvfromv(
|
|
struct avs_net_sock_desc_work *work,
|
|
struct avs_iovec *iovec,
|
|
int iov_count,
|
|
uint32_t *address,
|
|
avs_net_port_t *port)
|
|
{
|
|
struct io_buf recv_io_buf;
|
|
int result = -1;
|
|
int bytes_received = 0;
|
|
|
|
for (int i = 0; i < iov_count; i++) {
|
|
auto iovp = &iovec[i];
|
|
auto iov_len = iovp->iov_len;
|
|
|
|
if (!iov_len) {
|
|
continue;
|
|
}
|
|
|
|
recv_io_buf.bytes = reinterpret_cast<uint8_t *>(iovp->iov_base);
|
|
recv_io_buf.pos = 0;
|
|
recv_io_buf.limit = iov_len;
|
|
recv_io_buf.capacity = iov_len;
|
|
|
|
result = tls_recv(work, &recv_io_buf);
|
|
|
|
if (result < 0) {
|
|
|
|
// connection closed returns -2, convert to -1
|
|
if (result == -2) {
|
|
result = -1;
|
|
} else {
|
|
log_warning("avs::ssl", "{}: tls_recv failed: {}", __func__, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bytes_received += recv_io_buf.pos;
|
|
}
|
|
|
|
if (address != nullptr) {
|
|
*address = work->address;
|
|
}
|
|
|
|
if (port != nullptr) {
|
|
*port = work->port;
|
|
}
|
|
|
|
#if 0
|
|
log_warning("avs::ssl", "socket({}) received {} bytes", work->sock_fd, bytes_received);
|
|
#endif
|
|
|
|
return bytes_received;
|
|
}
|
|
|
|
static int ssl_socket_pollfds_add(
|
|
struct avs_net_sock_desc_work *work,
|
|
struct avs_net_poll_fd_opaque *fds,
|
|
avs_net_pollfds_size_t fds_size,
|
|
struct avs_net_poll_fd *events)
|
|
{
|
|
if (work->sock_fd < 0) {
|
|
return core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_NET_TIMEOUT);
|
|
}
|
|
|
|
return core::avs_net_pollfds_add(work->sock_fd, fds, fds_size, events);
|
|
}
|
|
|
|
static int ssl_socket_pollfds_get(
|
|
struct avs_net_sock_desc_work *work,
|
|
struct avs_net_poll_fd *events,
|
|
struct avs_net_poll_fd_opaque *fds)
|
|
{
|
|
if (work->sock_fd < 0) {
|
|
return core::avs_error_make(AVS_ERROR_CLASS_NET, AVS_ERROR_SUBCLASS_NET_TIMEOUT);
|
|
}
|
|
|
|
return core::avs_net_pollfds_get(work->sock_fd, events, fds);
|
|
}
|
|
|
|
static int ssl_socket_sockpeer(
|
|
struct avs_net_sock_desc_work *work,
|
|
bool peer_name,
|
|
uint32_t *address,
|
|
avs_net_port_t *port)
|
|
{
|
|
if (peer_name) {
|
|
return core::avs_net_get_peername(work->sock_fd, address, port);
|
|
}
|
|
|
|
return core::avs_net_get_sockname(work->sock_fd, address, port);
|
|
}
|
|
|
|
static struct core::avs_net_protocol_ops ssl_protocol_ops {
|
|
.protocol_initialize = ssl_protocol_initialize,
|
|
.protocol_finalize = ssl_protocol_finalize,
|
|
.allocate_socket = ssl_allocate_socket,
|
|
.free_socket = ssl_free_socket,
|
|
.initialize_socket = ssl_initialize_socket,
|
|
.finalize_socket = ssl_finalize_socket,
|
|
.setsockopt = ssl_setsockopt,
|
|
.getsockopt = ssl_socket_getsockopt,
|
|
.bind = ssl_socket_bind,
|
|
.connect = ssl_socket_connect,
|
|
.listen = ssl_socket_listen,
|
|
.accept = ssl_socket_accept,
|
|
.close = ssl_socket_close,
|
|
.shutdown = ssl_socket_shutdown,
|
|
.sendtov = ssl_socket_sendtov,
|
|
.recvfromv = ssl_socket_recvfromv,
|
|
.pollfds_add = ssl_socket_pollfds_add,
|
|
.pollfds_get = ssl_socket_pollfds_get,
|
|
.sockpeer = ssl_socket_sockpeer
|
|
};
|
|
|
|
static struct core::avs_net_protocol ssl_protocol {
|
|
.ops = &ssl_protocol_ops,
|
|
.magic = core::AVS_NET_PROTOCOL_MAGIC,
|
|
.protocol_id = SSL_PROTOCOL_ID,
|
|
.proto_work_size = sizeof(struct avs_net_proto_desc_work),
|
|
.sock_work_size = sizeof(struct avs_net_sock_desc_work),
|
|
};
|
|
|
|
static struct core::avs_net_protocol_legacy ssl_protocol_legacy {
|
|
.ops = &ssl_protocol_ops,
|
|
.protocol_id = SSL_PROTOCOL_ID,
|
|
.mystery = 0,
|
|
.sz_work = sizeof(struct avs_net_sock_desc_work),
|
|
};
|
|
|
|
void init() {
|
|
log_info("ssl", "initializing");
|
|
|
|
if (!core::avs_net_add_protocol) {
|
|
log_warning("ssl", "missing optional avs imports which are required for this module to work");
|
|
return;
|
|
}
|
|
|
|
core::avs_net_del_protocol(SSL_PROTOCOL_ID);
|
|
|
|
int regist_res = 0;
|
|
if (core::VERSION == core::AVSLEGACY) {
|
|
core::avs_net_add_protocol_legacy(&ssl_protocol_legacy);
|
|
} else {
|
|
core::avs_net_add_protocol(&ssl_protocol);
|
|
}
|
|
|
|
if (regist_res) {
|
|
log_fatal("ssl", "failed to register protocol");
|
|
}
|
|
}
|
|
}
|
|
}
|