mirror of https://github.com/axmolengine/axmol.git
260 lines
7.7 KiB
C++
260 lines
7.7 KiB
C++
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// A multi-platform support c++11 library with focus on asynchronous socket I/O for any
|
|
// client application.
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2012-2023 HALX99
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#ifndef YASIO__MBEDTLS_IMPL_HPP
|
|
#define YASIO__MBEDTLS_IMPL_HPP
|
|
|
|
#if YASIO_SSL_BACKEND == 2
|
|
|
|
YASIO__DECL yssl_ctx_st* yssl_ctx_new(const yssl_options& opts)
|
|
{
|
|
auto ctx = new yssl_ctx_st();
|
|
::mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
|
|
::mbedtls_entropy_init(&ctx->entropy);
|
|
::mbedtls_ssl_config_init(&ctx->conf);
|
|
::mbedtls_x509_crt_init(&ctx->cert);
|
|
::mbedtls_pk_init(&ctx->pkey);
|
|
|
|
do
|
|
{
|
|
int ret = 0;
|
|
// ssl role
|
|
if ((ret = ::mbedtls_ssl_config_defaults(&ctx->conf, opts.client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
|
|
YASIO_LOG("mbedtls_ssl_config_defaults fail with ret=%d", ret);
|
|
|
|
// rgn engine
|
|
cxx17::string_view pers = opts.client ? cxx17::string_view{YASIO_SSL_PIN, YASIO_SSL_PIN_LEN} : cxx17::string_view{YASIO_SSL_PON, YASIO_SSL_PON_LEN};
|
|
ret = ::mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, ::mbedtls_entropy_func, &ctx->entropy, (const unsigned char*)pers.data(), pers.length());
|
|
if (ret != 0)
|
|
{
|
|
YASIO_LOG("mbedtls_ctr_drbg_seed fail with ret=%d", ret);
|
|
break;
|
|
}
|
|
::mbedtls_ssl_conf_rng(&ctx->conf, ::mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
|
|
|
|
// --- load cert
|
|
int authmode = MBEDTLS_SSL_VERIFY_OPTIONAL;
|
|
if (yasio__valid_str(opts.crtfile_)) // the cafile_ must be full path
|
|
{
|
|
int fail_count = 0;
|
|
yssl_splitpath(opts.crtfile_, [&](char* first, char* last) {
|
|
yssl_split_term null_term(last);
|
|
|
|
if ((ret = ::mbedtls_x509_crt_parse_file(&ctx->cert, first)) != 0)
|
|
{
|
|
++fail_count;
|
|
YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
|
|
}
|
|
|
|
return !!ret;
|
|
});
|
|
if (!fail_count)
|
|
authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
|
|
}
|
|
|
|
if (opts.client)
|
|
{
|
|
::mbedtls_ssl_conf_authmode(&ctx->conf, authmode);
|
|
::mbedtls_ssl_conf_ca_chain(&ctx->conf, &ctx->cert, nullptr);
|
|
}
|
|
else
|
|
{
|
|
// --- load server private key
|
|
if (yasio__valid_str(opts.keyfile_))
|
|
{
|
|
# if MBEDTLS_VERSION_MAJOR >= 3
|
|
if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr, mbedtls_ctr_drbg_random, &ctx->ctr_drbg) != 0)
|
|
# else
|
|
if (::mbedtls_pk_parse_keyfile(&ctx->pkey, opts.keyfile_, nullptr) != 0)
|
|
# endif
|
|
{
|
|
YASIO_LOG("mbedtls_x509_crt_parse_file with ret=-0x%x", (unsigned int)-ret);
|
|
break;
|
|
}
|
|
}
|
|
::mbedtls_ssl_conf_ca_chain(&ctx->conf, ctx->cert.next, nullptr);
|
|
if ((ret = mbedtls_ssl_conf_own_cert(&ctx->conf, &ctx->cert, &ctx->pkey)) != 0)
|
|
{
|
|
YASIO_LOG("mbedtls_ssl_conf_own_cert with ret=-0x%x", (unsigned int)-ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while (false);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
YASIO__DECL void yssl_ctx_free(yssl_ctx_st*& ctx)
|
|
{
|
|
if (!ctx)
|
|
return;
|
|
::mbedtls_pk_free(&ctx->pkey);
|
|
::mbedtls_x509_crt_free(&ctx->cert);
|
|
::mbedtls_ssl_config_free(&ctx->conf);
|
|
::mbedtls_entropy_free(&ctx->entropy);
|
|
::mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
|
|
|
|
delete ctx;
|
|
ctx = nullptr;
|
|
}
|
|
|
|
YASIO__DECL int yssl_mbedtls_send(void* ctx, const unsigned char* buf, size_t len)
|
|
{
|
|
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
|
int fd = ((mbedtls_net_context*)ctx)->fd;
|
|
|
|
ret = yasio::xxsocket::send(fd, buf, static_cast<int>(len), YASIO_MSG_FLAG);
|
|
|
|
if (ret < 0)
|
|
{
|
|
int err = yasio::xxsocket::get_last_errno();
|
|
if (err == EWOULDBLOCK || err == EAGAIN)
|
|
{
|
|
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
}
|
|
|
|
# if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && !defined(EFI32)
|
|
if (WSAGetLastError() == WSAECONNRESET)
|
|
{
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
}
|
|
# else
|
|
if (errno == EPIPE || errno == ECONNRESET)
|
|
{
|
|
return MBEDTLS_ERR_NET_CONN_RESET;
|
|
}
|
|
|
|
if (errno == EINTR)
|
|
{
|
|
return MBEDTLS_ERR_SSL_WANT_WRITE;
|
|
}
|
|
# endif
|
|
|
|
return MBEDTLS_ERR_NET_SEND_FAILED;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
YASIO__DECL yssl_st* yssl_new(yssl_ctx_st* ctx, int fd, const char* hostname, bool client)
|
|
{
|
|
auto ssl = new yssl_st();
|
|
::mbedtls_ssl_init(ssl);
|
|
::mbedtls_ssl_setup(ssl, &ctx->conf);
|
|
|
|
// ssl_set_fd
|
|
ssl->bio.fd = fd;
|
|
::mbedtls_ssl_set_bio(ssl, &ssl->bio, ::yssl_mbedtls_send, ::mbedtls_net_recv, nullptr /* rev_timeout() */);
|
|
if (client)
|
|
::mbedtls_ssl_set_hostname(ssl, hostname);
|
|
return ssl;
|
|
}
|
|
YASIO__DECL void yssl_shutdown(yssl_st*& ssl, bool writable)
|
|
{
|
|
// Avoid SIGPIPE on linux, because linux not support SO_NOSIGPIPE,
|
|
// If the socket status not sync with kernel, we also pass MSG_NOSIGNAL
|
|
// to socket.send to avoid SIGPIPE, see also: yssl_mbedtls_send
|
|
if (writable)
|
|
::mbedtls_ssl_close_notify(ssl);
|
|
::mbedtls_ssl_free(ssl);
|
|
delete ssl;
|
|
ssl = nullptr;
|
|
}
|
|
YASIO__DECL int yssl_do_handshake(yssl_st* ssl, int& err)
|
|
{
|
|
int ret = ::mbedtls_ssl_handshake_step(ssl);
|
|
switch (ret)
|
|
{
|
|
case 0:
|
|
if (ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER)
|
|
return 0;
|
|
err = EWOULDBLOCK;
|
|
ret = -1;
|
|
break;
|
|
case MBEDTLS_ERR_SSL_WANT_READ:
|
|
err = EWOULDBLOCK;
|
|
break;
|
|
case MBEDTLS_ERR_SSL_WANT_WRITE:
|
|
err = EWOULDBLOCK;
|
|
break; // Nothing need to do
|
|
default:
|
|
err = yasio::errc::ssl_handshake_failed;
|
|
}
|
|
return ret;
|
|
}
|
|
const char* yssl_strerror(yssl_st* ssl, int sslerr, char* buf, size_t buflen)
|
|
{
|
|
int n = snprintf(buf, buflen, "error:%d:", sslerr);
|
|
::mbedtls_strerror(sslerr, buf + n, buflen - n);
|
|
return buf;
|
|
}
|
|
YASIO__DECL int yssl_write(yssl_st* ssl, const void* data, size_t len, int& err)
|
|
{
|
|
int n = ::mbedtls_ssl_write(ssl, static_cast<const uint8_t*>(data), len);
|
|
if (n > 0)
|
|
return n;
|
|
switch (n)
|
|
{
|
|
case MBEDTLS_ERR_SSL_WANT_READ:
|
|
case MBEDTLS_ERR_SSL_WANT_WRITE:
|
|
err = EWOULDBLOCK;
|
|
break;
|
|
default:
|
|
err = yasio::errc::ssl_write_failed;
|
|
}
|
|
return -1;
|
|
}
|
|
YASIO__DECL int yssl_read(yssl_st* ssl, void* data, size_t len, int& err)
|
|
{
|
|
int n = ::mbedtls_ssl_read(ssl, static_cast<uint8_t*>(data), len);
|
|
if (n > 0)
|
|
return n;
|
|
switch (n)
|
|
{
|
|
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: // n=0, the upper caller will regards as eof
|
|
n = 0;
|
|
case 0:
|
|
err = yasio::xxsocket::get_last_errno();
|
|
::mbedtls_ssl_close_notify(ssl);
|
|
break;
|
|
case MBEDTLS_ERR_SSL_WANT_READ:
|
|
case MBEDTLS_ERR_SSL_WANT_WRITE:
|
|
err = EWOULDBLOCK;
|
|
break;
|
|
default:
|
|
err = yasio::errc::ssl_read_failed;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|