I'm trying to send https web requests with Asio and OpenSSL. My code is working fine on most of the sites I tried it on, but on some others, I get an error during the handshake handshake: wrong version number (SSL routines, ssl3_get_record)
.
I've found some people having this issue because they were behind proxies or because they tried to connect to port 80 instead of port 443, but this is not the case here (as far as I know) because the exact same code (see below for minimal example) works for most of the sites I tried it on.
I've tried to check with wireshark to see if I could spot the difference between a case with and one without the error. Here what I found:
- when it works, TLSv1.2 or 1.3 is used, when it doesn't, it's TLSv1
- when it doesn't work, the DNS query shows a cloudfront cname redirection, but the endpoint used for the socket matches the redirection
Based on these observations, I know my code is capable of using TLSv1.3, and I thought that using TLSv1 was the issue. So I tried to force asio to use a version > 1 for TLS with asio::ssl::context::tlsv13_client
when creating the context, or by adding asio::ssl::context::no_tlsv1
to set_options, but wireshark still showed that a TLSv1 protocol was used.
For the second point, I'm not too familiar with web stuff, so I'm not sure what conclusion I can make about that, or even if it's relevant to the issue.
Minimal working example:
#include <asio.hpp>
#include <asio/ssl.hpp>
#include <iostream>
int main(int argc, char* argv[])
{
try
{
asio::io_context io_context;
asio::ip::tcp::resolver resolver(io_context);
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve("google.com", "https"); //not working with "api.minecraftservices.com" for example
asio::ssl::context ctx(asio::ssl::context::sslv23); // also tried tlsv13_client to force v1.3, without success
ctx.set_default_verify_paths();
ctx.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::verify_none);
asio::ssl::stream<asio::ip::tcp::socket> socket(io_context, ctx);
socket.set_verify_mode(asio::ssl::verify_none);
socket.set_verify_callback([](bool, asio::ssl::verify_context&) {return true; });
asio::connect(socket.lowest_layer(), endpoints);
socket.handshake(socket.client);
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
return -1;
}
return 0;
}
CodePudding user response:
You need to be more specific about the server you are trying to connect to:
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <iostream>
namespace ssl = boost::asio::ssl;
using boost::asio::ip::tcp;
int main() {
try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
ssl::context ctx(ssl::context::sslv23); // also tried tlsv13_client to
// force v1.3, without success
ctx.set_default_verify_paths();
ctx.set_options(ssl::context::default_workarounds |
ssl::context::verify_none);
ssl::stream<tcp::socket> socket(io_context, ctx);
socket.set_verify_mode(ssl::verify_none);
socket.set_verify_callback([](auto&&...) { return true; });
#ifndef COLIRU
connect(socket.lowest_layer(), resolver.resolve("d7uri8nf7uskq.cloudfront.net", "https"));
#else
socket.lowest_layer().connect({boost::asio::ip::address_v4::from_string("65.9.84.220"), 443});
#endif
std::cout << "Connected to " << socket.lowest_layer().remote_endpoint() << "\n";
socket.handshake(socket.client);
} catch (std::exception const& e) {
std::cout << e.what() << std::endl;
return -1;
}
}
Prints
Connected to 65.9.84.220:443
UPDATE
Indeed, using api.minecraftservices.com:443 does print
Connected to 65.9.78.95:443
handshake: wrong version number (SSL routines, ssl3_get_record) [asio.ssl:336130315]
It turns out you need SNI:
SSL_set_tlsext_host_name(socket.native_handle(), "api.minecraftservices.com");
And then it works (with varying IP resolutions)
Connected to 65.9.78.23:443