Connect SSL server via HTTP proxy using boost - c++

I should send text messages to messaging platform like Slacks using incoming webhook. But my network as developing is different from public one, so I decided to use our company's proxy server which can connect to public one. Briefly,
Client - HTTP PROXY - SERVER (messaging platform)
1. Firstly, establish C-P connection using normal TCP socket.
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_socket;
boost::asio::ip::tcp::resolver resolver(m_io);
boost::asio::ip::tcp::resolver::query query("www.proxy.dev", "8080");
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::async_connect(m_socket.lowest_layer(), iterator,
boost::bind(&ClientSocket::HandleHandShake, this,
boost::asio::placeholders::error, shared_from_this()));
After connecting, send connection string to proxy.
m_stringStream << "CONNECT";
m_stringStream << " " << m_domain << ":443";
m_stringStream << " HTTP/1.1\r\n";
m_stringStream << "HOST: " << m_domain << ":443\r\n";
m_stringStream << "Proxy-Connection: keep-alive\r\n";
m_stringStream << "Connection: keep-alive\r\n";
2. Receive status code from proxy.
200 OK
3. Handshake
m_socket.set_verify_mode(boost::asio::ssl::verify_none);
m_socket.set_verify_callback([](auto&& preverified, auto&& ctx)
{
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
return preverified;
});
m_socket.async_handshake(boost::asio::ssl::stream_base::client,
boost::bind(&ClientSocket::HandleHandShake, this,
boost::asio::placeholders::error, shared_from_this()));
4. Write HTTP POST, Read status code.
At 3 step, the error code informs me "short read" so i can't go next step.
I have searched many questions, but everything is not worked for me.
If I skip the handshake process, can receive "uninitialized" error.
If I also skip 'CONNECT' with proxy and send 'POST' directly to server, can receive "403 badrequest" error.
It's OK to connect server and send text message not through proxy server.
Thank you for reading.

Related

Why boost::beast::http::async_write sends one http request in multiple TLS segments?

I am working on one simple async Https client using boost::beast lib. Boost version is 1.69.0.
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sslSocket_;
boost::asio::ip::tcp::resolver resolver_;
boost::optional<boost::beast::http::request<boost::beast::http::string_body>> request_;
boost::optional<boost::beast::http::response_parser<boost::beast::http::string_body>> responseParser_;
boost::beast::flat_buffer buffer_;
After Tcp connection and tls handshake, everything works fine. However, when we build one http request and send it with boost::beast::http::async_write:
void SimpleAsyncHttpsClient::buildHttpRequest(const std::string& uri, const std::string& payload)
{
request_.emplace();
request_->set(boost::beast::http::field::host, host_);
request_->set(boost::beast::http::field::accept, "*/*");
request_->set(boost::beast::http::field::content_type, "application/json");
request_->set(boost::beast::http::field::date, "Tue, 01 Jun 2021 07:27:04 GMT");
request_->set(boost::beast::http::field::connection, "close");
request_->target(uri);
request_->method(boost::beast::http::verb::post);
request_->body() = payload;
request_->prepare_payload();
}
void SimpleAsyncHttpsClient::sendHttp()
{
boost::beast::http::async_write(sslSocket_,
*request_,
std::bind(
&SimpleAsyncHttpClient::waitForResponse,
shared_from_this(),
std::placeholders::_1));
}
Strangely the such piece of code sends my http request in multiple segments,
wireshark screenshot1
wireshark screenshot2
The client firstly send "POST", then send the path and then the header lines in each segment.
I've also tried one sync https client and it is the same behavior. However, if we use boost::asio::async_write API we do not have this problem.
Question: How could I send the http request in one packet with boost::beast APIs? The other beast lib(parser, etc) are very friendly to developers.

GRPC C++ Non-blocking server

I am relatively new to GRPC and I am trying to create a grpc server from within an MFC Application. However, the server->Wait() statement is blocking and thus holds up the entire application.
Is there a way to make it non-blocking? Or to Shutdown the server after it has served a single RPC request instead of waiting indefinitely?
Example of the code:
void RunServer() {
std::string server_address("127.0.0.1:50055");
TestReceiverImpl service("Output.txt");
ServerBuilder builder;
builder.RegisterService(&service);
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
server = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
//server->Wait(); <<-- Blocking code
}

Clarification needed about a SSL client using Boost asio

I have a C++ application that uses Boost_asio to do TCP/IP connection who read a .php document in a Web server that in turn uses the php script to update certain statistics.
The whole thing work as planned, but recently the server changed to use SSL (Google mandatory) and naturally, the previous connection stopped to work.
After a lot of theoretical reading about SSL, I'm still in the dark about certain practical details.
Using the example in the Boost asio documentation and a file “cacert.pem”, downloaded form somewhere following indications in this site, I'm able to run correctly the example using:
<host> = “www.google.com” and <port> = “https”.
Using the example “as is”, the Google server response is:
Verifying /OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign
Verifying /C=US/O=Google Trust Services/CN=Google Internet Authority G3
Verifying /C=US/ST=California/L=Mountain View/O=Google LLC/CN=www.google.com
Enter message: Reply:
But when using
<host> = “www.zator.com” and <port> = “https”
my domain, hosted in 1&1 IONOS, the reply is:
Handshake failed: tlsv1 alert internal error
At this point I have several questions:
What in the hell mean the sentence: ctx.load_verify_file("cacert.pem"); ?
The content of that file, can be the culprit of the fail when connecting with my domain?
Is that sentence essential to complete the connection?
In the case of google server (absence of error), is it supposed that after the sentence io_context.run(); the connection has been correctly established?
Assuming I make public the client's member socket_ (private in the example), can I continue with some as (I can't test that in google :-)
std::string request("GET /mystatistics.php HTTP/1.1\r\n\r\n");
boost::asio::write(c.socket_, boost::asio::buffer(request));
boost::system::error_code ec;
std::string response;
do { // read response
char buf[1024];
size_t bytes_transferred = c.socket_.read_some(boost::asio::buffer(buf), ec);
if (!ec) response.append(buf, buf + bytes_transferred);
} while (!ec);
std::cout << "Response received: '" << response << "'\n";
Thanks in advance.
I've found some useful information here. A good, albeit partial info, and a good start point to further search

"Connection closed" When using QNetworkAccessManager and QTcpServer

I have simple server with QTcpServer and simple client with QNetworkAccessManager.
When I request data from the server via curl or browser everything is ok
When I request data from any site via QNetworkAccessManager everything is ok
But I can not read data from QTcpServer via QNetworkAccessManager. All requests are reseted. QNetworkAccessManager (client) had send RST (reset connection) right after it received a data from server. And in client code we get the error: "Connection closed" (RemoteHostClosedError)
Aslo, I tried use QNetworkAccessManager from DownloadManager example and QTcpServer from FortuneServer example in various combinations, but the results were the same.
Tested Qt Versions:
Mac Qt 5.7
Linux Qt 5.7
Linux Qt 5.6.2
Linux Qt 5.5.1
Wireshark screenshot: qt-wireshark.png
The upper parts (with red lines) are results of QNetworkAccessManager, and the latest packets with success result are curl attempt to get data from QTcpServer
Also there is a simple example to reproduce the error: testNetwork.zip
And here is sample code for client:
void test(quint16 port)
{
QNetworkAccessManager *manager = new QNetworkAccessManager();
QNetworkRequest request;
request.setUrl(QUrl(QString("http://127.0.0.1:%1/").arg(port)));
manager->connect(manager, &QNetworkAccessManager::finished,
[](QNetworkReply *reply) {
qDebug() << QString("Finished. %1. %2").arg(reply->errorString()).arg(reply->error());
qDebug() << "readed: " << reply->readAll();
});
QNetworkReply *reply = manager->get(request);
reply->connect(reply, &QNetworkReply::readyRead, [reply]() {
qDebug() << QString("readyRead: '%1'").arg(QString(reply->readAll()));
});
}
and for server:
QTcpSocket socket;
...
if(socket.waitForReadyRead(5000))
{
QByteArray request;
request += socket.readAll();
QByteArray responce("HELLO, WORLD! HELLO, WORLD! HELLO, WORLD! HELLO, WORLD!");
socket.write(responce);
if(!socket.waitForBytesWritten())
{
qWarning() << QString("Error occurred in waitForBytesWritten() method of the tcp socket. %1 (%2)")
.arg(socket.errorString())
.arg(socket.error());
}
}
else
{
qWarning() << QString("Error occurred in read method of the tcp socket. %1 (%2)")
.arg(socket.errorString())
.arg(socket.error());
}
Also I created a Bug report on qt.io (QTBUG-56631)
Your client is making an HTTP request, but your server isn't an http server - it's not sending back a valid HTTP request.
Your client works when you point it at a web server, because that IS an http server.
QByteArray responce("HELLO, WORLD! HELLO, WORLD! HELLO, WORLD! HELLO, WORLD!");
isn't a valid HTTP response.

