提升ASIO SSL握手故障

发布于 2025-02-11 11:35:30 字数 2225 浏览 1 评论 0 原文

尝试使用Boost ASIO安全地连接到远程IMAP服务器时,服务器握手在每个连接上都会失败。异常消息读取:

handshake: unregistered scheme (STORE routines) [asio.ssl:369098857]

我的代码在下面( url std :: string_view 包含主机URL):

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;

boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
SSLSocket socket(context, ssl_context);

ssl_context.set_default_verify_paths();

tcp::resolver resolver(context);
auto endpoints = resolver.resolve(url, "993");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(url.data()));
socket.handshake(SSLSocket::client);

代码立即在最终行上抛出一个例外,阻塞同步握手。

前两行设置主机名验证,类似于完成。但是,这些检查似乎引起了问题,因为当它们被拆除时,握手会成功。显然,这不是一个好的解决方案。

踏上ASIO的一些内部内容后,我发现上述摘要的最后三行可以由以下方式替换:

SSL_set_verify(socket.native_handle(), SSL_VERIFY_PEER, nullptr);
socket.handshake(SSLSocket::client);

发生同样的错误。 ssl_set_verify 是一个openssl函数,设置无空回调直接导致相同问题的事实使我认为问题是我系统的OpenSSL环境而不是ASIO或主机名验证回调。但是,我无法确定错误的含义以及可能导致的误差。

这是我在故障排除时尝试过的事情的列表:

  1. 加载系统的证书(.pem)文件明确也许ASIO和/或OpenSSL无法加载正确的证书来完成验证,我在/private/etc/ssl/cert.pem 上找到了我的系统(Mac)证书文件。然后,我插入以下行:

      ssl_context.load_verify_file(“/private/etc/etc/ssl/cert.pem”);
     

    直接在 set_default_verify_paths()被调用。我的程序在不抱怨的情况下加载此证书文件,但没有解决握手错误。

  2. 首先,我使用Apple的系统版本OpenSSL(实际上是Libressl 2.8.3),使用其他版本的OpenSSL 。然后,我使用Homebrew Package Manager的OpenSSL(OpenSSL 3.0.4)重建代码。即使我尝试调用 load_verify_file ,也没有解决问题。

  3. 。连接和URL/端口号是正确的,我尝试使用以下命令通过SSL连接到IMAP服务器:

      openssl s_client -connect my.url.com:993 -crlf -verify 1
     

    它运行良好,连接到IMAP服务器并使我能够发送/接收IMAP响应。

使用OpenSSL和ASIO时,是否有人遇到过类似的问题?我对设置SSL/TLS连接并不熟悉,但是我看不出可能导致问题的原因。

感谢您的帮助!

When attempting to securely connect to a remote IMAP server using Boost ASIO, the server handshake fails on every connection. The exception message reads:

handshake: unregistered scheme (STORE routines) [asio.ssl:369098857]

My code is below (url is a std::string_view containing the host URL):

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;

boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
SSLSocket socket(context, ssl_context);

ssl_context.set_default_verify_paths();

tcp::resolver resolver(context);
auto endpoints = resolver.resolve(url, "993");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(url.data()));
socket.handshake(SSLSocket::client);

The code immediately throws an exception on the final line, which is a blocking synchronous handshake.

The prior two lines set up host name verification, similar to how it's done in the official ASIO tutorial. These checks seem to be causing an issue, however, because when they are removed the handshake succeeds. Obviously, this is not a good solution.

After stepping through some of ASIO's internals, I found that the last three lines of the above snippet could be replaced by:

SSL_set_verify(socket.native_handle(), SSL_VERIFY_PEER, nullptr);
socket.handshake(SSLSocket::client);

and the same error occurs. SSL_set_verify is an OpenSSL function, and the fact that setting a null callback directly causes the same issue makes me think that the issue is with my system's OpenSSL environment and not ASIO or the host name verification callback. However, I have not been able to determine what exactly the error means and what could be causing it.

