#include "AdapterTls.h"
namespace Socket
{
AdapterTls::AdapterTls(
const Socket &sock,
::gnutls_priority_t priority_cache,
::gnutls_certificate_credentials_t x509_cred
) noexcept {
// https://leandromoreira.com.br/2015/10/12/how-to-optimize-nginx-configuration-for-http2-tls-ssl/
// https://www.gnutls.org/manual/html_node/False-Start.html
#ifdef GNUTLS_ENABLE_FALSE_START
constexpr int flags = GNUTLS_SERVER | GNUTLS_ENABLE_FALSE_START;
#else
constexpr int flags = GNUTLS_SERVER;
#endif
::gnutls_init(&this->session, flags);
::gnutls_priority_set(this->session, priority_cache);
::gnutls_credentials_set(this->session, GNUTLS_CRD_CERTIFICATE, x509_cred);
::gnutls_certificate_server_set_request(this->session, GNUTLS_CERT_IGNORE);
::gnutls_transport_set_int2(this->session, sock.get_handle(), sock.get_handle() );
char h2[] = "h2";
char http11[] = "http/1.1";
const ::gnutls_datum_t protocols[] {
{ reinterpret_cast(h2), sizeof(h2) - 1 },
{ reinterpret_cast(http11), sizeof(http11) - 1 },
};
::gnutls_alpn_set_protocols(this->session, protocols, sizeof(protocols) / sizeof(::gnutls_datum_t), 0);
}
AdapterTls::AdapterTls(const ::gnutls_session_t session) noexcept : session(session) {}
bool AdapterTls::handshake() noexcept
{
int ret;
::gnutls_handshake_set_timeout(this->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
do {
ret = ::gnutls_handshake(this->session);
}
while (ret < 0 && ::gnutls_error_is_fatal(ret) == 0);
if (ret < 0) {
Socket sock(this->get_handle() );
sock.close();
::gnutls_deinit(this->session);
return false;
}
return true;
}
long AdapterTls::nonblock_send_all(
const void *buf, const size_t length,
const std::chrono::milliseconds &timeout
) const noexcept {
size_t record_size = length;
if (0 == record_size) {
return -1;
}
Socket sock(this->get_handle() );
size_t total = 0;
while (total < length) {
if (record_size > length - total) {
record_size = length - total;
}
long send_size = 0;
do {
sock.nonblock_send_sync();
send_size = ::gnutls_record_send(
this->session,
reinterpret_cast(buf) + total,
record_size
);
}
while (GNUTLS_E_AGAIN == send_size);
if (send_size < 0) {
return send_size;
}
total += long(send_size);
}
return long(total);
}
System::native_socket_type AdapterTls::get_handle() const noexcept {
return static_cast(
::gnutls_transport_get_int(this->session)
);
}
::gnutls_session_t AdapterTls::get_tls_session() const noexcept {
return this->session;
}
Adapter *AdapterTls::copy() const noexcept {
return new AdapterTls(this->session);
}
long AdapterTls::nonblock_recv(
void *buf,
const size_t length,
const std::chrono::milliseconds &timeout
) const noexcept {
Socket sock(this->get_handle() );
long result;
do {
if (sock.nonblock_recv_sync(timeout) == false) {
// Timeout
result = -1;
break;
}
result = ::gnutls_record_recv(this->session, buf, length);
}
while (GNUTLS_E_AGAIN == result || GNUTLS_E_INTERRUPTED == result);
return result;
}
long AdapterTls::nonblock_send(
const void *buf,
const size_t length,
const std::chrono::milliseconds &timeout
) const noexcept {
return this->nonblock_send_all(buf, length, timeout);
}
void AdapterTls::close() noexcept {
Socket sock(this->get_handle() );
// Wait for send all data to client
sock.nonblock_send_sync();
::gnutls_bye(this->session, GNUTLS_SHUT_RDWR);
sock.close();
::gnutls_deinit(this->session);
}
}