Skip to content

Commit

Permalink
Connect to Pokemon Showdown over TLS.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidstone committed May 21, 2024
1 parent 63439c3 commit 0637753
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 16 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ target_compile_definitions(Boost::headers INTERFACE
BOOST_CHRONO_HEADER_ONLY
)

find_package(OpenSSL REQUIRED)

find_package(TBB REQUIRED)

add_library(nlohmann_json INTERFACE IMPORTED)
Expand Down Expand Up @@ -535,6 +537,7 @@ target_link_libraries(tm_pokemon_showdown
bounded
concurrent
containers
OpenSSL::SSL
tm_common
tm_clients
strict_defaults
Expand Down
63 changes: 47 additions & 16 deletions source/tm/clients/pokemon_showdown/sockets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,63 @@
module;

#include <std_module/prelude.hpp>

#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>

#include <openssl/ssl.h>

export module tm.clients.ps.sockets;

import containers;
import std_module;

namespace technicalmachine::ps {

using namespace std::string_view_literals;

namespace http = boost::beast::http;
using tcp = boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using Websocket = boost::beast::websocket::stream<boost::beast::ssl_stream<tcp::socket>>;

auto make_ssl_context() -> ssl::context {
auto context = ssl::context(ssl::context::tlsv13_client);
context.set_default_verify_paths();
return context;
}

export struct Sockets {
Sockets(std::string_view const host, std::string_view const port, std::string_view const resource):
m_socket(make_connected_socket(host, port)),
m_websocket(m_socket)
m_ssl(make_ssl_context()),
m_websocket(m_io, m_ssl)
{
m_websocket.handshake(host, resource);
auto resolver = tcp::resolver(m_io);
boost::asio::connect(boost::beast::get_lowest_layer(m_websocket), resolver.resolve(host, port));
// Set SNI Hostname (many hosts need this to handshake successfully)
if(!SSL_set_tlsext_host_name(m_websocket.next_layer().native_handle(), std::string(host).c_str())) {
throw boost::beast::system_error(
boost::beast::error_code(
static_cast<int>(::ERR_get_error()),
boost::asio::error::get_ssl_category()
),
"Failed to set SNI Hostname"
);
}

m_websocket.next_layer().handshake(ssl::stream_base::client);

m_websocket.handshake(
std::string_view(containers::concatenate<containers::string>(host, ":"sv, port)),
resource
);
}

Sockets(Sockets &&) = delete;
Expand All @@ -46,28 +82,23 @@ export struct Sockets {
}

auto authenticate(std::string_view const host, std::string_view const port, http::request<http::string_body> const & request) -> http::response<http::string_body> {
auto socket = make_connected_socket(host, port);
auto socket = tcp::socket(m_io);
auto resolver = tcp::resolver(m_io);
boost::asio::connect(socket, resolver.resolve(host, port));

http::write(socket, request);

auto buffer = boost::beast::flat_buffer{};
auto response = http::response<http::string_body>{};
auto buffer = boost::beast::flat_buffer();
auto response = http::response<http::string_body>();
http::read(socket, buffer, response);
return response;
}

private:
auto make_connected_socket(std::string_view const host, std::string_view const port) -> tcp::socket {
auto socket = tcp::socket(m_io);
auto resolver = tcp::resolver(m_io);
boost::asio::connect(socket, resolver.resolve(host, port));
return socket;
}

boost::beast::flat_buffer m_buffer;
boost::asio::io_context m_io;
tcp::socket m_socket;
boost::beast::websocket::stream<tcp::socket &> m_websocket;
ssl::context m_ssl;
Websocket m_websocket;
};

} // namespace technicalmachine::ps

0 comments on commit 0637753

Please sign in to comment.