Here is a list of things I have tried while troubleshooting:

  1. Load the system's certificate (.pem) file explicitly Thinking maybe ASIO and/or OpenSSL's were not able to load the right certificates to do the validation, I found my system's (a Mac) certificate file at /private/etc/ssl/cert.pem. I then inserted the following line:

    ssl_context.load_verify_file("/private/etc/ssl/cert.pem");
    

    directly after set_default_verify_paths() is called. My program loads this certificate file without complaining, but it doesn't fix the handshake error.

  2. Use a different version of OpenSSL At first I was using Apple's system version of OpenSSL (which is really LibreSSL 2.8.3). I then rebuilt my code using the Homebrew package manager's version of OpenSSL (OpenSSL 3.0.4). This also did not fix the issue, even when I tried calling load_verify_file as in point 1.

  3. Sanity check using the OpenSSL command-line tool To make sure my network connection and URL/port number were correct, I tried connecting to the IMAP server over SSL using the following command:

     openssl s_client -connect my.url.com:993 -crlf -verify 1
    

    and it worked fine, connecting to the IMAP server and enabling me to send/receive IMAP responses.

Has anyone encountered similar issues when using OpenSSL and ASIO? I'm not very familiar with setting up an SSL/TLS connection, but I don't see what could be causing the problem.

Thanks for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浅浅 2025-02-18 11:35:30

鉴于 openssl s_client -connect my.url.com:993 -crlf -crlf -verify 1 成功,似乎没有很多错误。一件事引起了我的注意:我将在构建SSL流之前配置上下文

ssl::context ssl_context(ssl::context::tls);

ssl_context.set_default_verify_paths();

SSLSocket socket(context, ssl_context);

另外, openssl 可能使用 sni Extensions

// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
    throw boost::system::system_error(
        ::ERR_get_error(), boost::asio::error::get_ssl_category());
}

最后,确保 url 字符串视图包含正确的数据, 显然 这是一个有效的主机名 null终止的字符串。在这种情况下,我宁愿使用保证无效终止的字符串表示

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;


int main() {
    boost::asio::io_context context;
    ssl::context ssl_context(ssl::context::tls);

    ssl_context.set_default_verify_paths();

    SSLSocket socket(context, ssl_context);

    tcp::resolver r(context);
    std::string hostname = "www.example.com";
    auto endpoints = r.resolve(hostname, "443");
    boost::asio::connect(socket.next_layer(), endpoints);
    socket.set_verify_mode(ssl::verify_peer);
    socket.set_verify_callback(ssl::host_name_verification(hostname));

    // Set SNI Hostname (many hosts need this to handshake successfully)
    if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
    {
        throw boost::system::system_error(
            ::ERR_get_error(), boost::asio::error::get_ssl_category());
    }

    socket.handshake(SSLSocket::client);
}

Given that openssl s_client -connect my.url.com:993 -crlf -verify 1 succeeds there is not a lot that seems wrong. One thing catches my eye: I'd configure the context before constructing an SSL stream from it:

ssl::context ssl_context(ssl::context::tls);

ssl_context.set_default_verify_paths();

SSLSocket socket(context, ssl_context);

Also, openssl likely uses SNI extensions:

// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
    throw boost::system::system_error(
        ::ERR_get_error(), boost::asio::error::get_ssl_category());
}

Finally, make sure the url string view contains correct data, notably that it's a valid hostname and null-terminated string. In this case I'd prefer to use a string representation that guarantees null-termination:

Summary

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;


int main() {
    boost::asio::io_context context;
    ssl::context ssl_context(ssl::context::tls);

    ssl_context.set_default_verify_paths();

    SSLSocket socket(context, ssl_context);

    tcp::resolver r(context);
    std::string hostname = "www.example.com";
    auto endpoints = r.resolve(hostname, "443");
    boost::asio::connect(socket.next_layer(), endpoints);
    socket.set_verify_mode(ssl::verify_peer);
    socket.set_verify_callback(ssl::host_name_verification(hostname));

    // Set SNI Hostname (many hosts need this to handshake successfully)
    if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
    {
        throw boost::system::system_error(
            ::ERR_get_error(), boost::asio::error::get_ssl_category());
    }

    socket.handshake(SSLSocket::client);
}
月竹挽风 2025-02-18 11:35:30

