Network reply always empty with error : RemoteHostClosedError - c++

I have a secured URL working with an x509 certificate and I am supposed to receive a JSON as an answer. My certificate is in pfx format (ca certificate, local certificate, key). I added it on the computer and when launching a request on google chrome and internet explorer with the secured URL I have indeed the JSON as an answer.
My problem is when I try to do the same inside my Qt application I do not have any answer from the same URL. My code :
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setProtocol(QSsl::TlsV1_2);
_manager = std::make_unique<QNetworkAccessManager>();
auto line = "myurl";
qInfo() << line;
QNetworkRequest request((QUrl::fromUserInput(line)));
request.setSslConfiguration(config);
QEventLoop loop;
connect(_manager.get(), &QNetworkAccessManager::finished, &loop,
&QEventLoop::quit,
Qt::DirectConnection);
connect(_manager.get(), &QNetworkAccessManager::sslErrors, this,
&CheckConfController::sslErrors);
_loginReply = std::unique_ptr<QNetworkReply>(_manager->get(request));
connect(_loginReply.get(), &QNetworkReply::readyRead,
this, &CheckConfController::replyFinished);
connect(_loginReply.get(), &QNetworkReply::finished,
this, &CheckConfController::_loginReceive);
loop.exec();
The login receive method :
void CheckConfController::_loginReceive(){
qInfo() << "status code : " << _loginReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qInfo() << "error : " << _loginReply->error();
qInfo() << "error string : " << _loginReply->errorString();
QByteArray b = _loginReply->readAll();
qInfo() << "byte : " << b;
}
The SSL error method :
void CheckConfController::sslErrors(QNetworkReply* reply, const QList<QSslError> &errors)
{
QByteArray b = reply->readAll();
qInfo() << "reply : " << b;
for(auto element : errors){
qInfo() << "ssl error : " << element;
}
}
This is mu Outpput :
status code: QVariant(Invalid)
error: QNetworkReply::RemoteHostClosedError
err string: "Connection Closed"
byte : ""
SSL error is never called.
I switched my URL line to https://www.google.com and it works fine. I also tried with other urls and i receive my json correctly.
This is my configuration:
Qt version: 5.12.11
compiler: MSVC 2017 64 bits
Computer running the application: Windows 7
Any help would be appreciated. Thank you.

