mirror of
https://github.com/fpagliughi/sockpp.git
synced 2026-01-12 00:04:45 +08:00
TLS connector can be created without connecting so that TLS options can be set before connecting.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
// --------------------------------------------------------------------------
|
||||
// This file is part of the "sockpp" C++ socket library.
|
||||
//
|
||||
// Copyright (c) 2023 Frank Pagliughi
|
||||
// Copyright (c) 2023-2025 Frank Pagliughi
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@@ -81,7 +81,9 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sockpp::tls_connector conn{ctx, addr, ec};
|
||||
// Connect with an SNI check
|
||||
|
||||
sockpp::tls_connector conn{ctx, addr, host, ec};
|
||||
|
||||
if (ec) {
|
||||
cerr << "Error connecting to server: " << ec.message() << endl;
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
//
|
||||
// Example app showing some options for connecting to a secure server.
|
||||
//
|
||||
// This shows:
|
||||
// - Setting up a TLS client context, w/ options from the command line
|
||||
// - Althoug these could be done in a single call, this uses separate
|
||||
// steps to:
|
||||
// - Resolve the server address
|
||||
// - Make an insecure connection
|
||||
// - Make the TLS connector from the insecure TCP connector
|
||||
//
|
||||
// --------------------------------------------------------------------------
|
||||
// This file is part of the "sockpp" C++ socket library.
|
||||
//
|
||||
@@ -38,6 +46,7 @@
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
@@ -49,24 +58,28 @@
|
||||
#include "sockpp/version.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const string DEFAULT_HOST = "example.org";
|
||||
const in_port_t DEFAULT_PORT = 443;
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
bool verify = false;
|
||||
string trustStore, certFile, keyFile;
|
||||
string trustStore, certFile, keyFile, hostName;
|
||||
|
||||
int c, iOpt;
|
||||
static option longOpts[] = {
|
||||
{"verify", no_argument, 0, 'v'},
|
||||
{"trust-store", required_argument, 0, 't'},
|
||||
{"cert", required_argument, 0, 'c'},
|
||||
{"key", required_argument, 0, 'k'},
|
||||
{0, 0, 0, 0}
|
||||
{"verify", no_argument, 0, 'v'}, {"trust-store", required_argument, 0, 't'},
|
||||
{"cert", required_argument, 0, 'c'}, {"key", required_argument, 0, 'k'},
|
||||
{"hostname", required_argument, 0, 'h'}, {0, 0, 0, 0}
|
||||
};
|
||||
|
||||
cout << "Sample TLS test connector for 'sockpp' " << sockpp::SOCKPP_VERSION << '\n'
|
||||
<< endl;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "t:c:k:v", longOpts, &iOpt)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "t:c:k:h:v", longOpts, &iOpt)) != -1) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
verify = true;
|
||||
@@ -84,6 +97,10 @@ int main(int argc, char* argv[]) {
|
||||
keyFile = string{optarg};
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
hostName = string(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
cerr << "Unknown option: " << optarg << endl;
|
||||
return 1;
|
||||
@@ -92,9 +109,13 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
int narg = argc - optind;
|
||||
|
||||
string host = (narg > 0) ? argv[optind] : "example.org";
|
||||
string host = (narg > 0) ? argv[optind] : DEFAULT_HOST;
|
||||
in_port_t port = (narg > 1) ? atoi(argv[optind + 1]) : 443;
|
||||
|
||||
// The default host requires SNI
|
||||
if (host == DEFAULT_HOST)
|
||||
hostName = host;
|
||||
|
||||
sockpp::initialize();
|
||||
|
||||
auto ctxBldr = sockpp::tls_context_builder::client();
|
||||
@@ -132,13 +153,35 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sockpp::tls_connector conn{ctx, addr, ec};
|
||||
cout << "Connecting to server at " << host << ':' << port << endl;
|
||||
sockpp::tcp_connector tcp_conn{addr, 10s, ec};
|
||||
|
||||
if (ec) {
|
||||
cerr << "Error connecting to server: " << ec.message() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "Securing the connection..." << endl;
|
||||
sockpp::tls_connector conn{ctx};
|
||||
|
||||
if (ec) {
|
||||
cerr << "Error creating the TLS connector: " << ec.message() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!hostName.empty()) {
|
||||
cout << "Using SNI host name: " << hostName << "..." << endl;
|
||||
if (auto res = conn.set_host_name(hostName); !res) {
|
||||
cerr << "Error: " << res.error_message() << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto res = conn.tls_connect(std::move(tcp_conn)); !res) {
|
||||
cerr << "Error securing the connection: " << res.error_message() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "Successful connection to " << addr << endl;
|
||||
|
||||
if (auto opt_cert = conn.peer_certificate(); !opt_cert) {
|
||||
@@ -161,7 +204,7 @@ int main(int argc, char* argv[]) {
|
||||
ofstream pemfil("peer.pem");
|
||||
auto pem = cert.to_pem();
|
||||
pemfil.write(pem.data(), pem.size());
|
||||
cout << "\nWrote peer certificate to peer.pem" << endl;
|
||||
cout << "Wrote peer certificate to peer.pem" << endl;
|
||||
}
|
||||
|
||||
if (auto res = conn.write("HELO"); !res) {
|
||||
@@ -170,11 +213,15 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "\nSuccessfully wrote to server." << endl;
|
||||
|
||||
/*
|
||||
char buf[512];
|
||||
if (auto res = conn.read(buf, sizeof(buf)); !res) {
|
||||
cerr << "Error: " << res.error_message() << endl;
|
||||
return 1;
|
||||
}
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,21 @@ class tls_connector : public tls_socket
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a TLS connector and connects to the server.
|
||||
* Creates an unconnected TLS connector.
|
||||
* @param ctx The TLS context.
|
||||
* @throws std::system_error If it fails to find the server
|
||||
* @throws tls_error If it fails to make a secure TLS connection
|
||||
*/
|
||||
tls_connector(const tls_context& ctx) : base{ctx} {}
|
||||
/**
|
||||
* Creates an unconnected TLS connector.
|
||||
* Once created, TLS options can be set on the object
|
||||
* @param ctx The TLS context.
|
||||
* @param ec Gets the error code on failure
|
||||
*/
|
||||
tls_connector(const tls_context& ctx, error_code& ec) noexcept : base{ctx, ec} {}
|
||||
/**
|
||||
* Creates a TLS connector and attempts to connect to the server.
|
||||
* @param ctx The TLS context.
|
||||
* @param addr The address of the remote server.
|
||||
* @throws std::system_error If it fails to find the server
|
||||
@@ -65,14 +79,36 @@ public:
|
||||
if (auto res = tls_connect(); !res)
|
||||
throw tls_error{res.error()};
|
||||
}
|
||||
/**
|
||||
* Creates a TLS connector, connects to the server, and checks the SNI
|
||||
* host name..
|
||||
* @param ctx The TLS context.
|
||||
* @param addr The address of the remote server.
|
||||
* @param hostname The host name for an
|
||||
* @throws std::system_error If it fails to find the server
|
||||
* @throws tls_error If it fails to make a secure TLS connection
|
||||
*/
|
||||
tls_connector(const tls_context& ctx, const sock_address& addr, string& hostname)
|
||||
: base{ctx, connector{addr}} {
|
||||
if (auto res = set_host_name(hostname); !res)
|
||||
throw tls_error{res.error()};
|
||||
if (auto res = tls_connect(); !res)
|
||||
throw tls_error{res.error()};
|
||||
}
|
||||
/**
|
||||
* Creates a TLS connector and connects to the server.
|
||||
* @param ctx The TLS context.
|
||||
* @param addr The address of the remote server.
|
||||
* @param ec Gets the error code on failure
|
||||
*/
|
||||
tls_connector(const tls_context& ctx, const sock_address& addr, error_code& ec) noexcept
|
||||
tls_connector(
|
||||
const tls_context& ctx, const sock_address& addr, string& hostname, error_code& ec
|
||||
) noexcept
|
||||
: base{ctx, connector{addr}, ec} {
|
||||
if (!ec) {
|
||||
if (auto res = set_host_name(hostname); !res)
|
||||
ec = res.error();
|
||||
}
|
||||
if (!ec) {
|
||||
if (auto res = tls_connect(); !res)
|
||||
ec = res.error();
|
||||
@@ -114,13 +150,24 @@ public:
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Connect the TLS session.
|
||||
* This assumes that the underlying, insecure connection has already
|
||||
* been made, and then this call secures the connection.
|
||||
* @return The error code on failure.
|
||||
*/
|
||||
result<> tls_connect() noexcept { return tls_check_res_none(::SSL_connect(ssl())); }
|
||||
/**
|
||||
* Connect the TLS session.
|
||||
* This assumes that the underlying, insecure, connection has already
|
||||
* been made.
|
||||
* @return The error code on failure.
|
||||
*/
|
||||
result<> tls_connect() noexcept { return tls_check_res_none(::SSL_connect(ssl())); }
|
||||
result<> tls_connect(stream_socket&& sock) noexcept {
|
||||
if (auto res = attach(std::move(sock)); !res)
|
||||
return res;
|
||||
return tls_check_res_none(::SSL_connect(ssl()));
|
||||
}
|
||||
/**
|
||||
* Connect the TLS session.
|
||||
* @return The error code on failure.
|
||||
|
||||
@@ -85,6 +85,18 @@ class tls_socket : public stream_socket
|
||||
tls_socket& operator=(const socket&) = delete;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new, unconnected, TLS socket.
|
||||
* @param ctx The TLS context
|
||||
* @throws tls_error on failure
|
||||
*/
|
||||
tls_socket(const tls_context& ctx);
|
||||
/**
|
||||
* Creates a new, unconnected, TLS socket.
|
||||
* @param ctx The TLS context
|
||||
* @param ec The error code on failure
|
||||
*/
|
||||
tls_socket(const tls_context& ctx, error_code& ec) noexcept;
|
||||
/**
|
||||
* Creates a new TLS socket from an existing stream socket.
|
||||
* @param ctx The TLS context
|
||||
@@ -122,7 +134,12 @@ public:
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
tls_socket& operator=(tls_socket&& rhs);
|
||||
|
||||
/**
|
||||
* Attach an insecure stream socket to this object.
|
||||
* @param sock A stream socket
|
||||
*/
|
||||
result<> attach(stream_socket&& sock) noexcept;
|
||||
;
|
||||
/**
|
||||
* Returns the peer's X.509 certificate.
|
||||
*/
|
||||
@@ -140,6 +157,14 @@ public:
|
||||
string peer_certificate_status_message();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets the Server Name Indication (SNI) for use by Secure Sockets
|
||||
* Layer (SSL).
|
||||
*
|
||||
* Call this before the TLS handshake.
|
||||
*/
|
||||
result<> set_host_name(const string& hostname);
|
||||
|
||||
// I/O primitives
|
||||
|
||||
using base::read;
|
||||
|
||||
@@ -99,7 +99,8 @@ result<size_t> stream_socket::read(const std::vector<iovec>& ranges) {
|
||||
#else
|
||||
std::vector<WSABUF> bufs;
|
||||
for (const auto& iovec : ranges) {
|
||||
bufs.push_back({static_cast<ULONG>(iovec.iov_len), static_cast<CHAR*>(iovec.iov_base)}
|
||||
bufs.push_back(
|
||||
{static_cast<ULONG>(iovec.iov_len), static_cast<CHAR*>(iovec.iov_base)}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,7 +160,8 @@ result<size_t> stream_socket::write(const std::vector<iovec>& ranges) {
|
||||
#else
|
||||
std::vector<WSABUF> bufs;
|
||||
for (const auto& iovec : ranges) {
|
||||
bufs.push_back({static_cast<ULONG>(iovec.iov_len), static_cast<CHAR*>(iovec.iov_base)}
|
||||
bufs.push_back(
|
||||
{static_cast<ULONG>(iovec.iov_len), static_cast<CHAR*>(iovec.iov_base)}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,17 @@ namespace sockpp {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tls_socket::tls_socket(const tls_context& ctx) : ssl_{::SSL_new(ctx.ctx_)} {
|
||||
if (!ssl_)
|
||||
throw tls_error::from_last_error();
|
||||
}
|
||||
|
||||
tls_socket::tls_socket(const tls_context& ctx, error_code& ec) noexcept
|
||||
: ssl_{::SSL_new(ctx.ctx_)} {
|
||||
if (!ssl_)
|
||||
ec = tls_last_error();
|
||||
}
|
||||
|
||||
tls_socket::tls_socket(const tls_context& ctx, stream_socket&& sock)
|
||||
: base{std::move(sock)}, ssl_{::SSL_new(ctx.ctx_)} {
|
||||
if (!ssl_)
|
||||
@@ -73,6 +84,17 @@ tls_socket& tls_socket::operator=(tls_socket&& rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
result<> tls_socket::attach(stream_socket&& sock) noexcept {
|
||||
// TODO: Implement this
|
||||
// if (!ssl_)
|
||||
// throw tls_error::from_last_error();
|
||||
|
||||
auto h = sock.release();
|
||||
base::reset(h);
|
||||
|
||||
return tls_check_res_none(::SSL_set_fd(ssl_, h));
|
||||
}
|
||||
|
||||
std::optional<tls_certificate> tls_socket::peer_certificate() {
|
||||
// TODO: Implement this
|
||||
X509* cert = SSL_get1_peer_certificate(ssl_);
|
||||
@@ -97,6 +119,10 @@ string tls_socket::peer_certificate_status_message() {
|
||||
}
|
||||
#endif
|
||||
|
||||
result<> tls_socket::set_host_name(const string& hostname) {
|
||||
return tls_check_res_none(::SSL_set_tlsext_host_name(ssl_, hostname.c_str()));
|
||||
}
|
||||
|
||||
result<size_t> tls_socket::read(void* buf, size_t n) {
|
||||
size_t nx;
|
||||
int ret = ::SSL_read_ex(ssl_, buf, n, &nx);
|
||||
|
||||
Reference in New Issue
Block a user