1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "server.h"
52
53#include <algorithm>
54
55QT_BEGIN_NAMESPACE
56
57namespace {
58
59QString peer_info(const QHostAddress &address, quint16 port)
60{
61 const static QString info = QStringLiteral("(%1:%2)");
62 return info.arg(a: address.toString()).arg(a: port);
63}
64
65QString connection_info(QDtls *connection)
66{
67 QString info(DtlsServer::tr(s: "Session cipher: "));
68 info += connection->sessionCipher().name();
69
70 info += DtlsServer::tr(s: "; session protocol: ");
71 switch (connection->sessionProtocol()) {
72 case QSsl::DtlsV1_0:
73 info += DtlsServer::tr(s: "DTLS 1.0.");
74 break;
75 case QSsl::DtlsV1_2:
76 info += DtlsServer::tr(s: "DTLS 1.2.");
77 break;
78 case QSsl::DtlsV1_2OrLater:
79 info += DtlsServer::tr(s: "DTLS 1.2 or later.");
80 break;
81 default:
82 info += DtlsServer::tr(s: "Unknown protocol.");
83 }
84
85 return info;
86}
87
88} // unnamed namespace
89
90//! [1]
91DtlsServer::DtlsServer()
92{
93 connect(sender: &serverSocket, signal: &QAbstractSocket::readyRead, receiver: this, slot: &DtlsServer::readyRead);
94
95 serverConfiguration = QSslConfiguration::defaultDtlsConfiguration();
96 serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server");
97 serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
98}
99//! [1]
100
101DtlsServer::~DtlsServer()
102{
103 shutdown();
104}
105
106//! [2]
107bool DtlsServer::listen(const QHostAddress &address, quint16 port)
108{
109 if (address != serverSocket.localAddress() || port != serverSocket.localPort()) {
110 shutdown();
111 listening = serverSocket.bind(address, port);
112 if (!listening)
113 emit errorMessage(message: serverSocket.errorString());
114 } else {
115 listening = true;
116 }
117
118 return listening;
119}
120//! [2]
121
122bool DtlsServer::isListening() const
123{
124 return listening;
125}
126
127void DtlsServer::close()
128{
129 listening = false;
130}
131
132void DtlsServer::readyRead()
133{
134 //! [3]
135 const qint64 bytesToRead = serverSocket.pendingDatagramSize();
136 if (bytesToRead <= 0) {
137 emit warningMessage(message: tr(s: "A spurious read notification"));
138 return;
139 }
140
141 QByteArray dgram(bytesToRead, Qt::Uninitialized);
142 QHostAddress peerAddress;
143 quint16 peerPort = 0;
144 const qint64 bytesRead = serverSocket.readDatagram(data: dgram.data(), maxlen: dgram.size(),
145 host: &peerAddress, port: &peerPort);
146 if (bytesRead <= 0) {
147 emit warningMessage(message: tr(s: "Failed to read a datagram: ") + serverSocket.errorString());
148 return;
149 }
150
151 dgram.resize(size: bytesRead);
152 //! [3]
153 //! [4]
154 if (peerAddress.isNull() || !peerPort) {
155 emit warningMessage(message: tr(s: "Failed to extract peer info (address, port)"));
156 return;
157 }
158
159 const auto client = std::find_if(first: knownClients.begin(), last: knownClients.end(),
160 pred: [&](const std::unique_ptr<QDtls> &connection){
161 return connection->peerAddress() == peerAddress
162 && connection->peerPort() == peerPort;
163 });
164 //! [4]
165
166 //! [5]
167 if (client == knownClients.end())
168 return handleNewConnection(peerAddress, peerPort, clientHello: dgram);
169 //! [5]
170
171 //! [6]
172 if ((*client)->isConnectionEncrypted()) {
173 decryptDatagram(connection: client->get(), clientMessage: dgram);
174 if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError)
175 knownClients.erase(position: client);
176 return;
177 }
178 //! [6]
179
180 //! [7]
181 doHandshake(newConnection: client->get(), clientHello: dgram);
182 //! [7]
183}
184
185//! [13]
186void DtlsServer::pskRequired(QSslPreSharedKeyAuthenticator *auth)
187{
188 Q_ASSERT(auth);
189
190 emit infoMessage(message: tr(s: "PSK callback, received a client's identity: '%1'")
191 .arg(a: QString::fromLatin1(str: auth->identity())));
192 auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f"));
193}
194//! [13]
195
196//! [8]
197void DtlsServer::handleNewConnection(const QHostAddress &peerAddress,
198 quint16 peerPort, const QByteArray &clientHello)
199{
200 if (!listening)
201 return;
202
203 const QString peerInfo = peer_info(address: peerAddress, port: peerPort);
204 if (cookieSender.verifyClient(socket: &serverSocket, dgram: clientHello, address: peerAddress, port: peerPort)) {
205 emit infoMessage(message: peerInfo + tr(s: ": verified, starting a handshake"));
206 //! [8]
207 //! [9]
208 std::unique_ptr<QDtls> newConnection{new QDtls{QSslSocket::SslServerMode}};
209 newConnection->setDtlsConfiguration(serverConfiguration);
210 newConnection->setPeer(address: peerAddress, port: peerPort);
211 newConnection->connect(sender: newConnection.get(), signal: &QDtls::pskRequired,
212 receiver: this, slot: &DtlsServer::pskRequired);
213 knownClients.push_back(x: std::move(newConnection));
214 doHandshake(newConnection: knownClients.back().get(), clientHello);
215 //! [9]
216 } else if (cookieSender.dtlsError() != QDtlsError::NoError) {
217 emit errorMessage(message: tr(s: "DTLS error: ") + cookieSender.dtlsErrorString());
218 } else {
219 emit infoMessage(message: peerInfo + tr(s: ": not verified yet"));
220 }
221}
222
223//! [11]
224void DtlsServer::doHandshake(QDtls *newConnection, const QByteArray &clientHello)
225{
226 const bool result = newConnection->doHandshake(socket: &serverSocket, dgram: clientHello);
227 if (!result) {
228 emit errorMessage(message: newConnection->dtlsErrorString());
229 return;
230 }
231
232 const QString peerInfo = peer_info(address: newConnection->peerAddress(),
233 port: newConnection->peerPort());
234 switch (newConnection->handshakeState()) {
235 case QDtls::HandshakeInProgress:
236 emit infoMessage(message: peerInfo + tr(s: ": handshake is in progress ..."));
237 break;
238 case QDtls::HandshakeComplete:
239 emit infoMessage(message: tr(s: "Connection with %1 encrypted. %2")
240 .arg(args: peerInfo, args: connection_info(connection: newConnection)));
241 break;
242 default:
243 Q_UNREACHABLE();
244 }
245}
246//! [11]
247
248//! [12]
249void DtlsServer::decryptDatagram(QDtls *connection, const QByteArray &clientMessage)
250{
251 Q_ASSERT(connection->isConnectionEncrypted());
252
253 const QString peerInfo = peer_info(address: connection->peerAddress(), port: connection->peerPort());
254 const QByteArray dgram = connection->decryptDatagram(socket: &serverSocket, dgram: clientMessage);
255 if (dgram.size()) {
256 emit datagramReceived(peerInfo, cipherText: clientMessage, plainText: dgram);
257 connection->writeDatagramEncrypted(socket: &serverSocket, dgram: tr(s: "to %1: ACK").arg(a: peerInfo).toLatin1());
258 } else if (connection->dtlsError() == QDtlsError::NoError) {
259 emit warningMessage(message: peerInfo + ": " + tr(s: "0 byte dgram, could be a re-connect attempt?"));
260 } else {
261 emit errorMessage(message: peerInfo + ": " + connection->dtlsErrorString());
262 }
263}
264//! [12]
265
266//! [14]
267void DtlsServer::shutdown()
268{
269 for (const auto &connection : qExchange(t&: knownClients, newValue: {}))
270 connection->shutdown(socket: &serverSocket);
271
272 serverSocket.close();
273}
274//! [14]
275
276QT_END_NAMESPACE
277

source code of qtbase/examples/network/secureudpserver/server.cpp