Update
After comments below this answer, I noticed the server that OP is trying to reach has certificate limitations, hence defaulting to system certificates is not an option, because we can't be sure which certificate is used.
Now that we know that, I suggest ensuring that your certificate is somehow preferred, for example try:
QSslConfiguration::setDefaultConfiguration(config);
I mean, at end of OP's original code:
QSslKey key;
QSslCertificate certificate;
QList<QSslCertificate> certChain;
bool imported = QSslCertificate::importPkcs12(&pfxFile, &key, &certificate, &certChain, "mypassphrase");
qInfo() << "imported : " << imported;
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setCaCertificates(certChain);
config.setLocalCertificate(certificate);
config.setPrivateKey(key);
config.setProtocol(QSsl::TlsV1_2);
Old answer
As comments said:
OP no longer adds certificate manually (and adds it to system),
Also, OP's code works for other HTTPS URLs.
Last but not least, OP's Site has valid certificate, and logs just mention closed connection.
Counting all together, this does not seem to have anything to do with Qt. OP's site simply rejects any JSON request.
Maybe try setting auth header (or, do whatever else the site's requirements are).
I usually use Postman App (to test sites' API and routes).

Related

Can't access .ONION websites using QT C++, How To do it?

Can someone can tell me why my code does not work? I made a program using QT C++ to route through TOR, I can access normal websites and I have a different public IP but I can't access .ONION websites, I get no response and/or Host not found.
EDIT: AFTER FURTHER INSPECTION I THINK THE PROBLEM IS THAT I NEED TO RESOLVE THE DNS FOR THE .ONION ADDRESS, HOW CAN I DO THAT?
tester::tester(QObject *parent) :
QObject(parent)
{
}
QTcpSocket socket ;
void tester::GetUrl()
{
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::DefaultProxy);
proxy.setHostName("127.0.0.1");
proxy.setPort(9050);
proxy.setCapabilities(QNetworkProxy::HostNameLookupCapability | proxy.capabilities());
QNetworkProxy::setApplicationProxy(proxy);
qDebug()<<"Connecting";
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
manager->setProxy(proxy);
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
//manager->get(QNetworkRequest(QUrl("http://www.whatsmyip.org"))); //works
//test access to random .onion website
manager->get(QNetworkRequest(QUrl("http://blackma6xtzkajcy2eahws4q65ayhnsa6kghu6oa6sci2ul47fq66jqd.onion/products.php")));
//test access to random .onion website via socket
socket.setProxy(proxy);
socket.connectToHost("http://blackma6xtzkajcy2eahws4q65ayhnsa6kghu6oa6sci2ul47fq66jqd.onion/products.php",9051);
qDebug() << "\nTCP SOCKET Response...\n";
if(!socket.waitForConnected(5000))
qDebug() << "Error: " + socket.errorString() +"\n";
}
void tester::replyFinished(QNetworkReply* Reply)
{
qDebug() << "Response...\n";
if (Reply->isOpen())
{
qDebug()<<Reply->read((5000));
Reply->close();
}
qDebug() << "\nTCP SOCKET Response...\n";
if(!socket.waitForConnected(5000))
qDebug() << "Error: " + socket.errorString() +"\n";
}

WIN QAbstractSocket::UnsupportedSocketOperationError while connect Qt EchoClient to https://www.websocket.org/echo.html

I take an example of EchoClient from Qt repository:
https://code.qt.io/cgit/qt/qtwebsockets.git/tree/examples/websockets/echoclient?h=5.14&id=66ea748c2ba1fa35c78c5d55742a982976b07435
I've made only single one modification, I changed URL address I would like to connect to:
EchoClient client(QUrl("wss://echo.websocket.org"), true);
And it doesn't work, expected result is that onConnected callback will fire,
look on code below.
I've added error callback:
EchoClient::EchoClient(const QUrl &url, bool debug, QObject *parent) :
QObject(parent),
m_url(url),
m_debug(debug)
{
m_webSocket = new QWebSocket;
if (m_debug)
qDebug() << "WebSocket server:" << url;
connect(m_webSocket, &QWebSocket::connected, this, &EchoClient::onConnected);
connect(m_webSocket, &QWebSocket::disconnected, this, &EchoClient::closed);
connect(m_webSocket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error),
[=](QAbstractSocket::SocketError error)
{
qDebug() << "error: " << error;
});
QNetworkRequest request=QNetworkRequest(QUrl(url));
m_webSocket->open(request);
}
and what I see is that Qt always returns
QAbstractSocket::UnsupportedSocketOperationError (10) QAbstractSocket::SocketError
What I'm doing wrong? What is the reason for this error?
It requires that OpenSSL SHALL be installed on the system.
In spite of the fact that Qt installer puts some Open SSL libs to Qt\Tools folder, it's not enough to get it work properly.

QNetworkReply returning incomplete XML data

