Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to set SNI & connect to wss:// ? #71

Open
davidtwomey opened this issue Mar 2, 2022 · 2 comments
Open

How to set SNI & connect to wss:// ? #71

davidtwomey opened this issue Mar 2, 2022 · 2 comments

Comments

@davidtwomey
Copy link

davidtwomey commented Mar 2, 2022

Hi @chronoxor,

Really liking the design of CppServer!....however, unfortunately, I am running into issues attempting to connect to a wss:// endpoint.

In general, I am unable to get a simple example of connecting working so any help explaining how to correctly resolve and connect to a websocket endpoint specified by a wss:// uri would be greatly appreciated!

For example to connect to wss://ws.okx.com:8443/ws/v5/public (taken from here) I attempted the following based on the wss client example:

 // ...int main() {

  // Create a new Asio service
  auto service = std::make_shared<AsioService>();
  service->Start();


  // Create and prepare a new SSL client context
  auto context = std::make_shared<CppServer::Asio::SSLContext>(asio::ssl::context::tlsv12);
  context->set_default_verify_paths();
  context->set_root_certs();
  context->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
  context->load_verify_file("../external/CppServer/tools/certificates/ca.pem");

  // Use TCPResolver to lookup DNS
  // See: https://github.com/chronoxor/CppServer/issues/55
  auto resolver = std::make_shared<CppServer::Asio::TCPResolver>(service);

 // @NOTE ClientWebsocket is a child class of `public CppServer::WS::WSSClient`
  auto ws = std::make_shared<ClientWebsocket>(service, context, "ws.okx.com", 8443);
  ws->Connect(resolver);

and the upgrade request attempt:

class ClientWebsocket : public CppServer::WS::WSSClient
{
public:
  using CppServer::WS::WSSClient::WSSClient;
protected:
  void onWSConnecting(CppServer::HTTP::HTTPRequest &request) override
  {
    request.SetBegin("GET", "/ws/v5/public", "HTTP/2");
    request.SetHeader("Host", address() + ":8443"); // @todo
    request.SetHeader("Upgrade", "websocket");
    request.SetHeader("Connection", "upgrade");
    request.SetHeader("Sec-WebSocket-Key", CppCommon::Encoding::Base64Encode(ws_nonce()));
    request.SetHeader("Accept", "/");
    // request.SetHeader("Sec-WebSocket-Version", "13");
    request.SetHeader("User-Agent", "beast.v1");
  }

This did not work, and my comments are:

(1) Do I need to set SNI somewhere on the stream native handle (using SSL_set_tlsext_host_name)
(2) Why (once SNI is set) the upgrade request is not working? If you have any experience with this.


NOTE: something like this seemed to get round initial SSL v3 handshake errors

bool SSLClient::Connect(const std::shared_ptr<TCPResolver>& resolver) {
 // ...

    std::cout << "Setting SNI Hostname: " << _address << std::endl;
    if(SSL_set_tlsext_host_name(_stream.native_handle(), _address.c_str())) {
        std::cout << "Success!" << std::endl;
    }
    else {
        std::cout << "Failed!" << std::endl;
    }
// ...
@AlexSilver9
Copy link

AlexSilver9 commented Nov 27, 2022

Hi,

same here. SNI doesn't seem to work.
I tried with your URL @davidtwomey , but the same happens with wss://api.huobi.com:443/ws, while same implementation works fine with wss://stream.binance.com:443.

I managed to set the server address manually like this, but without effect. No matter if I set it before the connection is started, or later. It prints SNI Failed!:

    if (SSL_CTX_ctrl(_ssl_context->native_handle(),
        SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,
        (void *) _configuration->getWebSocketServerHost().c_str()))
    {
        std::cout << "SNI Success!" << std::endl;
    }
    else {
        std::cout << "SNI Failed!" << std::endl;
    }

The error on connecting is always the same:
WebSocketClient caught an error with code 167773200 and category 'asio.ssl': sslv3 alert handshake failure (SSL routines)

I don't even get to void onWSConnecting(CppServer::HTTP::HTTPRequest &request) callback.
I just get disconnected right on the connection attempt.

Any idea?

Thank you, Alex

@AlexSilver9
Copy link

AlexSilver9 commented Nov 27, 2022

After more research I found out that the following piece of Code now says that SNI is successful, but it is still not working:

if (SSL_set_tlsext_host_name(stream().native_handle(), host.c_str())) {
        std::cout << "SNI Success!" << std::endl;
    } else {
        std::cout << "SNI Failed!" << std::endl;
    }

Btw... same code is working on Boost::Beast and connection can be established and data received from the same source:

if ( ! SSL_set_tlsext_host_name(ws.next_layer().native_handle(), host.c_str()))
            throw beast::system_error(
                    beast::error_code(
                            static_cast<int>(::ERR_get_error()),
                            net::error::get_ssl_category()),
                    "Failed to set SNI Hostname");

Both native_handle() function documentations say that a pointer of type SSL* is returned:

Example The native_handle() function returns a pointer of type SSL* that is suitable for passing
to functions such as SSL_get_verify_result and
SSL_get_peer_certificate: asio::ssl::stream  sock(my_context, ctx); // ... establish connection
and perform handshake ... 

if (X509* cert = SSL_get_peer_certificate(sock.native_handle())) {
    if (SSL_get_verify_result(sock.native_handle()) == X509_V_OK) {
         // ... 
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants