SSL context creation crashes of c++ native module in Electron application
Tags: question, cpp, websocketpp, ssl
Published: 2024-06-17 02:33:16

I am building a C++ native module to be used in an Electron application. The native module is responsible for communicating with a WebSocket server. I am using the WebSocketPP library and the following sample code:

index.cc

```
#include <websocketpp/config/asio_client.hpp>   // TLS
#include <websocketpp/client.hpp>
typedef websocketpp::client<websocketpp::config::asio_tls_client> client;
typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
using websocketpp::lib::bind;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
//
...
//
class WebSocketHandler
{
public:
    void set(const std::string &url, const std::string &token)
    {
        ws_url = url;
        authorizationHeader = "Bearer " + token;
        // Initialize ASIO
        _webSocket.init_asio();

        // Set logging to be pretty verbose (everything except message payloads)
        _webSocket.set_access_channels(websocketpp::log::alevel::all);
        _webSocket.clear_access_channels(websocketpp::log::alevel::frame_payload);

        // Set open handler
        _webSocket.set_open_handler(bind(&WebSocketHandler::on_open, this, std::placeholders::_1));

        // Set close handler
        _webSocket.set_close_handler(bind(&WebSocketHandler::on_close, this, std::placeholders::_1));

        // Set fail handler
        _webSocket.set_fail_handler(bind(&WebSocketHandler::on_fail, this, std::placeholders::_1));

        // Set message handler
        _webSocket.set_message_handler(bind(&WebSocketHandler::on_message, this, std::placeholders::_1, std::placeholders::_2));
        // Set TLS handler
        _webSocket.set_tls_init_handler(bind(&WebSocketHandler::on_tls_init, this, std::placeholders::_1));
    }

    void start()
    {
        websocketpp::lib::error_code ec;

        client::connection_ptr con = _webSocket.get_connection(ws_url, ec);
        if (ec)
        {
            std::cout << "Could not create connection because: " << ec.message() << std::endl;
            return;
        }

        // Set the authorization header
        con->replace_header("Authorization", authorizationHeader);

        // Connect to server
        _webSocket.connect(con);

        // Start the ASIO io_service run loop
        _thread.reset(new websocketpp::lib::thread(&client::run, &_webSocket));
    }

    void stop()
    {
        _webSocket.stop();
        if (_thread && _thread->joinable())
        {
            _thread->join();
        }
    }

private:
    context_ptr on_tls_init(websocketpp::connection_hdl hdl)
    {
        context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);  // crash at this line

        try {
            // Simplified SSL options for testing
            ctx->set_options(boost::asio::ssl::context::default_workarounds |
                             boost::asio::ssl::context::no_sslv2 |
                             boost::asio::ssl::context::no_sslv3 |
                             boost::asio::ssl::context::single_dh_use);
            std::cout << "SSL options set successfully" << std::endl;
        } catch (std::exception &e) {
            std::cout << "Exception during set_options: " << e.what() << std::endl;
        }

        return ctx;
    }

    void on_open(websocketpp::connection_hdl hdl)
    {
        std::cout << "connection opened" << std::endl;
    }

    void on_close(websocketpp::connection_hdl hdl)
    {
        std::cout << "connection closed" << std::endl;
    }

    void on_fail(websocketpp::connection_hdl hdl)
    {
        std::cout << "connection failed" << std::endl;
    }

    void on_message(websocketpp::connection_hdl hdl, client::message_ptr msg)
    {
        std::cout << "message arrived" << std::endl;
    }

    client _webSocket;
    std::string ws_url;
    std::string authorizationHeader;
};
//
...
//
WebSocketHandler handler;
handler.set("wss://echo.websocket.org/", "Token_xxxx");
handler.start();
....
handler.stop();
```
binding.gyp

```
{
  "targets": [
    {
      "target_name": "binding",
      "include_dirs": [
        "<!@(node -p \"require('node-addon-api').include\")",
        "<(module_root_dir)/include"
      ],
      "conditions": [
        ['OS=="win"', {
          "sources": [
            "./src/index.cc"
          ],
          "configurations": {
            "Debug": {
              "msvs_settings": {
                "VCCLCompilerTool": {
                  "RuntimeLibrary": "0", 
                  "ExceptionHandling": "1"
                },
              },
            },
            "Release": {
              "msvs_settings": {
                "VCCLCompilerTool": {
                  "RuntimeLibrary": "0", 
                  "ExceptionHandling": "1"
                },
              },
            },
          },
          "libraries": [
            "-lws2_32",
            "-lShlwapi"
          ]
        }]
      ]
    }
  ],
}
```
Test Script

```
const engine = require("../bin/binding.node");
const test = async () => {
    try {
        engine.startConnection();
    } catch (err) {
        console.log("Error occurred", err);
    }
};
test();
```
Problem The module works correctly in a JavaScript test script but crashes in Electron at this line:

context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
I suspect the issue might be related to the way SSL libraries are linked. I feel linking SSL libraries statically might resolve the issue, but I am unsure how to achieve this. I tested with other several libraries based in boost but the result was same. It keeps crahsed in ssl context creation part only in electron application.

Environment

C++14/17
Electron v23(version upgrade doesn't help)
WebSocketPP 0.8.2
Node 16.14.2/18.x.x
Dependencies installed using vcpkg: OpenSSL, WebSocketPP, Boost
Question

How can I link SSL libraries statically in my project to potentially fix this issue? Are there any other possible solutions or insights regarding this problem?

Thank you for your assistance!