I'm sending a HTTP GET request to a remote server. Using various parameters I define the content I'm interested in. In particular I make sure that output=xml is in the query since it makes the server return a reply as XML.
I have the following connections between my class HttpRetriever and the respective QNetworkReply and QNetworkAccessManager (for the QNetworkRequest see slotStartRequest()):
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
The slots that are of interest here have the following declaration:
slotFinishRequest():
void HttpRetriever::slotFinishRequest()
{
LOG(INFO) << "Finishing HTTP GET request from URL \"" << this->url.toString() << "\"";
this->reply = Q_NULLPTR;
// Reset validity of reply from a previous request
this->validReply = false;
// Skip validation if it's disabled
if (!this->validateReply)
{
LOG(WARNING) << "Validation disabled. In order to enable it see the \"validate\" and \"validationMode\" in \"settings.ini\"";
this->validReply = true;
}
else
{
// Validate reply
this->validReply = validateReply();
}
if (!this->validReply)
{
return;
}
processReply(); // Parsing
this->processingRequest = false;
}
slotReadyReadRequest():
void HttpRetriever::slotReadyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
}
Inside the slotFinishRequest() I call the processReply():
void HttpRetriever::processReply()
{
LOG(INFO) << "Processing reply for request \"" << this->url.toString() << "\"";
LOG(DEBUG) << QString(this->bufferReply);
// Process the XML from the reply and extract necessary data
QXmlStreamReader reader;
reader.addData(this->bufferReply);
// Read the XML reply and extract required data
// TODO
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading XML element";
reader.readNextStartElement();
QXmlStreamAttributes attributes = reader.attributes();
foreach (QXmlStreamAttribute attrib, attributes)
{
LOG(DEBUG) << attrib.name();
}
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
LOG(INFO) << "Sending data to data fusion handler";
// TODO
}
I trigger the HTTP get request through the following slot:
void HttpRetriever::slotStartRequest(quint32 id)
{
if (this->processingRequest)
{
this->reply->abort();
}
this->processingRequest = false;
// The first request also instantiates the manager. If the slot is called after the instance of HafasHttpRetriever
// is moved to a new thread this will ensure proper parenting
if (!this->manager)
{
this->manager = new QNetworkAccessManager(this);
}
quint32 requestId = generateRequestId(stopId);
if (!this->url.hasQuery())
{
LOG(WARNING) << "Attempting to start request without parameters";
}
// Part of the filters applied to the request to reduce the data received (for more see slotSetRequestParams())
QUrlQuery query(this->url.query());
query.addQueryItem("input", QString::number(requestId));
// TODO Add more filters; see documentation
this->url.setQuery(query);
LOG(INFO) << "Requesting data from \"" << this->url.toString() << "\" with request ID:" << requestId;
QNetworkRequest request(this->url);
this->reply = this->manager->get(request);
// Establish connections from/to the reply and the network access manager
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
}
As you can see so far I have laid down the foundation for the network communication between my class and the server and I am yet to start working on the parsing of the XML reply and extracting the information I need from it.
The problem is that I am getting (very, very often) either
Encountered error while parsing XML data: Start tag expected.
or
Encountered error while parsing XML data: Premature end of document
in my processReply() function. This happens every time I get a large reply (a couple of hundreds up to a couple of thousands of lines). It never happens when I get a small one (30-40 lines give or take).
So the issue is obviously somewhere in the amount of data I am receiving, the way it is put together by the QNetworkAccessManager (or whichever Qt component in all this buffers the received chunks of data) and/or perhaps the way I have setup the instances of the network-related components in my class. I also have to make an important note here namely that in my browser (latest Firefox with the HttpRequester add-on) I am always receiving the complete XML no matter how large it is. So this seems to be a problem exclusive to my application and has nothing to do with the network settings on my system.
Since #Marco didn't write the answer...
The problem was that I was rewriting my buffer all the time by assigning the result from QNetworkReply::readAll(). As suggested using QByteArray::append() solves the problem.
In order to prevent a possible issue from this solution namely that you keep appending with each and every next reply you get, QByteArray::clear() needs to be called at some point for example when the finished() signal is emitted. Of course one needs to first process its contents before flushing it down the drain.

BB 10 Cascades Internet Check

I need to check the Internet Connection If I get Socket Error. I am proceeding like this,
void Client::socketError(QAbstractSocket::SocketError socketError) {
HttpPost("https://www.google.co.in/");
}
void Client::HttpPost(QString URL ) {
QNetworkRequest request = QNetworkRequest();
request.setUrl(QUrl(URL));
QNetworkAccessManager *mNetworkAccessManager = new QNetworkAccessManager(this);
bool result = connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(HttpResponse(QNetworkReply *)));
mNetworkAccessManager->get(request);
qDebug() << "::: Client.cpp Request made to Service :::";
}
void Client::HttpResponse(QNetworkReply* reply) {
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "\n Internet Ok **********";
} else {
qDebug() << "\n No Internet **********" << reply->errorString();
showDialog("No Inernet");
}
}
and I am connecting to Signals and Slots like
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(socketError(QAbstractSocket::SocketError)));
But some times I am getting "No Internet Dialog " Even If wi-fi Is Available. Why ?
Method 2:
// this method will return true If Net is available false other wise
// but even this is return false some times even Wi fi signal is Available
bool Client::isNetworkAvailable() {
bool isFound = false;
QNetworkConfigurationManager netMgr;
QList<QNetworkConfiguration> mNetList = netMgr.allConfigurations(QNetworkConfiguration::Active);
if (mNetList.count() > 0) {
if (netMgr.isOnline()) {
isFound = true;
}
}
qDebug() << "\n ************** isNetworkAvailable:::" << isFound;
return isFound;
}
Is there a problem in my code?
Your network check snippet seems a bit too complicated. You could simply just call the following method:
bool QNetworkConfigurationManager::isOnline () const
Returns true if the system is considered to be connected to another device via an active network interface; otherwise returns false.
If this does not work for someone, it very likely means that the internet connection is unreliably, especially if it does not work randomly.
What about
NetworkAccessibility QNetworkAccessManager::networkAccessible ()
networkAccessible : NetworkAccessibility
This property holds whether the network is currently accessible via
this network access manager.
If the network is not accessible the network access manager will not
process any new network requests, all such requests will fail with an
error. Requests with URLs with the file:// scheme will still be
processed.