就我而言,我要求它与此功能一起使用:

ssl_context.load_verify_file("/etc/ssl/certs/ca-bundle.crt");

因此,指向CA-Bundle文件而不是CERT文件。

In my case I manged to make it work with this:

ssl_context.load_verify_file("/etc/ssl/certs/ca-bundle.crt");

so pointing to the ca-bundle file instead of the cert file.

回梦 2025-02-18 11:35:30

我在Boost Asio中的握手也有一些挑战。我在Filemaker插件中使用它,当涉及到这一行时:

namespace net = boost::asio;
..
..
stream.handshake(net::ssl::stream_base::handshake_type::client, ec);

因此,它崩溃了整个应用程序。
经过数小时,更改设置,谷歌搜索并阅读文档后,我将该方法放入了一个小的独立应用程序中,就像在孤立的环境中对其进行测试一样。在插件代码和测试应用程序中都具有所有相同的Boost版本(1.82.0)和相同的OpenSSL库(3.1.4)。

在测试应用程序中,它可以按预期工作。代码从服务器检索数据。在具有相同代码的插件中,FileMaker崩溃。

在您的下方,您可以查看崩溃日志片段以及测试应用程序的代码。

崩溃日志

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   FileMaker Pro Advanced                 0x10a6f7b64 boost::system::error_category::equivalent(boost::system::error_code const&, int) const + 4
1   ACF_plugin_db                          0x116ce2f4d boost::system::detail::failed_impl(int, boost::system::error_category const&) + 93 (error_category.hpp:217)
2   ACF_plugin_db                          0x116ce2ea5 boost::system::error_code::error_code(int, boost::system::error_category const&) + 53 (error_code.hpp:129)
3   ACF_plugin_db                          0x116ce2dd3 boost::system::error_code::error_code(int, boost::system::error_category const&) + 35 (error_code.hpp:130)
4   ACF_plugin_db                          0x116d0ee6d boost::asio::error::make_error_code(boost::asio::error::misc_errors) + 45 (error.hpp:354)
5   ACF_plugin_db                          0x116d11acf boost::system::error_code::error_code<boost::asio::error::misc_errors>(boost::asio::error::misc_errors, boost::system::detail::enable_if<(is_error_code_enum<boost::asio::error::misc_errors>::value) || (std::is_error_code_enum<boost::asio::error::misc_errors>::value), void>::type*) + 63 (error_code.hpp:150)
6   ACF_plugin_db                          0x116d11a83 boost::system::error_code::error_code<boost::asio::error::misc_errors>(boost::asio::error::misc_errors, boost::system::detail::enable_if<(is_error_code_enum<boost::asio::error::misc_errors>::value) || (std::is_error_code_enum<boost::asio::error::misc_errors>::value), void>::type*) + 35 (error_code.hpp:149)
7   ACF_plugin_db                          0x116d10612 boost::asio::ssl::detail::engine::map_error_code(boost::system::error_code&) const + 50 (engine.ipp:240)
8   ACF_plugin_db                          0x116d1024f unsigned long boost::asio::ssl::detail::io<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::ssl::detail::handshake_op>(boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>&, boost::asio::ssl::detail::stream_core&, boost::asio::ssl::detail::handshake_op const&, boost::system::error_code&) + 607 (io.hpp:81)
9   ACF_plugin_db                          0x116d0ffbc boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >::handshake(boost::asio::ssl::stream_base::handshake_type, boost::system::error_code&) + 76 (stream.hpp:437)
10  ACF_plugin_db                          0x116cdf364 boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >::handshake(boost::asio::ssl::stream_base::handshake_type) + 52 (stream.hpp:420)
11  ACF_plugin_db                          0x116cde1de SendSoapRequest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 1710 (DocServiceXML.cpp:256)

测试应用程序的工作来源与插件相同的代码

//
//  main.cpp
//  test-boost-ssl
//
//  Created by Ole Kristian Ek Hornnes on 02/11/2023.
//

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>

namespace net = boost::asio;
namespace ssl = boost::asio::ssl;
namespace http = boost::beast::http;
using tcp = boost::asio::ip::tcp;


struct httpResponse {
    int StatusCode;
    std::string ResultText;
};

httpResponse SendPostRequest(const std::string& xurl, const std::string& post_data) {
    net::io_context ioc;
    std::string url = xurl;
    // Determine the scheme (http or https), hostname, and path
    std::string scheme, hostname, path;
    int port = 0;
    httpResponse httpResult;
    
    if (url.substr(0, 5) == "https") {
        scheme = "https";
        port = 443;
        url = url.substr(8); // Remove "https://"
    } else if (url.substr(0, 4) == "http") {
        scheme = "http";
        port = 80;
        url = url.substr(7); // Remove "http://"
    }
    
    size_t slashPos = url.find('/');
    if (slashPos != std::string::npos) {
        hostname = url.substr(0, slashPos);
        path = url.substr(slashPos);
    } else {
        hostname = url;
        path = "/";
    }
    
    bool useSSL = (scheme == "https");
    
    // These objects perform I/O
    
    try {
        
        if ( useSSL ) {
            // Set up SSL context and stream
            // Set up SSL context (only if using SSL)
            
            net::io_context xxx;
            tcp::resolver resolver(xxx);
            // tcp::socket socket(ioc);
            net::ssl::context sslContext(net::ssl::context::tlsv12_client);
            // sslContext.set_options(net::ssl::context::default_workarounds);
            
      /*      sslContext.set_options(net::ssl::context::no_sslv2);
            sslContext.set_options(net::ssl::context::no_sslv3);
            sslContext.set_options(net::ssl::context::tlsv12);*/
            
            sslContext.set_default_verify_paths();
            net::ssl::stream<tcp::socket> stream = {xxx, sslContext };
      
            // Resolve the hostname and port
            auto it = resolver.resolve(hostname, std::to_string(port));
          // std::cout << "resolver " << std::string(it) << std::endl;
            boost::system::error_code ec;
           
            net::connect(stream.next_layer(), it, ec);
            
            if (ec) {
                std::stringstream err;
                err << "Error during SSL connect: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -2;
                return httpResult;
            }
            // stream.set_verify_mode(net::ssl::verify_peer);
            // Perform the SSL handshake
            
            try {
                stream.handshake(net::ssl::stream_base::handshake_type::client, ec);
            } catch (const std::exception &e)  {
                std::stringstream err;
                err << "Error during SSL handshake: " << e.what() << "ec: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -2;
                return httpResult;
            }
            
            if (ec) {
                std::stringstream err;
                err << "Error during SSL Handshake: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -3;
                return httpResult;
            }
            
            std::string agent ("ACF_Plugin for FileMaker ver. ");
            // agent += std::string (PLUGIN_VERSION);
            // Set up an HTTPS POST request
            http::request<http::string_body> req{http::verb::post, path, 11};
            req.set(http::field::host, hostname);
            req.set(http::field::user_agent, agent);
            req.set(http::field::content_type, "text/xml");
           //  req.set(http::field::soapaction, SoapAction);
            
            req.body() = post_data;
            req.prepare_payload();
            
            // Send the HTTP request
            http::write(stream, req);
            
            // This buffer is used for reading and must be persisted
            boost::beast::flat_buffer buffer;
            
            // Declare a container to hold the response
            http::response<http::string_body> res;
            
            // Receive the HTTP response
            http::read(stream, buffer, res);
            // Convert the response to a std::string and return it
            // Check the HTTP status code and set StatusCode accordingly
            httpResult.StatusCode = static_cast<int>(res.result_int());
            httpResult.ResultText = res.body();
            return httpResult;
        } else {
            tcp::socket socket(ioc);
            tcp::resolver resolver(ioc);
            // Resolve the hostname and port
            auto results = resolver.resolve(hostname, std::to_string(port));
            boost::asio::connect(socket, results.begin(), results.end());
            
            
            // Set up an HTTP POST request
            http::request<http::string_body> req{http::verb::post, path, 11};
            req.set(http::field::host, hostname);
            req.set(http::field::user_agent, "MyClient/1.0");
            req.set(http::field::content_type, "text/xml");
          /*  if (SoapAction != "" ) {
                req.set(http::field::soapaction, SoapAction);
            } */
            req.body() = post_data;
            req.prepare_payload();
            
            // Send the HTTP request
            http::write(socket, req);
            
            // This buffer is used for reading and must be persisted
            boost::beast::flat_buffer buffer;
            
            // Declare a container to hold the response
            http::response<http::string_body> res;
            
            // Receive the HTTP response
            http::read(socket, buffer, res);
            // Convert the response to a std::string and return it
            // Check the HTTP status code and set StatusCode accordingly
            httpResult.StatusCode = static_cast<int>(res.result_int());
            httpResult.ResultText = res.body();
            return httpResult;
        }
    } catch (const std::exception &e) {
        std::stringstream err;
        err << "Error: " << e.what() << std::endl;
        httpResult.ResultText = err.str();
        httpResult.StatusCode = -1;
        return httpResult;
    }
}

