1/*
2 Copyright (C) 2003 Justin Karneges <justin@affinix.com>
3 Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21*/
22
23#include <QtCrypto>
24
25#include <QCoreApplication>
26#include <QDebug>
27#include <QHostAddress>
28#include <QTcpServer>
29#include <QTcpSocket>
30#include <QTimer>
31
32#ifdef QT_STATICPLUGIN
33#include "import_plugins.h"
34#endif
35
36char pemdata_cert[] =
37 "-----BEGIN CERTIFICATE-----\n"
38 "MIICeTCCAeKgAwIBAgIRAKKKnOj6Aarmwf0phApitVAwDQYJKoZIhvcNAQEFBQAw\n"
39 "ODELMAkGA1UEBhMCVVMxFDASBgNVBAoTC0V4YW1wbGUgT3JnMRMwEQYDVQQDEwpF\n"
40 "eGFtcGxlIENBMB4XDTA2MDMxNTA3MDU1MloXDTA3MDMxNTA3MDU1MlowOjEVMBMG\n"
41 "A1UEAxMMRXhhbXBsZSBVc2VyMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRXhhbXBs\n"
42 "ZSBPcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPkKn0FfHMvRZv+3uFcw\n"
43 "VrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9sEAY\n"
44 "YNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6YjR2\n"
45 "NEvIKt1P4mHzYXLmwoF24C1bAgMBAAGjgYAwfjAdBgNVHQ4EFgQUmQIdzyDaPYWF\n"
46 "fPJ8PPOOm1eSsucwHwYDVR0jBBgwFoAUkCglAizTO7iqwLeaO6r/8kJuqhMwDAYD\n"
47 "VR0TAQH/BAIwADAeBgNVHREEFzAVgRNleGFtcGxlQGV4YW1wbGUuY29tMA4GA1Ud\n"
48 "DwEB/wQEAwIF4DANBgkqhkiG9w0BAQUFAAOBgQAuhbiUgy2a++EUccaonID7eTJZ\n"
49 "F3D5qXMqUpQxlYxU8du+9AxDD7nFxTMkQC2pzfmEc1znRNmJ1ZeLRL72VYsVndcT\n"
50 "psyM8ABkvPp1d2jWIyccVjGpt+/RN5IPKm/YIbtIZcywvWuXrOp1lanVmppLfPnO\n"
51 "6yneBkC9iqjOv/+Q+A==\n"
52 "-----END CERTIFICATE-----\n";
53
54char pemdata_privkey[] =
55 "-----BEGIN PRIVATE KEY-----\n"
56 "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPkKn0FfHMvRZv+3\n"
57 "uFcwVrOadJmANzLVeVW/DHZp4CXokXSksM66ZMqFuQRBk5rnIZZpZmVp1tTRDVt9\n"
58 "sEAYYNa8CRM4HXkVlU0lCKdey18CSq2VuSvNtw8dDpoBmQt3nr9tePvKHnpS3nm6\n"
59 "YjR2NEvIKt1P4mHzYXLmwoF24C1bAgMBAAECgYEAyIjJHDaeVXDU42zovyxpZE4n\n"
60 "PcOEryY+gdFJE8DFgUD4f1huFsj4iCuNg+PaG42p+hf9IARNvSho/RcEaVg4AJrV\n"
61 "jRP8r7fSqcIGr6lGuvDFFv3SU5ddy84g5oqLYGKvuPSHMGfVsZSxAwOrzD4bH19L\n"
62 "SNqtNcpdBsBd7ZiEE4ECQQD/oJGui9D5Dx3QVcS+QV4F8wuyN9jYIANmX/17o0fl\n"
63 "BL0bwRU4RICwadrcybi5N0JQLIYSUm2HGqNvAJbtnuQxAkEA+WeYLLYPeawcy+WU\n"
64 "kGcOR7BUjHiG71+6cvU4XIDW2bezA04fqWXkZRFAwHTMpQb785/XalFftgS21kql\n"
65 "8yLDSwJAHkeT2hwftdDPlEUEmBDAJW5DvWmWGwu3u2G1cfbGZl9oUyhM7ixXHg57\n"
66 "6VlPs0jTZxHPE86FwNIr99MXDbCbkQJBAMDFOJK+ecGirXNP1P+0GA6DFSap9inJ\n"
67 "BRTbwx+EmgwX966DUOefEOSpbDIVVSPs/Qr2LgtIMEFA7Y0+j3wZD3cCQBsTwccd\n"
68 "ASQx59xakpq11eOlTYz14rjwodr4QMyj26WxEPJtz7hKokx/+EH6fWuPIUSrROM5\n"
69 "07y2gaVbYxtis0s=\n"
70 "-----END PRIVATE KEY-----\n";
71
72class SecureServer : public QObject
73{
74 Q_OBJECT
75
76public:
77 enum
78 {
79 Idle,
80 Handshaking,
81 Active,
82 Closing
83 };
84
85 SecureServer(quint16 _port)
86 : port(_port)
87 {
88 server = new QTcpServer;
89 connect(sender: server, signal: &QTcpServer::newConnection, context: this, slot: &SecureServer::server_handleConnection);
90
91 ssl = new QCA::TLS;
92 connect(sender: ssl, signal: &QCA::TLS::handshaken, context: this, slot: &SecureServer::ssl_handshaken);
93 connect(sender: ssl, signal: &QCA::TLS::readyRead, context: this, slot: &SecureServer::ssl_readyRead);
94 connect(sender: ssl, signal: &QCA::TLS::readyReadOutgoing, context: this, slot: &SecureServer::ssl_readyReadOutgoing);
95 connect(sender: ssl, signal: &QCA::TLS::closed, context: this, slot: &SecureServer::ssl_closed);
96 connect(sender: ssl, signal: &QCA::TLS::error, context: this, slot: &SecureServer::ssl_error);
97
98 cert = QCA::Certificate::fromPEM(s: QString::fromLatin1(ba: pemdata_cert));
99 privkey = QCA::PrivateKey::fromPEM(s: QString::fromLatin1(ba: pemdata_privkey));
100
101 mode = Idle;
102 }
103
104 ~SecureServer() override
105 {
106 delete ssl;
107 delete server;
108 }
109
110 void start()
111 {
112 if (cert.isNull()) {
113 qDebug() << "Error loading cert!";
114 QTimer::singleShot(interval: 0, receiver: this, slot: &SecureServer::quit);
115 return;
116 }
117 if (privkey.isNull()) {
118 qDebug() << "Error loading private key!";
119 QTimer::singleShot(interval: 0, receiver: this, slot: &SecureServer::quit);
120 return;
121 }
122 if (false == server->listen(address: QHostAddress::Any, port)) {
123 qDebug() << "Error binding to port " << port;
124 QTimer::singleShot(interval: 0, receiver: this, slot: &SecureServer::quit);
125 return;
126 }
127 qDebug() << "Listening on port" << port;
128 }
129
130Q_SIGNALS:
131 void quit();
132
133private Q_SLOTS:
134 void sock_readyRead()
135 {
136 QByteArray buf(sock->bytesAvailable(), 0x00);
137
138 int num = sock->read(data: buf.data(), maxlen: buf.size());
139
140 if (-1 == num)
141 qDebug() << "Error reading data from socket";
142
143 if (num < buf.size())
144 buf.resize(size: num);
145
146 ssl->writeIncoming(a: buf);
147 }
148
149 void server_handleConnection()
150 {
151 // Note: only 1 connection supported at a time in this example!
152 if (mode != Idle) {
153 QTcpSocket *tmp = server->nextPendingConnection();
154 tmp->close();
155 connect(sender: tmp, signal: &QTcpSocket::disconnected, context: tmp, slot: &QTcpSocket::deleteLater);
156 qDebug() << "throwing away extra connection";
157 return;
158 }
159 mode = Handshaking;
160 sock = server->nextPendingConnection();
161 connect(sender: sock, signal: &QTcpSocket::readyRead, context: this, slot: &SecureServer::sock_readyRead);
162 connect(sender: sock, signal: &QTcpSocket::disconnected, context: this, slot: &SecureServer::sock_disconnected);
163#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
164 connect(sender: sock, signal: &QTcpSocket::errorOccurred, context: this, slot: &SecureServer::sock_error);
165#else
166 connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &SecureServer::sock_error);
167#endif
168 connect(sender: sock, signal: &QTcpSocket::bytesWritten, context: this, slot: &SecureServer::sock_bytesWritten);
169
170 qDebug() << "Connection received! Starting TLS handshake.";
171 ssl->setCertificate(cert, key: privkey);
172 ssl->startServer();
173 }
174
175 void sock_disconnected()
176 {
177 qDebug() << "Connection closed.";
178 }
179
180 void sock_bytesWritten(qint64 x)
181 {
182 if (mode == Active && sent) {
183 qint64 bytes = ssl->convertBytesWritten(encryptedBytes: x);
184 bytesLeft -= bytes;
185
186 if (bytesLeft == 0) {
187 mode = Closing;
188 qDebug() << "Data transfer complete - SSL shutting down";
189 ssl->close();
190 }
191 }
192 }
193
194 void sock_error(QAbstractSocket::SocketError error)
195 {
196 qDebug() << "Socket error: " << (unsigned)error;
197 }
198
199 void ssl_handshaken()
200 {
201 qDebug() << "Successful SSL handshake. Waiting for newline.";
202 bytesLeft = 0;
203 sent = false;
204 mode = Active;
205 ssl->continueAfterStep();
206 }
207
208 void ssl_readyRead()
209 {
210 ssl->read();
211 QByteArray b =
212 "<html>\n"
213 "<head><title>Test</title></head>\n"
214 "<body>this is only a test</body>\n"
215 "</html>\n";
216
217 qDebug() << "Sending test response.";
218 sent = true;
219 ssl->write(a: b);
220 }
221
222 void ssl_readyReadOutgoing()
223 {
224 int plainBytes;
225 QByteArray outgoingData = ssl->readOutgoing(plainBytes: &plainBytes);
226 sock->write(data: outgoingData);
227 }
228
229 void ssl_closed()
230 {
231 qDebug() << "Closing socket.";
232 sock->close();
233 mode = Idle;
234 }
235
236 void ssl_error()
237 {
238 if (ssl->errorCode() == QCA::TLS::ErrorHandshake) {
239 qDebug() << "SSL Handshake Error! Closing.";
240 sock->close();
241 } else {
242 qDebug() << "SSL Error! Closing.";
243 sock->close();
244 }
245 mode = Idle;
246 }
247
248private:
249 quint16 port;
250 QTcpServer *server;
251 QTcpSocket *sock;
252 QCA::TLS *ssl;
253 QCA::Certificate cert;
254 QCA::PrivateKey privkey;
255
256 bool sent;
257 int mode;
258 qint64 bytesLeft;
259};
260
261#include "sslservtest.moc"
262
263int main(int argc, char **argv)
264{
265 QCA::Initializer init;
266
267 QCoreApplication app(argc, argv);
268 int port = argc > 1 ? QString::fromLatin1(ba: argv[1]).toInt() : 8000;
269
270 if (!QCA::isSupported(features: "tls")) {
271 qDebug() << "TLS not supported!";
272 return 1;
273 }
274
275 SecureServer *server = new SecureServer(port);
276 QObject::connect(sender: server, signal: &SecureServer::quit, context: &app, slot: &QCoreApplication::quit);
277 server->start();
278 app.exec();
279 delete server;
280
281 return 0;
282}
283

source code of qca/examples/sslservtest/sslservtest.cpp