I'm writing a proxy server, http part is ready, but are having problems with https.
I created a certificate and private key (as I understood, without it will not work) in this way:
OpenSSL> req-x509-newkey rsa: 2048-keyout server.key-nodes-days 365-out server.csr
I did a simple QTcpServer that is passed a socketDescriptor to the created object on newIncomingConnection().
In constructor of my object I did:
sock = new QSslSocket();
connect (sock,SIGNAL(readyRead()),this,SLOT(onQuery()));
connect(sock,SIGNAL(disconnected()),this,SLOT(deleteLater()));
connect(sock,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError(QAbstractSocket::SocketError)));
connect(sock,SIGNAL(sslErrors(QList<QSslError>)),this,SLOT(sltSslErrors(QList<QSslError>)));
...
Load key and cert
...
sock->setProtocol(QSsl::AnyProtocol);
QSslKey sslKey(key, QSsl::Rsa);
QSslCertificate sslCert(cert);
sock->setPrivateKey(sslKey);
sock->setLocalCertificate(sslCert);
sock->setSocketDescriptor(socketDesc);
sock->startServerEncryption();
if(!sock->waitForEncrypted(30000)) {
qDebug()<<"wait for encrypted failed";
}
On connect in console I see "wait for encrypted failed" and socket emited signal error() with QAbstractSocket::SslHandshakeFailedError.
Could you give advice on what else to do that would be to establish the ssl connection without error ?
I believe you need to call setSocketDescriptor before calling the setPrivateKey and setLocalCertificate methods.
Below is code that I've used to create a HTTPS Server Socket, extending QTcpServer.
void SslServer::incomingConnection(qintptr socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
QFile keyFile(<sslKeyFileName>);
if (!keyFile.open(QIODevice::ReadOnly)) {
delete serverSocket;
return;
}
QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
if (key.isNull()) {
delete serverSocket;
return;
}
keyFile.close();
serverSocket->setPrivateKey(key);
// to prevent asking for client certificate.
serverSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
serverSocket->setLocalCertificate(<certificateFileName>);
serverSocket->startServerEncryption();
if (serverSocket->waitForEncrypted(3000)) {
// this will emit a newConnection() signal
addPendingConnection(serverSocket);
} else {
qDebug() << "Encryption Failed.";
delete serverSocket;
}
} else {
delete serverSocket;
}
}
Related
My server is qt and for accept client I use QTcpSocket and setSocketDescriptor, if I want to close socket
or any problem that causes the server to disconnect, dart client don't get any notify from server, I will only notice when I start sending data to the server,
Methods not working
socket->close();
socket->aboutToClose();
socket->disconnectFromHost();
dart side
Socket.connect(_serverIp, _serverPort).then((Socket sock) {
_wSock = sock;
_wSock.encoding = utf8;
_wSock
.transform(StreamTransformer.fromHandlers(
handleData: _bufferHandle.handleSocketDataMessage,
))
.listen(sendingSocketData, onError: (e) {
print("onError: " + e.toString());
}, onDone: () async {
print("socket done");
});
}).then((value) async {
_wSock.write(startHandShake)
});
my next problem
In qt and thread I create socket object in heap and set SocketDescriptor
void HandShakeThread::run(){
socket = new QTcpSocket();
if(!socket->setSocketDescriptor(socketId)){
emit error(socket->error());
_isRunning = false;
}
exec();
return;
}
if I want to move a socket to another class and thread I should be to send setSocketDescriptor to another thread and create new heap object and set setSocketDescriptor to listen, if I want to delete old heap QTcpsocket object, my current QTcpsocket disccounected and setSocketDescriptor don't work any more, how to delete old heap QTcpSocket object?
you can use from errorOccurred slot on your client side.
slots:
void errorOccurred(QAbstractSocket::SocketError error);
see the qt refrece for this slot.
I created a TcpServer in order to receive data from a client. The client sends a lot of messages and I would like to read them. So far my TcpServer.cpp looks like this :
void TcpServer::serverStart()
{
server = new QTcpServer(this);
if (!server->listen(QHostAddress("192.168.x.x"), 48583))
{
qDebug() << "Not listening";
server->close();
delete server;
return;
}
else {
qDebug() << "Listening";
}
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}
void TcpServer::newConnection()
{
socket = server->nextPendingConnection();
qDebug() << "Client connected";
connect(socket, SIGNAL(readyRead()), this, SLOT(getData()));
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
}
void TcpServer::getData()
{
QByteArray buffer;
while (socket->bytesAvailable())
{
buffer.append(socket->readAll());
}
qDebug() << buffer;
}
void TcpServer::serverStop()
{
server->close();
delete server;
}
I know my getData function needs a lot more in order to receive everything but I don't understand the steps needed to do that.If someone could give me some pointers I would be grateful !
TCP is a transport protocol which is stream oriented. Imagine it as being a continuous flow of data. There are no messages defined by TCP yet, because once again it is a continuous flow of data.
I'm taking from your comment that you are not using any application layer protocol. You need an application layer protocol, like e.g. http, which is then defining "messages" and giving you further instructions on how to read a complete message.
QtMqtt cannot connect to the server, but I can connect normally using other test software.The server is mosquitto on Ubuntu.
m_client= new QMqttClient(this);
m_client->setProtocolVersion(QMqttClient::MQTT_3_1_1);
m_client->setPort(1883);
m_client->setHostname("127.0.0.1");
m_client->setClientId("qt");
m_client->connectToHost();
connect(m_client,SIGNAL(stateChanged(ClientState)),this,SLOT(slot_stateChanged()),Qt::UniqueConnection);
void slot_stateChanged()
{
qDebug() << "mqtt stsate" << _client->state();
}
you are using a broker at localhost, maybe you should connect the signal slot before calling the connectToHost()
try with
m_client= new QMqttClient(this);
//connect signal slot
connect(m_client,SIGNAL(stateChanged(ClientState)),this,SLOT(slot_stateChanged()),Qt::UniqueConnection);
//connect to borker
m_client->setProtocolVersion(QMqttClient::MQTT_3_1_1);
m_client->setPort(1883);
m_client->setHostname("127.0.0.1");
m_client->setClientId("qt");
m_client->connectToHost();
void slot_stateChanged()
{
qDebug() << "mqtt stsate" << _client->state();
}
I have two questions about this issue.
First of all I'm trying to get the following code working
socket = new QTcpSocket(this);
// I'm a little confused as to why we're connecting on port 80
// when my goal is to listen just on port 3000. Shouldn't I just
// need to connect straight to port 3000?
socket->connectToHost("localhost", 80);
if (socket->waitForConnected(3000))
{
qDebug() << "Connected!";
// send
socket->write("hello server\r\n\r\n\r\n\r\n");
socket->waitForBytesWritten(1000);
socket->waitForReadyRead(3000);
qDebug() << "Reading: " << socket->bytesAvailable();
qDebug() << socket->readAll();
socket->close();
}
else
{
qDebug() << "Not connected!";
}
But this is the error that I get:
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>400 Bad Request</title>\n</head><body>\n<h1>Bad `Request</h1>\n<p>Your browser sent a request that this server could not understand.<br />\n</p>\n<hr>\n<address>Apache/2.4.18 (Ubuntu) Server at 127.0.1.1 Port 80</address>\n</body></html>\n"`
Has anyone got any ideas about this?
Second question is: I'm trying to get a c++/Qt server working similar to a node js server. So I'm wanting to be able to access the connection requests in the browser. So when someone connects to site:3000 I will be able to catch the request and display some content. Can it be achieved with a QTcpSocket server? If so then how could I implement something like :
// I know this isn't valid c++, Just to give an idea of what I'm trying to achieve
socket.on(Request $request) {
if ($request.method() == 'GET') {
}
}
If this is achievable is there much speed gains in comparison to doing this in nodejs?
I'm personally trying to avoid js as much as possible.
if i comment the code then I can get a running program but when I try to connect on port 8000 from the browser nothing happens (just a 404 error)
updated answer:
header file:
#ifndef SOCKETTEST_H
#define SOCKETTEST_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
class SocketTest : public QTcpServer
{
public:
SocketTest(QObject *parent);
private:
QTcpSocket *client;
public slots:
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
};
#endif // SOCKETTEST_H
.cpp file
#include "sockettest.h"
SocketTest::SocketTest(QObject *parent) :
QTcpServer(parent)
{
this->startServer(8000);
}
void SocketTest::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void SocketTest::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
//client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void SocketTest::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
qDebug() << "Just got a connection";
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
// this gives me the following error
// /user_data/projects/qt/QtServer/sockettest.cpp:47: error: no ‘void
// SocketTest::disconnected()’ member function declared in class ‘SocketTest’
void SocketTest::disconnected()
^
void SocketTest::disconnected()
{
// jsut a qu, wont all these * vars lead to a memory leak? and shouldn't I be using a var Qtc... *client; in the header file?
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}
Here with waitForConnected, you are connecting on port 80, and waiting 3000ms maximum for the "connected state", i.e. not connecting on port 3000 at all. This is the blocking way of waiting for a connection to be established, instead of connecting to the QTcpSocket::connected signal.
Like Yuriy pointed out, QNetworkAccessManager is way more convenient to handle HTTP requests as a client. As in your example, you created a TCP client, and not a server
Yes you can build an web server with Qt, it's a bit painfull from scratch (QTcpServer class), but several projects make it a bit easier: QHttpServer, QtWebApp
If performance is your goal, I doubt you can achieve something significantly better (or just "better") without spending a lot of time on it. Namely to be able to handle a large number of request simultaneously in a fast way, a basic implementation will not be enough.
You should subclass QTCPServer. Set it up to listen on the port you want. This object will then get the requests and you can parse them and respond to them.
Something like this (partial code);
#include <QTcpServer>
#include <QTcpSocket>
class mySuperNodeLikeServer : public QTcpServer
{
mySuperNodeLikeServer(QObject *parent);
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
}
// in your .cpp file
void mySuperNodeLikeServer::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void mySuperNodeLikeServer::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void mySuperNodeLikeServer::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
void mySuperNodeLikeServer::disconnected()
{
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}
I am writing ssl proxy server using Qt. Here is code sample:
# header
class SslProxyServer : public QTcpServer
{
Q_OBJECT
public:
explicit SslProxyServer(quint16 port, QObject *parent = 0);
private slots:
void onEncrypted();
void onReadyRead();
void onSslErrors(QList<QSslError> sslErrors);
void onModeChanged(QSslSocket::SslMode sslMode);
void onStateChanged(QAbstractSocket::SocketState socketState);
void onError(QAbstractSocket::SocketError socketError);
protected:
void incomingConnection(qintptr socketDescriptor);
};
# source
SslProxyServer::SslProxyServer(quint16 port, QObject *parent) : QTcpServer(parent)
{
if (!listen(QHostAddress::Any, port)) {
qDebug() << "Unable to start tcp server";
return;
}
if (m_tcpServer->isListening()) {
qDebug() << "Listening port" << m_tcpServer->serverPort();
} else {
qDebug() << "Not listening";
}
}
void SslProxyServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "incomingConnection";
QSslSocket *serverSocket = new QSslSocket(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(onEncrypted()));
connect(serverSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(onSslErrors(QList<QSslError>)));
connect(serverSocket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(onModeChanged(QSslSocket::SslMode)));
connect(serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
connect(serverSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
QSslConfiguration sslConfiguration = serverSocket->sslConfiguration();
// ...
QSslCertificate cert(&certFile, QSsl::Pem);
QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem);
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
sslConfiguration.setLocalCertificate(cert); // set domain cert
sslConfiguration.setPrivateKey(key); // set domain key
sslConfiguration.setProtocol(QSsl::AnyProtocol);
// ...
QSslCertificate caCert(&caCertFile, QSsl::Pem);
sslConfiguration.setCaCertificates(QList<QSslCertificate>() << caCert); // add ca cert
serverSocket->setSslConfiguration(sslConfiguration);
serverSocket->startServerEncryption();
} else {
qDebug() << "Cannot set socket descriptor";
delete serverSocket;
}
}
void SslProxyServer::onEncrypted()
{
qDebug() << "onEncrypted";
}
void SslProxyServer::onReadyRead()
{
qDebug() << "onReadyRead";
}
void SslProxyServer::onSslErrors(QList<QSslError> sslErrors)
{
qDebug() << "onSslErrors";
}
void SslProxyServer::onModeChanged(QSslSocket::SslMode sslMode)
{
qDebug() << "onModeChanged(" << (int) sslMode << ")";
}
void SslProxyServer::onStateChanged(QAbstractSocket::SocketState socketState)
{
qDebug() << "onStateChanged(" << (int) socketState << ")";
}
void SslProxyServer::onError(QAbstractSocket::SocketError socketError)
{
qDebug() << "onError(" << (int) socketError << ")";
QSslSocket *serverSocket = qobject_cast<QSslSocket *>(sender());
qDebug() << serverSocket->errorString();
}
I've generated CA self-signed certificate with private key, and another certificate for specific domain, which I signed with my CA certificate. After I copy CA certificate to /usr/local/share/ca-certificates and run sudo update-ca-certificates. But when I try to connect to my proxy server using 3rd-party app, where my server used as https proxy I get QAbstractSocket::SslHandshakeFailedError error with next output:
Listening port 8888
incomingConnection
onModeChanged( 2 )
onError( 13 )
"Error during SSL handshake: error:1407609B:SSL routines:SSL23_GET_CLIENT_HELLO:https proxy request"
onStateChanged( 0 )
So it does not even enter in onReadyRead slot.
When I try to test my server using openssl command: openssl s_client -connect 127.0.0.1:8888 -debug - it is successfully connected to my server. Output contains next lines:
verify error:num=19:self signed certificate in certificate chain
verify return:0
No client certificate CA names sent
---
SSL handshake has read 2667 bytes and written 439 bytes
Verify return code: 19 (self signed certificate in certificate chain)
---
but I can send data to my server and see its raw value in my onReadyRead slot.
Some info about my env:
OS: Ubuntu 12.04 x86_64
Qt: 5.2.1 (GCC 4.6.1, 64 bits)
Thanks in advance,
... when I try to connect to my proxy server using 3rd-party app, where my server used as https proxy
... When I try to test my server using openssl command: openssl s_client -connect 127.0.0.1:8888 -debug - it is successfully connected to my server.
These are different things. With your openssl command you establish a TCP connection which you immediately upgrade to SSL. But, an https proxy works differently: it first establishes a TCP connection, then issues a HTTP CONNECT command and only once it gets a successful response from the proxy it upgrades the connection to SSL, e.g.
- client to server
> CONNECT ip:port HTTP/1.0\r\n
> \r\n
- followed by server to client
< HTTP/1.0 200 connection established\r\n
< \r\n
... SSL handshake ...
And because the client send a https proxy request like it should but you expect the immediate start of the SSL handshake (that is you expect a ClientHello message) this fails with:
"Error during SSL handshake: error:1407609B:SSL routines:SSL23_GET_CLIENT_HELLO:https proxy request"