view C++/modules/Socket/SocketSsl.cpp @ 388:131ecc6ce7b5

Socket: fix kqueue with new names
author David Demelier <markand@malikania.fr>
date Tue, 30 Jun 2015 13:53:40 +0200
parents 743b3a1c71c8
children e292f0fa5395
line wrap: on
line source

/*
 * SocketSsl.cpp -- OpenSSL extension for sockets
 *
 * Copyright (c) 2013, David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#if 0

#include "SocketAddress.h"
#include "SocketSsl.h"

namespace {

inline auto sslMethod(int type) noexcept
{
	if (type == SocketSslOptions::SSLv3)
		return SSLv3_method();
	if (type == SocketSslOptions::TLSv1)
		return TLSv1_method();

	throw std::invalid_argument("unknown method selected");
}

inline std::string sslError(int error)
{
	return ERR_reason_error_string(error);
}

} // !namespace

std::mutex SocketAbstractSsl::s_sslMutex;
std::atomic<bool> SocketAbstractSsl::s_sslInitialized{false};

SocketSsl::SocketSsl(SocketTcp<Address> sc, SSL_CTX *context, SSL *ssl)
	: SocketTcp{std::move(*sc)}
	, m_context{context, SSL_CTX_free}
	, m_ssl{ssl, SSL_free}
{
#if !defined(SOCKET_NO_SSL_INIT)
	if (!s_sslInitialized) {
		sslInitialize();
	}
#endif
}

SocketSsl::SocketSsl(int family, int protocol, SocketSslOptions options)
	: SocketTcp{family, protocol}
	, m_context{nullptr, nullptr}
	, m_ssl{nullptr, nullptr}
	, m_options{std::move(options)}
{
#if !defined(SOCKET_NO_SSL_INIT)
	if (!s_sslInitialized) {
		sslInitialize();
	}
#endif
	m_context = ContextHandle{SSL_CTX_new(sslMethod(m_options.method)), SSL_CTX_free};
	m_ssl = SslHandle{SSL_new(m_context.get()), SSL_free};

	SSL_set_fd(m_ssl.get(), m_handle);
}

void SocketSsl::connect(const std::unique_ptr<SocketAddress> &address)
{
	// 1. Standard connect
	SocketTcp::connect(address);

	// 2. OpenSSL handshake
	auto ret = SSL_connect(m_ssl.get());

	if (ret <= 0) {
		auto error = SSL_get_error(m_ssl.get(), ret);

		if (error == SSL_ERROR_WANT_READ) {
			throw SocketError(SocketError::WouldBlockRead, "connect", "Operation in progress");
		} else if (error == SSL_ERROR_WANT_WRITE) {
			throw SocketError(SocketError::WouldBlockWrite, "connect", "Operation in progress");
		} else {
			throw SocketError(SocketError::System, "connect", sslError(error));
		}
	}

	m_state = SocketState::Connected;
}

std::unique_ptr<SocketTcp> SocketSsl::accept(std::unique_ptr<SocketAddress> &info)
{
	auto client = SocketTcp::accept(info);
	auto context = SSL_CTX_new(sslMethod(m_options.method));

	if (m_options.certificate.size() > 0)
		SSL_CTX_use_certificate_file(context, m_options.certificate.c_str(), SSL_FILETYPE_PEM);
	if (m_options.privateKey.size() > 0)
		SSL_CTX_use_PrivateKey_file(context, m_options.privateKey.c_str(), SSL_FILETYPE_PEM);
	if (m_options.verify && !SSL_CTX_check_private_key(context)) {
		throw SocketError(SocketError::System, "accept", "certificate failure");
	}

	// SSL object
	auto ssl = SSL_new(context);

	SSL_set_fd(ssl, client->handle());

	auto ret = SSL_accept(ssl);

	if (ret <= 0) {
		auto error = SSL_get_error(ssl, ret);

		if (error == SSL_ERROR_WANT_READ) {
			throw SocketError(SocketError::WouldBlockRead, "accept", "Operation would block");
		} else if (error == SSL_ERROR_WANT_WRITE) {
			throw SocketError(SocketError::WouldBlockWrite, "accept", "Operation would block");
		} else {
			throw SocketError(SocketError::System, "accept", sslError(error));
		}
	}

	return std::make_unique<SocketSsl>(std::move(client), context, ssl);
}

unsigned SocketSsl::recv(void *data, unsigned len)
{
	auto nbread = SSL_read(m_ssl.get(), data, len);

	if (nbread <= 0) {
		auto error = SSL_get_error(m_ssl.get(), nbread);

		if (error == SSL_ERROR_WANT_READ) {
			throw SocketError(SocketError::WouldBlockRead, "recv", "Operation would block");
		} else if (error == SSL_ERROR_WANT_WRITE) {
			throw SocketError(SocketError::WouldBlockWrite, "recv", "Operation would block");
		} else {
			throw SocketError(SocketError::System, "recv", sslError(error));
		}
	}

	return nbread;
}

unsigned SocketSsl::send(const void *data, unsigned len)
{
	auto nbread = SSL_write(m_ssl.get(), data, len);

	if (nbread <= 0) {
		auto error = SSL_get_error(m_ssl.get(), nbread);

		if (error == SSL_ERROR_WANT_READ) {
			throw SocketError(SocketError::WouldBlockRead, "send", "Operation would block");
		} else if (error == SSL_ERROR_WANT_WRITE) {
			throw SocketError(SocketError::WouldBlockWrite, "send", "Operation would block");
		} else {
			throw SocketError(SocketError::System, "send", sslError(error));
		}
	}

	return nbread;
}

#endif