QtBase  v6.3.1
server.cpp
Go to the documentation of this file.
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 
56 
57 namespace {
58 
60 {
61  const static QString info = QStringLiteral("(%1:%2)");
62  return info.arg(address.toString()).arg(port);
63 }
64 
66 {
67  QString info(DtlsServer::tr("Session cipher: "));
68  info += connection->sessionCipher().name();
69 
70  info += DtlsServer::tr("; session protocol: ");
71  switch (connection->sessionProtocol()) {
72  case QSsl::DtlsV1_2:
73  info += DtlsServer::tr("DTLS 1.2.");
74  break;
75  default:
76  info += DtlsServer::tr("Unknown protocol.");
77  }
78 
79  return info;
80 }
81 
82 } // unnamed namespace
83 
86 {
87  connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead);
88 
89  serverConfiguration = QSslConfiguration::defaultDtlsConfiguration();
90  serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server");
91  serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
92 }
94 
96 {
97  shutdown();
98 }
99 
102 {
103  if (address != serverSocket.localAddress() || port != serverSocket.localPort()) {
104  shutdown();
105  listening = serverSocket.bind(address, port);
106  if (!listening)
107  emit errorMessage(serverSocket.errorString());
108  } else {
109  listening = true;
110  }
111 
112  return listening;
113 }
115 
117 {
118  return listening;
119 }
120 
122 {
123  listening = false;
124 }
125 
126 void DtlsServer::readyRead()
127 {
129  const qint64 bytesToRead = serverSocket.pendingDatagramSize();
130  if (bytesToRead <= 0) {
131  emit warningMessage(tr("A spurious read notification"));
132  return;
133  }
134 
135  QByteArray dgram(bytesToRead, Qt::Uninitialized);
136  QHostAddress peerAddress;
137  quint16 peerPort = 0;
138  const qint64 bytesRead = serverSocket.readDatagram(dgram.data(), dgram.size(),
139  &peerAddress, &peerPort);
140  if (bytesRead <= 0) {
141  emit warningMessage(tr("Failed to read a datagram: ") + serverSocket.errorString());
142  return;
143  }
144 
145  dgram.resize(bytesRead);
148  if (peerAddress.isNull() || !peerPort) {
149  emit warningMessage(tr("Failed to extract peer info (address, port)"));
150  return;
151  }
152 
153  const auto client = std::find_if(knownClients.begin(), knownClients.end(),
154  [&](const std::unique_ptr<QDtls> &connection){
155  return connection->peerAddress() == peerAddress
156  && connection->peerPort() == peerPort;
157  });
159 
161  if (client == knownClients.end())
162  return handleNewConnection(peerAddress, peerPort, dgram);
164 
166  if ((*client)->isConnectionEncrypted()) {
167  decryptDatagram(client->get(), dgram);
168  if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError)
169  knownClients.erase(client);
170  return;
171  }
173 
175  doHandshake(client->get(), dgram);
177 }
178 
180 void DtlsServer::pskRequired(QSslPreSharedKeyAuthenticator *auth)
181 {
182  Q_ASSERT(auth);
183 
184  emit infoMessage(tr("PSK callback, received a client's identity: '%1'")
185  .arg(QString::fromLatin1(auth->identity())));
186  auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f"));
187 }
189 
191 void DtlsServer::handleNewConnection(const QHostAddress &peerAddress,
192  quint16 peerPort, const QByteArray &clientHello)
193 {
194  if (!listening)
195  return;
196 
197  const QString peerInfo = peer_info(peerAddress, peerPort);
198  if (cookieSender.verifyClient(&serverSocket, clientHello, peerAddress, peerPort)) {
199  emit infoMessage(peerInfo + tr(": verified, starting a handshake"));
202  std::unique_ptr<QDtls> newConnection{new QDtls{QSslSocket::SslServerMode}};
203  newConnection->setDtlsConfiguration(serverConfiguration);
204  newConnection->setPeer(peerAddress, peerPort);
205  newConnection->connect(newConnection.get(), &QDtls::pskRequired,
206  this, &DtlsServer::pskRequired);
207  knownClients.push_back(std::move(newConnection));
208  doHandshake(knownClients.back().get(), clientHello);
210  } else if (cookieSender.dtlsError() != QDtlsError::NoError) {
211  emit errorMessage(tr("DTLS error: ") + cookieSender.dtlsErrorString());
212  } else {
213  emit infoMessage(peerInfo + tr(": not verified yet"));
214  }
215 }
216 
218 void DtlsServer::doHandshake(QDtls *newConnection, const QByteArray &clientHello)
219 {
220  const bool result = newConnection->doHandshake(&serverSocket, clientHello);
221  if (!result) {
222  emit errorMessage(newConnection->dtlsErrorString());
223  return;
224  }
225 
226  const QString peerInfo = peer_info(newConnection->peerAddress(),
227  newConnection->peerPort());
228  switch (newConnection->handshakeState()) {
230  emit infoMessage(peerInfo + tr(": handshake is in progress ..."));
231  break;
233  emit infoMessage(tr("Connection with %1 encrypted. %2")
234  .arg(peerInfo, connection_info(newConnection)));
235  break;
236  default:
237  Q_UNREACHABLE();
238  }
239 }
241 
243 void DtlsServer::decryptDatagram(QDtls *connection, const QByteArray &clientMessage)
244 {
245  Q_ASSERT(connection->isConnectionEncrypted());
246 
247  const QString peerInfo = peer_info(connection->peerAddress(), connection->peerPort());
248  const QByteArray dgram = connection->decryptDatagram(&serverSocket, clientMessage);
249  if (dgram.size()) {
250  emit datagramReceived(peerInfo, clientMessage, dgram);
251  connection->writeDatagramEncrypted(&serverSocket, tr("to %1: ACK").arg(peerInfo).toLatin1());
252  } else if (connection->dtlsError() == QDtlsError::NoError) {
253  emit warningMessage(peerInfo + ": " + tr("0 byte dgram, could be a re-connect attempt?"));
254  } else {
255  emit errorMessage(peerInfo + ": " + connection->dtlsErrorString());
256  }
257 }
259 
261 void DtlsServer::shutdown()
262 {
263  for (const auto &connection : qExchange(knownClients, {}))
264  connection->shutdown(&serverSocket);
265 
266  serverSocket.close();
267 }
269 
void infoMessage(const QString &message)
bool isListening() const
[2]
Definition: server.cpp:116
DtlsServer()
[1]
Definition: server.cpp:85
void close()
Definition: server.cpp:121
void datagramReceived(const QString &peerInfo, const QByteArray &cipherText, const QByteArray &plainText)
~DtlsServer()
[1]
Definition: server.cpp:95
void errorMessage(const QString &message)
void warningMessage(const QString &message)
bool listen(const QHostAddress &address, quint16 port)
[2]
Definition: server.cpp:101
QHostAddress localAddress() const
virtual bool bind(const QHostAddress &address, quint16 port=0, BindMode mode=DefaultForPlatform)
void close() override
quint16 localPort() const
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
qsizetype size() const noexcept
Definition: qbytearray.h:470
QString dtlsErrorString() const
Definition: qdtls.cpp:527
bool verifyClient(QUdpSocket *socket, const QByteArray &dgram, const QHostAddress &address, quint16 port)
Definition: qdtls.cpp:467
QDtlsError dtlsError() const
Definition: qdtls.cpp:512
This class provides encryption for UDP sockets.
Definition: qdtls.h:119
@ HandshakeComplete
Definition: qdtls.h:129
@ HandshakeInProgress
Definition: qdtls.h:127
bool doHandshake(QUdpSocket *socket, const QByteArray &dgram={})
Definition: qdtls.cpp:846
quint16 peerPort() const
Definition: qdtls.cpp:659
QString dtlsErrorString() const
Definition: qdtls.cpp:1181
HandshakeState handshakeState() const
Definition: qdtls.cpp:817
QHostAddress peerAddress() const
Definition: qdtls.cpp:644
void pskRequired(QSslPreSharedKeyAuthenticator *authenticator)
The QHostAddress class provides an IP address.\inmodule QtNetwork.
Definition: qhostaddress.h:74
bool isNull() const
void readyRead()
QString errorString() const
Definition: qiodevice.cpp:2169
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Definition: qobject.cpp:2772
void setPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
void setPreSharedKeyIdentityHint(const QByteArray &hint)
The QSslPreSharedKeyAuthenticator class provides authentication data for pre shared keys (PSK) cipher...
Q_NETWORK_EXPORT QByteArray identity() const
Q_NETWORK_EXPORT void setPreSharedKey(const QByteArray &preSharedKey)
@ SslServerMode
Definition: qsslsocket.h:72
The QString class provides a Unicode character string.
Definition: qstring.h:388
static QString fromLatin1(QByteArrayView ba)
Definition: qstring.cpp:5488
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host=nullptr, quint16 *port=nullptr)
Definition: qudpsocket.cpp:494
qint64 pendingDatagramSize() const
Definition: qudpsocket.cpp:314
backing_store_ptr info
[4]
Definition: jmemsys.h:161
@ DtlsV1_2
Definition: qssl.h:88
QString peer_info(const QHostAddress &address, quint16 port)
Definition: server.cpp:59
QString connection_info(QDtls *connection)
Definition: server.cpp:65
constexpr Initialization Uninitialized
Definition: qnamespace.h:1613
#define QByteArrayLiteral(str)
Definition: qbytearray.h:80
#define Q_UNREACHABLE()
DBusConnection * connection
@ RemoteClosedConnectionError
EGLOutputPortEXT port
unsigned short quint16
Definition: qglobal.h:286
long long qint64
Definition: qglobal.h:298
GLuint GLuint64EXT address
Definition: qopenglext.h:11428
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
SSL_CTX int(*) void arg)
#define QStringLiteral(str)
#define tr(X)
#define emit
Definition: qtmetamacros.h:85
QByteArray clientHello(serverSocket.pendingDatagramSize(), Qt::Uninitialized)