Poco stops after SMTPClientSession.login

I just started with the Poco library and tried to create an email program (Which I knew virtually nothing about). The following is my code (There may be other problems with it besides the one I've encountered so far, but I just started working on it)
int main(int argc, char** argv)
{
Poco::Net::SocketAddress add("smtp.gmail.com:465");
Poco::Net::StreamSocket sock(add);
Poco::Net::SMTPClientSession sess(sock);
std::cout << "-";
sess.login(
"gmail.com",
Poco::Net::SMTPClientSession::AUTH_LOGIN,
"----",
"----"
);
Poco::Net::MailMessage msg;
Poco::Net::MailRecipient resp(Poco::Net::MailRecipient::PRIMARY_RECIPIENT,"michaelrgoldfine#gmail.com");
msg.addRecipient(resp);
std::string content("HELP SOS");
msg.encodeWord(content);
std::cout << msg.getContent() << "-";
}
When I go into the debugger, it runs fine until it gets to sess.login then suddenly the little bar that represents were I am in the code disappears but the program keeps running (I'm not experienced enough to know what that means). None of the cout stuff I put in actually prints, the debugger just goes past that line but nothing shows up. After a little while this comes up:
terminate called throwing an exception
So what's going on?
You are attempting to use SMTP over TLS (the port 465 passed to the SocketAddress). In one shot you have to learn (1) TLS and certificate handling in POCO, before focusing on (2) your goal: sending an email message.
I suggest to start learning POCO with simpler examples. You can find sample code in the various samples directories in the POCO source code.
I think that your code is just hanging on the TLS handshake, because it doesn't know what to do.
These are the fixes you should do before looking at the solution:
Place your code inside a try/catch block. POCO uses exceptions.
Replace StreamSocket with SecureStreamSocket.
The simplest way to properly initialize SecureStreamSocket is via the Application class. See the Applications slides and Util/samples/SampleApp/src/SampleApp.cpp.
See the documentation for the SSLManager for how to properly tell the Application which certificates to use.
Don't specify an hostname to the login() method. The hostname is optional and should be the client hostname, not the server (See the SMTP RFC).
Remember to actually send the message! Your code is not sending it :-)
OK, and now for the running code. I left steps 4 and 6 as an exercise, but this code will at least run the TLS handshake, will tell you that it cannot verify the server's certificate and, if you answer Yes on the terminal to the questions on the certificates, it will fail the SMTP authentication.
class MiniApp : public Poco::Util::Application {
int main(const vector <string>& args) {
try {
Poco::Net::SocketAddress add("smtp.gmail.com:465");
Poco::Net::SecureStreamSocket sock(add);
Poco::Net::SMTPClientSession session(sock);
session.login(Poco::Net::SMTPClientSession::AUTH_LOGIN, "user", "pw");
Poco::Net::MailMessage msg;
Poco::Net::MailRecipient recipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT,
"michaelrgoldfine#gmail.com");
msg.addRecipient(recipient);
string content("HELP SOS");
msg.encodeWord(content);
} catch (Poco::Exception& e) {
cout << "Error: " << e.displayText() << endl;
return -1;
}
return 0;
}
};
POCO_APP_MAIN(MiniApp)
Yes, so I struggled with login(), trying to use smtp.gmail.com. This is the excerpt of the communication with the SSL session that made it work.
string host("smtp.gmail.com")
Poco::UInt16 port = 587;
SecureSMTPClientSession session(host, port);
session.open();
Poco::Net::initializeSSL();
SharedPtr<InvalidCertificateHandler> ptrHandler = new AcceptCertificateHandler(false);
Context::Ptr ptrContext = new Context(Context::CLIENT_USE, "", "", "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH");
SSLManager::instance().initializeClient(0, ptrHandler, ptrContext);
try
{
// SSL
session.login();
if (session.startTLS(ptrContext))
{
session.login(SMTPClientSession::AUTH_LOGIN, "user#gmail.com", "yourpassword");
session.sendMessage(message);
}
session.close();
Poco::Net::uninitializeSSL();
}
catch (SMTPException &e)
{
cout << e.message() << endl;
session.close();
Poco::Net::uninitializeSSL();
return 0;
}
Original source:
http://www.axistasoft.sg/tutorials/cpp/poco/item/sending-email-messages-using-smtp-protocol