Getting Started with POP3 Using Boost Asio

To help myself learn socket programming, I made a very simple SMTP client (see code below), using some code from the the Wt SMTP Library
As an exercise, I thought I would take a shot at implementing a POP3 client. Using the Wikipedia as a reference, it looked as if you must simply connect to the server on port 110 and send commands (just like SMTP connects on port 25 and sends commands).
However, I have so far been unable to even connect, and basically just timeout in the get_response() every time. I tried connecting to pop.gmail.com with no luck.
I don't believe that you need to send any user/password/other information when the connection is first established? I expected to receive something along the lines of +OK POP3 Server Ready once I connect, and I can then send the USER and PASS commands.
My question is: Am I missing something during the connection phase? Am I trying to connect to a bad host (gmail)? If so, is there a better host than pop.gmail.com with which I can test? Or am I going about this entirely wrong?
#include <string>
#include <sstream>
#include <iostream>
#include <iterator>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
boost::asio::io_service io_service;
tcp::socket socket(io_service);
tcp::resolver resolver(io_service);
tcp::resolver::query query("localhost", "25"); // Change this for POP3
tcp::resolver::iterator endpt_iter = resolver.resolve(query);
boost::system::error_code erc = boost::asio::error::host_not_found;
while(erc && endpt_iter != end)
{
socket.close();
socket.connect(*endpt_iter++, erc);
}
if(erc) { socket.close(); return }
std::cout << get_response(socket) << std::endl; // 220 <my-host-name> ESMTP Postfix
send(socket, "EHLO localhost\r\n");
std::cout << get_response(socket) << std::endl; // 250-<my-host-name>
return 0;
}
std::string get_response(tcp::socket& socket)
{
boost::asio::streambuf response;
for(;;)
{
std::string msg;
boost::asio::read_until(socket, response, "\r\n");
std::istream in(&response);
std::getline(in, msg);
return msg;
}
}
void send(tcp::socket& socket, std::string msg)
{
boost::asio::write(socket, boost::asio::buffer(msg));
}
Gmail requires SSL/TLS encryption on its POP3 and SMTP connections.
For POP3, you have to connect to port 995 and initiate an SSL/TLS handshake immediately upon connecting. Then you can read the server's initial +OK greeting from, and send commands to, the encrypted connection.
For SMTP, you have a couple of choices:
connect to port 465. Initiate an SSL/TLS handshake immediately upon connecting. Read the +OK greeting from, and send all commands to, the encrypted connection.
connect to port 587. Read the +OK greeting from, and send EHLO and STARTTLS commands to, the unencrypted connection. Initiate the SSL/TLS handshake. Send a new EHLO command, and other commands, to the encrypted connection.