int main(int argc, char* argv[]) {
    
    std::string url;
    if (argc != 2) {
       // std::cerr << "Usage: postService <URL>" << std::endl;
       //  return 1;
        url = "https://myserver.no/wsx/doc/";
    } else {
        url = argv[1];
    }
    
    std::string post_data = "TEST\n\n"; // You can set your POST data here
    
    httpResponse result = SendPostRequest(url, post_data);
    
    std::cout << "URL: " << url << std::endl;
    std::cout << "HTTP Status Code: " << result.StatusCode << std::endl;
    std::cout << "Response Body:\n" << result.ResultText << std::endl;
    
    return 0;
}

在显示通信的测试时示例输出,我得到了肥皂失误,但是由于我没有't实际上发送了任何有效的肥皂请求,预期。但是,它表明功能与服务器交换数据。

URL: https://myserver.no/ws/doc/
HTTP Status Code: 500
Response Body:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>Bad Request</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

Program ended with exit code: 0

I have also some challenge with this handshake in boost asio. I am using it in a FileMaker Plugin, and when it comes to this line:

namespace net = boost::asio;
..
..
stream.handshake(net::ssl::stream_base::handshake_type::client, ec);

It consequently crash the whole application.
After trying for hours, changing the setup, googling, and reading the documentation, I put the method into a small stand-alone application made like a command-line utility to test it in a isolated environment. Having all the same boost version (1.82.0) and the same openSSL libraries (3.1.4) in both the plugin code and test application.

In the test application, it works as expected. The code retrieve data from the server. In the plugin with the same code, FileMaker crash.

Below you ca see the crash log snippet, and also the code of the test application.

Crash log

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   FileMaker Pro Advanced                 0x10a6f7b64 boost::system::error_category::equivalent(boost::system::error_code const&, int) const + 4
1   ACF_plugin_db                          0x116ce2f4d boost::system::detail::failed_impl(int, boost::system::error_category const&) + 93 (error_category.hpp:217)
2   ACF_plugin_db                          0x116ce2ea5 boost::system::error_code::error_code(int, boost::system::error_category const&) + 53 (error_code.hpp:129)
3   ACF_plugin_db                          0x116ce2dd3 boost::system::error_code::error_code(int, boost::system::error_category const&) + 35 (error_code.hpp:130)
4   ACF_plugin_db                          0x116d0ee6d boost::asio::error::make_error_code(boost::asio::error::misc_errors) + 45 (error.hpp:354)
5   ACF_plugin_db                          0x116d11acf boost::system::error_code::error_code<boost::asio::error::misc_errors>(boost::asio::error::misc_errors, boost::system::detail::enable_if<(is_error_code_enum<boost::asio::error::misc_errors>::value) || (std::is_error_code_enum<boost::asio::error::misc_errors>::value), void>::type*) + 63 (error_code.hpp:150)
6   ACF_plugin_db                          0x116d11a83 boost::system::error_code::error_code<boost::asio::error::misc_errors>(boost::asio::error::misc_errors, boost::system::detail::enable_if<(is_error_code_enum<boost::asio::error::misc_errors>::value) || (std::is_error_code_enum<boost::asio::error::misc_errors>::value), void>::type*) + 35 (error_code.hpp:149)
7   ACF_plugin_db                          0x116d10612 boost::asio::ssl::detail::engine::map_error_code(boost::system::error_code&) const + 50 (engine.ipp:240)
8   ACF_plugin_db                          0x116d1024f unsigned long boost::asio::ssl::detail::io<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>, boost::asio::ssl::detail::handshake_op>(boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor>&, boost::asio::ssl::detail::stream_core&, boost::asio::ssl::detail::handshake_op const&, boost::system::error_code&) + 607 (io.hpp:81)
9   ACF_plugin_db                          0x116d0ffbc boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >::handshake(boost::asio::ssl::stream_base::handshake_type, boost::system::error_code&) + 76 (stream.hpp:437)
10  ACF_plugin_db                          0x116cdf364 boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::any_io_executor> >::handshake(boost::asio::ssl::stream_base::handshake_type) + 52 (stream.hpp:420)
11  ACF_plugin_db                          0x116cde1de SendSoapRequest(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 1710 (DocServiceXML.cpp:256)

The Working Source of the test application that has the same code as the plugin

//
//  main.cpp
//  test-boost-ssl
//
//  Created by Ole Kristian Ek Hornnes on 02/11/2023.
//

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>

namespace net = boost::asio;
namespace ssl = boost::asio::ssl;
namespace http = boost::beast::http;
using tcp = boost::asio::ip::tcp;


struct httpResponse {
    int StatusCode;
    std::string ResultText;
};

httpResponse SendPostRequest(const std::string& xurl, const std::string& post_data) {
    net::io_context ioc;
    std::string url = xurl;
    // Determine the scheme (http or https), hostname, and path
    std::string scheme, hostname, path;
    int port = 0;
    httpResponse httpResult;
    
    if (url.substr(0, 5) == "https") {
        scheme = "https";
        port = 443;
        url = url.substr(8); // Remove "https://"
    } else if (url.substr(0, 4) == "http") {
        scheme = "http";
        port = 80;
        url = url.substr(7); // Remove "http://"
    }
    
    size_t slashPos = url.find('/');
    if (slashPos != std::string::npos) {
        hostname = url.substr(0, slashPos);
        path = url.substr(slashPos);
    } else {
        hostname = url;
        path = "/";
    }
    
    bool useSSL = (scheme == "https");
    
    // These objects perform I/O
    
    try {
        
        if ( useSSL ) {
            // Set up SSL context and stream
            // Set up SSL context (only if using SSL)
            
            net::io_context xxx;
            tcp::resolver resolver(xxx);
            // tcp::socket socket(ioc);
            net::ssl::context sslContext(net::ssl::context::tlsv12_client);
            // sslContext.set_options(net::ssl::context::default_workarounds);
            
      /*      sslContext.set_options(net::ssl::context::no_sslv2);
            sslContext.set_options(net::ssl::context::no_sslv3);
            sslContext.set_options(net::ssl::context::tlsv12);*/
            
            sslContext.set_default_verify_paths();
            net::ssl::stream<tcp::socket> stream = {xxx, sslContext };
      
            // Resolve the hostname and port
            auto it = resolver.resolve(hostname, std::to_string(port));
          // std::cout << "resolver " << std::string(it) << std::endl;
            boost::system::error_code ec;
           
            net::connect(stream.next_layer(), it, ec);
            
            if (ec) {
                std::stringstream err;
                err << "Error during SSL connect: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -2;
                return httpResult;
            }
            // stream.set_verify_mode(net::ssl::verify_peer);
            // Perform the SSL handshake
            
            try {
                stream.handshake(net::ssl::stream_base::handshake_type::client, ec);
            } catch (const std::exception &e)  {
                std::stringstream err;
                err << "Error during SSL handshake: " << e.what() << "ec: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -2;
                return httpResult;
            }
            
            if (ec) {
                std::stringstream err;
                err << "Error during SSL Handshake: " << ec << std::endl;
                httpResult.ResultText = err.str();
                httpResult.StatusCode = -3;
                return httpResult;
            }
            
            std::string agent ("ACF_Plugin for FileMaker ver. ");
            // agent += std::string (PLUGIN_VERSION);
            // Set up an HTTPS POST request
            http::request<http::string_body> req{http::verb::post, path, 11};
            req.set(http::field::host, hostname);
            req.set(http::field::user_agent, agent);
            req.set(http::field::content_type, "text/xml");
           //  req.set(http::field::soapaction, SoapAction);
            
            req.body() = post_data;
            req.prepare_payload();
            
            // Send the HTTP request
            http::write(stream, req);
            
            // This buffer is used for reading and must be persisted
            boost::beast::flat_buffer buffer;
            
            // Declare a container to hold the response
            http::response<http::string_body> res;
            
            // Receive the HTTP response
            http::read(stream, buffer, res);
            // Convert the response to a std::string and return it
            // Check the HTTP status code and set StatusCode accordingly
            httpResult.StatusCode = static_cast<int>(res.result_int());
            httpResult.ResultText = res.body();
            return httpResult;
        } else {
            tcp::socket socket(ioc);
            tcp::resolver resolver(ioc);
            // Resolve the hostname and port
            auto results = resolver.resolve(hostname, std::to_string(port));
            boost::asio::connect(socket, results.begin(), results.end());
            
            
            // Set up an HTTP POST request
            http::request<http::string_body> req{http::verb::post, path, 11};
            req.set(http::field::host, hostname);
            req.set(http::field::user_agent, "MyClient/1.0");
            req.set(http::field::content_type, "text/xml");
          /*  if (SoapAction != "" ) {
                req.set(http::field::soapaction, SoapAction);
            } */
            req.body() = post_data;
            req.prepare_payload();
            
            // Send the HTTP request
            http::write(socket, req);
            
            // This buffer is used for reading and must be persisted
            boost::beast::flat_buffer buffer;
            
            // Declare a container to hold the response
            http::response<http::string_body> res;
            
            // Receive the HTTP response
            http::read(socket, buffer, res);
            // Convert the response to a std::string and return it
            // Check the HTTP status code and set StatusCode accordingly
            httpResult.StatusCode = static_cast<int>(res.result_int());
            httpResult.ResultText = res.body();
            return httpResult;
        }
    } catch (const std::exception &e) {
        std::stringstream err;
        err << "Error: " << e.what() << std::endl;
        httpResult.ResultText = err.str();
        httpResult.StatusCode = -1;
        return httpResult;
    }
}

int main(int argc, char* argv[]) {
    
    std::string url;
    if (argc != 2) {
       // std::cerr << "Usage: postService <URL>" << std::endl;
       //  return 1;
        url = "https://myserver.no/wsx/doc/";
    } else {
        url = argv[1];
    }
    
    std::string post_data = "TEST\n\n"; // You can set your POST data here
    
    httpResponse result = SendPostRequest(url, post_data);
    
    std::cout << "URL: " << url << std::endl;
    std::cout << "HTTP Status Code: " << result.StatusCode << std::endl;
    std::cout << "Response Body:\n" << result.ResultText << std::endl;
    
    return 0;
}

Sample output when testing that shows communication, I got a soap-fault, but since I didn't actually send any valid soap request, its expected. However, it shows that the function exchange data with the server.

URL: https://myserver.no/ws/doc/
HTTP Status Code: 500
Response Body:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Client</faultcode><faultstring>Bad Request</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

Program ended with exit code: 0
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文