diff options
Diffstat (limited to 'examples/network')
27 files changed, 2562 insertions, 167 deletions
diff --git a/examples/network/network-chat/client.cpp b/examples/network/network-chat/client.cpp index c1eda52a0c..97c2c44b6b 100644 --- a/examples/network/network-chat/client.cpp +++ b/examples/network/network-chat/client.cpp @@ -78,7 +78,7 @@ void Client::sendMessage(const QString &message) QString Client::nickName() const { - return QString(peerManager->userName()) + '@' + QHostInfo::localHostName() + return peerManager->userName() + '@' + QHostInfo::localHostName() + ':' + QString::number(server.serverPort()); } diff --git a/examples/network/network-chat/connection.cpp b/examples/network/network-chat/connection.cpp index 332d5dc56b..58cf67eb6d 100644 --- a/examples/network/network-chat/connection.cpp +++ b/examples/network/network-chat/connection.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. @@ -55,17 +56,29 @@ static const int TransferTimeout = 30 * 1000; static const int PongTimeout = 60 * 1000; static const int PingInterval = 5 * 1000; -static const char SeparatorToken = ' '; + +/* + * Protocol is defined as follows, using the CBOR Data Definition Language: + * + * protocol = [ + * greeting, ; must start with a greeting command + * * command ; zero or more regular commands after + * ] + * command = plaintext / ping / pong / greeting + * plaintext = { 0 => text } + * ping = { 1 => null } + * pong = { 2 => null } + * greeting = { 3 => text } + */ Connection::Connection(QObject *parent) - : QTcpSocket(parent) + : QTcpSocket(parent), writer(this) { greetingMessage = tr("undefined"); username = tr("unknown"); state = WaitingForGreeting; currentDataType = Undefined; - numBytesForCurrentDataType = -1; - transferTimerId = 0; + transferTimerId = -1; isGreetingMessageSent = false; pingTimer.setInterval(PingInterval); @@ -76,6 +89,22 @@ Connection::Connection(QObject *parent) this, SLOT(sendGreetingMessage())); } +Connection::Connection(qintptr socketDescriptor, QObject *parent) + : Connection(parent) +{ + setSocketDescriptor(socketDescriptor); + reader.setDevice(this); +} + +Connection::~Connection() +{ + if (isGreetingMessageSent) { + // Indicate clean shutdown. + writer.endArray(); + waitForBytesWritten(2000); + } +} + QString Connection::name() const { return username; @@ -91,9 +120,11 @@ bool Connection::sendMessage(const QString &message) if (message.isEmpty()) return false; - QByteArray msg = message.toUtf8(); - QByteArray data = "MESSAGE " + QByteArray::number(msg.size()) + ' ' + msg; - return write(data) == data.size(); + writer.startMap(1); + writer.append(PlainText); + writer.append(message); + writer.endMap(); + return true; } void Connection::timerEvent(QTimerEvent *timerEvent) @@ -101,61 +132,75 @@ void Connection::timerEvent(QTimerEvent *timerEvent) if (timerEvent->timerId() == transferTimerId) { abort(); killTimer(transferTimerId); - transferTimerId = 0; + transferTimerId = -1; } } void Connection::processReadyRead() { - if (state == WaitingForGreeting) { - if (!readProtocolHeader()) - return; - if (currentDataType != Greeting) { - abort(); - return; + // we've got more data, let's parse + reader.reparse(); + while (reader.lastError() == QCborError::NoError) { + if (state == WaitingForGreeting) { + if (!reader.isArray()) + break; // protocol error + + reader.enterContainer(); // we'll be in this array forever + state = ReadingGreeting; + } else if (reader.containerDepth() == 1) { + // Current state: no command read + // Next state: read command ID + if (!reader.hasNext()) { + reader.leaveContainer(); + disconnectFromHost(); + return; + } + + if (!reader.isMap() || !reader.isLengthKnown() || reader.length() != 1) + break; // protocol error + reader.enterContainer(); + } else if (currentDataType == Undefined) { + // Current state: read command ID + // Next state: read command payload + if (!reader.isInteger()) + break; // protocol error + currentDataType = DataType(reader.toInteger()); + reader.next(); + } else { + // Current state: read command payload + if (reader.isString()) { + auto r = reader.readString(); + buffer += r.data; + if (r.status != QCborStreamReader::EndOfString) + continue; + } else if (reader.isNull()) { + reader.next(); + } else { + break; // protocol error + } + + // Next state: no command read + reader.leaveContainer(); + if (transferTimerId != -1) { + killTimer(transferTimerId); + transferTimerId = -1; + } + + if (state == ReadingGreeting) { + if (currentDataType != Greeting) + break; // protocol error + processGreeting(); + } else { + processData(); + } } - state = ReadingGreeting; } - if (state == ReadingGreeting) { - if (!hasEnoughData()) - return; - - buffer = read(numBytesForCurrentDataType); - if (buffer.size() != numBytesForCurrentDataType) { - abort(); - return; - } - - username = QString(buffer) + '@' + peerAddress().toString() + ':' - + QString::number(peerPort()); - currentDataType = Undefined; - numBytesForCurrentDataType = 0; - buffer.clear(); - - if (!isValid()) { - abort(); - return; - } - - if (!isGreetingMessageSent) - sendGreetingMessage(); - - pingTimer.start(); - pongTime.start(); - state = ReadyForUse; - emit readyForUse(); - } + if (reader.lastError() != QCborError::EndOfFile) + abort(); // parse error - do { - if (currentDataType == Undefined) { - if (!readProtocolHeader()) - return; - } - if (!hasEnoughData()) - return; - processData(); - } while (bytesAvailable() > 0); + if (transferTimerId != -1 && reader.containerDepth() > 1) + transferTimerId = startTimer(TransferTimeout); } void Connection::sendPing() @@ -165,112 +210,58 @@ void Connection::sendPing() return; } - write("PING 1 p"); + writer.startMap(1); + writer.append(Ping); + writer.append(nullptr); // no payload + writer.endMap(); } void Connection::sendGreetingMessage() { - QByteArray greeting = greetingMessage.toUtf8(); - QByteArray data = "GREETING " + QByteArray::number(greeting.size()) + ' ' + greeting; - if (write(data) == data.size()) - isGreetingMessageSent = true; -} + writer.startArray(); // this array never ends -int Connection::readDataIntoBuffer(int maxSize) -{ - if (maxSize > MaxBufferSize) - return 0; + writer.startMap(1); + writer.append(Greeting); + writer.append(greetingMessage); + writer.endMap(); + isGreetingMessageSent = true; - int numBytesBeforeRead = buffer.size(); - if (numBytesBeforeRead == MaxBufferSize) { - abort(); - return 0; - } - - while (bytesAvailable() > 0 && buffer.size() < maxSize) { - buffer.append(read(1)); - if (buffer.endsWith(SeparatorToken)) - break; - } - return buffer.size() - numBytesBeforeRead; + if (!reader.device()) + reader.setDevice(this); } -int Connection::dataLengthForCurrentDataType() +void Connection::processGreeting() { - if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0 - || !buffer.endsWith(SeparatorToken)) - return 0; - - buffer.chop(1); - int number = buffer.toInt(); + username = buffer + '@' + peerAddress().toString() + ':' + + QString::number(peerPort()); + currentDataType = Undefined; buffer.clear(); - return number; -} - -bool Connection::readProtocolHeader() -{ - if (transferTimerId) { - killTimer(transferTimerId); - transferTimerId = 0; - } - - if (readDataIntoBuffer() <= 0) { - transferTimerId = startTimer(TransferTimeout); - return false; - } - if (buffer == "PING ") { - currentDataType = Ping; - } else if (buffer == "PONG ") { - currentDataType = Pong; - } else if (buffer == "MESSAGE ") { - currentDataType = PlainText; - } else if (buffer == "GREETING ") { - currentDataType = Greeting; - } else { - currentDataType = Undefined; + if (!isValid()) { abort(); - return false; - } - - buffer.clear(); - numBytesForCurrentDataType = dataLengthForCurrentDataType(); - return true; -} - -bool Connection::hasEnoughData() -{ - if (transferTimerId) { - QObject::killTimer(transferTimerId); - transferTimerId = 0; + return; } - if (numBytesForCurrentDataType <= 0) - numBytesForCurrentDataType = dataLengthForCurrentDataType(); - - if (bytesAvailable() < numBytesForCurrentDataType - || numBytesForCurrentDataType <= 0) { - transferTimerId = startTimer(TransferTimeout); - return false; - } + if (!isGreetingMessageSent) + sendGreetingMessage(); - return true; + pingTimer.start(); + pongTime.start(); + state = ReadyForUse; + emit readyForUse(); } void Connection::processData() { - buffer = read(numBytesForCurrentDataType); - if (buffer.size() != numBytesForCurrentDataType) { - abort(); - return; - } - switch (currentDataType) { case PlainText: - emit newMessage(username, QString::fromUtf8(buffer)); + emit newMessage(username, buffer); break; case Ping: - write("PONG 1 p"); + writer.startMap(1); + writer.append(Pong); + writer.append(nullptr); // no payload + writer.endMap(); break; case Pong: pongTime.restart(); @@ -280,6 +271,5 @@ void Connection::processData() } currentDataType = Undefined; - numBytesForCurrentDataType = 0; buffer.clear(); } diff --git a/examples/network/network-chat/connection.h b/examples/network/network-chat/connection.h index 67d25d4d1e..fa0671a522 100644 --- a/examples/network/network-chat/connection.h +++ b/examples/network/network-chat/connection.h @@ -51,10 +51,12 @@ #ifndef CONNECTION_H #define CONNECTION_H +#include <QCborStreamReader> +#include <QCborStreamWriter> +#include <QElapsedTimer> #include <QHostAddress> #include <QString> #include <QTcpSocket> -#include <QTime> #include <QTimer> static const int MaxBufferSize = 1024000; @@ -78,6 +80,8 @@ public: }; Connection(QObject *parent = 0); + Connection(qintptr socketDescriptor, QObject *parent = 0); + ~Connection(); QString name() const; void setGreetingMessage(const QString &message); @@ -96,20 +100,19 @@ private slots: void sendGreetingMessage(); private: - int readDataIntoBuffer(int maxSize = MaxBufferSize); - int dataLengthForCurrentDataType(); - bool readProtocolHeader(); bool hasEnoughData(); + void processGreeting(); void processData(); + QCborStreamReader reader; + QCborStreamWriter writer; QString greetingMessage; QString username; QTimer pingTimer; - QTime pongTime; - QByteArray buffer; + QElapsedTimer pongTime; + QString buffer; ConnectionState state; DataType currentDataType; - int numBytesForCurrentDataType; int transferTimerId; bool isGreetingMessageSent; }; diff --git a/examples/network/network-chat/peermanager.cpp b/examples/network/network-chat/peermanager.cpp index c70cc5e56d..38fa2e8e50 100644 --- a/examples/network/network-chat/peermanager.cpp +++ b/examples/network/network-chat/peermanager.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. @@ -62,16 +63,14 @@ PeerManager::PeerManager(Client *client) { this->client = client; - QStringList envVariables; - envVariables << "USERNAME" << "USER" << "USERDOMAIN" - << "HOSTNAME" << "DOMAINNAME"; + static const char *envVariables[] = { + "USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME" + }; - QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); - foreach (QString string, envVariables) { - if (environment.contains(string)) { - username = environment.value(string).toUtf8(); + for (const char *varname : envVariables) { + username = qEnvironmentVariable(varname); + if (!username.isNull()) break; - } } if (username.isEmpty()) @@ -95,7 +94,7 @@ void PeerManager::setServerPort(int port) serverPort = port; } -QByteArray PeerManager::userName() const +QString PeerManager::userName() const { return username; } @@ -108,7 +107,7 @@ void PeerManager::startBroadcasting() bool PeerManager::isLocalHostAddress(const QHostAddress &address) { foreach (QHostAddress localAddress, ipAddresses) { - if (address == localAddress) + if (address.isEqual(localAddress)) return true; } return false; @@ -116,9 +115,14 @@ bool PeerManager::isLocalHostAddress(const QHostAddress &address) void PeerManager::sendBroadcastDatagram() { - QByteArray datagram(username); - datagram.append('@'); - datagram.append(QByteArray::number(serverPort)); + QByteArray datagram; + { + QCborStreamWriter writer(&datagram); + writer.startArray(2); + writer.append(username); + writer.append(serverPort); + writer.endArray(); + } bool validBroadcastAddresses = true; foreach (QHostAddress address, broadcastAddresses) { @@ -142,11 +146,27 @@ void PeerManager::readBroadcastDatagram() &senderIp, &senderPort) == -1) continue; - QList<QByteArray> list = datagram.split('@'); - if (list.size() != 2) - continue; + int senderServerPort; + { + // decode the datagram + QCborStreamReader reader(datagram); + if (reader.lastError() != QCborError::NoError || !reader.isArray()) + continue; + if (!reader.isLengthKnown() || reader.length() != 2) + continue; + + reader.enterContainer(); + if (reader.lastError() != QCborError::NoError || !reader.isString()) + continue; + while (reader.readString().status == QCborStreamReader::Ok) { + // we don't actually need the username right now + } + + if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger()) + continue; + senderServerPort = reader.toInteger(); + } - int senderServerPort = list.at(1).toInt(); if (isLocalHostAddress(senderIp) && senderServerPort == serverPort) continue; diff --git a/examples/network/network-chat/peermanager.h b/examples/network/network-chat/peermanager.h index 0bcd67579c..b79028235b 100644 --- a/examples/network/network-chat/peermanager.h +++ b/examples/network/network-chat/peermanager.h @@ -68,7 +68,7 @@ public: PeerManager(Client *client); void setServerPort(int port); - QByteArray userName() const; + QString userName() const; void startBroadcasting(); bool isLocalHostAddress(const QHostAddress &address); @@ -87,7 +87,7 @@ private: QList<QHostAddress> ipAddresses; QUdpSocket broadcastSocket; QTimer broadcastTimer; - QByteArray username; + QString username; int serverPort; }; diff --git a/examples/network/network-chat/server.cpp b/examples/network/network-chat/server.cpp index cc154728d5..b3e4a07f60 100644 --- a/examples/network/network-chat/server.cpp +++ b/examples/network/network-chat/server.cpp @@ -61,7 +61,6 @@ Server::Server(QObject *parent) void Server::incomingConnection(qintptr socketDescriptor) { - Connection *connection = new Connection(this); - connection->setSocketDescriptor(socketDescriptor); + Connection *connection = new Connection(socketDescriptor, this); emit newConnection(connection); } diff --git a/examples/network/network.pro b/examples/network/network.pro index d64b16c760..93049014eb 100644 --- a/examples/network/network.pro +++ b/examples/network/network.pro @@ -29,7 +29,12 @@ qtHaveModule(widgets) { } - qtConfig(openssl): SUBDIRS += securesocketclient + qtConfig(openssl) { + SUBDIRS += \ + securesocketclient \ + secureudpserver \ + secureudpclient + } qtConfig(sctp): SUBDIRS += multistreamserver multistreamclient } diff --git a/examples/network/secureudpclient/addressdialog.cpp b/examples/network/secureudpclient/addressdialog.cpp new file mode 100644 index 0000000000..ccb58c853c --- /dev/null +++ b/examples/network/secureudpclient/addressdialog.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "addressdialog.h" +#include "ui_addressdialog.h" + +#include <QtCore> +#include <QtNetwork> +#include <QtWidgets> + +#include <limits> + +AddressDialog::AddressDialog(QWidget *parent) + : QDialog(parent), + ui(new Ui::AddressDialog) +{ + ui->setupUi(this); + setupHostSelector(); + setupPortSelector(); +} + +AddressDialog::~AddressDialog() +{ + delete ui; +} + +QString AddressDialog::remoteName() const +{ + if (ui->addressSelector->count()) + return ui->addressSelector->currentText(); + return {}; +} + +quint16 AddressDialog::remotePort() const +{ + return quint16(ui->portSelector->text().toUInt()); +} + +void AddressDialog::setupHostSelector() +{ + QString name(QHostInfo::localHostName()); + if (!name.isEmpty()) { + ui->addressSelector->addItem(name); + const QString domain = QHostInfo::localDomainName(); + if (!domain.isEmpty()) + ui->addressSelector->addItem(name + QChar('.') + domain); + } + + if (name != QStringLiteral("localhost")) + ui->addressSelector->addItem(QStringLiteral("localhost")); + + const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); + for (const QHostAddress &ipAddress : ipAddressesList) { + if (!ipAddress.isLoopback()) + ui->addressSelector->addItem(ipAddress.toString()); + } + + ui->addressSelector->insertSeparator(ui->addressSelector->count()); + + for (const QHostAddress &ipAddress : ipAddressesList) { + if (ipAddress.isLoopback()) + ui->addressSelector->addItem(ipAddress.toString()); + } +} + +void AddressDialog::setupPortSelector() +{ + ui->portSelector->setValidator(new QIntValidator(0, std::numeric_limits<quint16>::max(), + ui->portSelector)); + ui->portSelector->setText(QStringLiteral("22334")); +} diff --git a/examples/network/secureudpclient/addressdialog.h b/examples/network/secureudpclient/addressdialog.h new file mode 100644 index 0000000000..7c5e2e03e8 --- /dev/null +++ b/examples/network/secureudpclient/addressdialog.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ADDRESSDIALOG_H +#define ADDRESSDIALOG_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE + +namespace Ui { + +class AddressDialog; + +} + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class AddressDialog : public QDialog +{ + Q_OBJECT + +public: + + explicit AddressDialog(QWidget *parent = nullptr); + ~AddressDialog(); + + QString remoteName() const; + quint16 remotePort() const; + +private: + + void setupHostSelector(); + void setupPortSelector(); + + Ui::AddressDialog *ui = nullptr; +}; + +#endif // ADDRESSDIALOG_H diff --git a/examples/network/secureudpclient/addressdialog.ui b/examples/network/secureudpclient/addressdialog.ui new file mode 100644 index 0000000000..a7d9bdc253 --- /dev/null +++ b/examples/network/secureudpclient/addressdialog.ui @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AddressDialog</class> + <widget class="QDialog" name="AddressDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>548</width> + <height>143</height> + </rect> + </property> + <property name="windowTitle"> + <string>Host info</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Host name (server's address):</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="addressSelector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>320</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>320</width> + <height>16777215</height> + </size> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="frame"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Server port:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="portSelector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>320</width> + <height>0</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>320</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddressDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddressDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/secureudpclient/association.cpp b/examples/network/secureudpclient/association.cpp new file mode 100644 index 0000000000..11f52ba96c --- /dev/null +++ b/examples/network/secureudpclient/association.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "association.h" + +QT_BEGIN_NAMESPACE + +DtlsAssociation::DtlsAssociation(const QHostAddress &address, quint16 port, + const QString &connectionName) + : name(connectionName), + crypto(QSslSocket::SslClientMode) +{ + auto configuration = QSslConfiguration::defaultDtlsConfiguration(); + configuration.setPeerVerifyMode(QSslSocket::VerifyNone); + crypto.setRemote(address, port); + crypto.setDtlsConfiguration(configuration); + + connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout); + connect(&crypto, &QDtls::pskRequired, this, &DtlsAssociation::pskRequired); + + connect(&socket, &QUdpSocket::readyRead, this, &DtlsAssociation::readyRead); + + pingTimer.setInterval(5000); + connect(&pingTimer, &QTimer::timeout, this, &DtlsAssociation::pingTimeout); +} + +DtlsAssociation::~DtlsAssociation() +{ + if (crypto.connectionEncrypted()) + crypto.sendShutdownAlert(&socket); +} + +void DtlsAssociation::startHandshake() +{ + if (!crypto.doHandshake(&socket, {})) + emit errorMessage(name + tr(": failed to start a handshake - ") + crypto.dtlsErrorString()); + else + emit infoMessage(name + tr(": starting a handshake")); +} + +void DtlsAssociation::readyRead() +{ + QByteArray dgram(socket.pendingDatagramSize(), '\0'); + const qint64 bytesRead = socket.readDatagram(dgram.data(), dgram.size()); + if (bytesRead <= 0) { + emit warningMessage(name + tr(": spurious read notification?")); + return; + } + + dgram.resize(bytesRead); + if (crypto.connectionEncrypted()) { + const QByteArray plainText = crypto.decryptDatagram(&socket, dgram); + if (plainText.size()) { + emit serverResponse(name, dgram, plainText); + pingTimer.start(); + return; + } + + if (crypto.dtlsError() == QDtlsError::RemoteClosedConnectionError) { + emit errorMessage(name + tr(": shutdown alert received")); + socket.close(); + pingTimer.stop(); + return; + } + + emit warningMessage(name + tr(": zero-length datagram received?")); + } else { + if (!crypto.doHandshake(&socket, dgram)) { + emit errorMessage(name + tr(": handshake error - ") + crypto.dtlsErrorString()); + return; + } + if (crypto.connectionEncrypted()) { + emit infoMessage(name + tr(": encrypted connection established!")); + pingTimer.start(); + pingTimeout(); + } else { + emit infoMessage(name + tr(": continuing with handshake ...")); + } + } +} + +void DtlsAssociation::handshakeTimeout() +{ + emit warningMessage(name + tr(": handshake timeout, trying to re-transmit")); + if (!crypto.handleTimeout(&socket)) + emit errorMessage(name + tr(": failed to re-transmit - ") + crypto.dtlsErrorString()); +} + +void DtlsAssociation::pskRequired(QSslPreSharedKeyAuthenticator *auth) +{ + Q_ASSERT(auth); + + emit infoMessage(name + tr(": providing pre-shared key ...")); + auth->setIdentity(name.toLatin1()); + auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f")); +} + +void DtlsAssociation::pingTimeout() +{ + static const QString message = QStringLiteral("I am %1, please, accept our ping %2"); + const qint64 written = crypto.writeDatagramEncrypted(&socket, message.arg(name).arg(ping).toLatin1()); + if (written <= 0) { + emit errorMessage(name + tr(": failed to send a ping - ") + crypto.dtlsErrorString()); + pingTimer.stop(); + return; + } + + ++ping; +} + +QT_END_NAMESPACE diff --git a/examples/network/secureudpclient/association.h b/examples/network/secureudpclient/association.h new file mode 100644 index 0000000000..08fc28e3ed --- /dev/null +++ b/examples/network/secureudpclient/association.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ASSOCIATION_H +#define ASSOCIATION_H + +#include <QtNetwork> +#include <QtCore> + +QT_BEGIN_NAMESPACE + +class DtlsAssociation : public QObject +{ + Q_OBJECT + +public: + + DtlsAssociation(const QHostAddress &address, quint16 port, + const QString &connectionName); + ~DtlsAssociation(); + void startHandshake(); + +signals: + + void errorMessage(const QString &message); + void warningMessage(const QString &message); + void infoMessage(const QString &message); + void serverResponse(const QString &clientInfo, const QByteArray &datagraam, + const QByteArray &plainText); + +private slots: + + void readyRead(); + void handshakeTimeout(); + void pskRequired(QSslPreSharedKeyAuthenticator *auth); + void pingTimeout(); + +private: + + QString name; + QUdpSocket socket; + QDtls crypto; + + QTimer pingTimer; + unsigned ping = 0; + + Q_DISABLE_COPY(DtlsAssociation) +}; + +QT_END_NAMESPACE + +#endif // ASSOCIATION_H diff --git a/examples/network/secureudpclient/main.cpp b/examples/network/secureudpclient/main.cpp new file mode 100644 index 0000000000..2cf35878f2 --- /dev/null +++ b/examples/network/secureudpclient/main.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QT_USE_NAMESPACE + + QApplication app(argc, argv); + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/examples/network/secureudpclient/mainwindow.cpp b/examples/network/secureudpclient/mainwindow.cpp new file mode 100644 index 0000000000..07c614cf3a --- /dev/null +++ b/examples/network/secureudpclient/mainwindow.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtNetwork> + +#include "addressdialog.h" +#include "association.h" +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include <utility> + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), + ui(new Ui::MainWindow), + nameTemplate(QStringLiteral("Alice (clone number %1)")) +{ + ui->setupUi(this); + updateUi(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>")); + +void MainWindow::addErrorMessage(const QString &message) +{ + ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message)); +} + +void MainWindow::addWarningMessage(const QString &message) +{ + ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message)); +} + +void MainWindow::addInfoMessage(const QString &message) +{ + ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message)); +} + +void MainWindow::addServerResponse(const QString &clientInfo, const QByteArray &datagram, + const QByteArray &plainText) +{ + static const QString messageColor = QStringLiteral("DarkMagenta"); + static const QString formatter = QStringLiteral("<br>---------------" + "<br>%1 received a DTLS datagram:<br> %2" + "<br>As plain text:<br> %3"); + + const QString html = formatter.arg(clientInfo, QString::fromUtf8(datagram.toHex(' ')), + QString::fromUtf8(plainText)); + ui->serverMessages->insertHtml(colorizer.arg(messageColor, html)); +} + +void MainWindow::on_connectButton_clicked() +{ + if (lookupId != -1) { + QHostInfo::abortHostLookup(lookupId); + lookupId = -1; + port = 0; + updateUi(); + return; + } + + AddressDialog dialog; + if (dialog.exec() != QDialog::Accepted) + return; + + const QString hostName = dialog.remoteName(); + if (hostName.isEmpty()) + return addWarningMessage(tr("Host name or address required to connect")); + + port = dialog.remotePort(); + QHostAddress remoteAddress; + if (remoteAddress.setAddress(hostName)) + return startNewConnection(remoteAddress); + + addInfoMessage(tr("Looking up the host ...")); + lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo))); + updateUi(); +} + +void MainWindow::updateUi() +{ + ui->connectButton->setText(lookupId == -1 ? tr("Connect ...") : tr("Cancel lookup")); + ui->shutdownButton->setEnabled(connections.size() != 0); +} + +void MainWindow::lookupFinished(const QHostInfo &hostInfo) +{ + if (hostInfo.lookupId() != lookupId) + return; + + lookupId = -1; + updateUi(); + + if (hostInfo.error() != QHostInfo::NoError) { + addErrorMessage(hostInfo.errorString()); + return; + } + + const QList<QHostAddress> foundAddresses = hostInfo.addresses(); + if (foundAddresses.empty()) { + addWarningMessage(tr("Host not found")); + return; + } + + const auto remoteAddress = foundAddresses.at(0); + addInfoMessage(tr("Connecting to: %1").arg(remoteAddress.toString())); + startNewConnection(remoteAddress); +} + +void MainWindow::startNewConnection(const QHostAddress &address) +{ + AssocPtr newConnection(new DtlsAssociation(address, port, nameTemplate.arg(nextId))); + connect(newConnection.data(), &DtlsAssociation::errorMessage, this, &MainWindow::addErrorMessage); + connect(newConnection.data(), &DtlsAssociation::warningMessage, this, &MainWindow::addWarningMessage); + connect(newConnection.data(), &DtlsAssociation::infoMessage, this, &MainWindow::addInfoMessage); + connect(newConnection.data(), &DtlsAssociation::serverResponse, this, &MainWindow::addServerResponse); + connections.push_back(std::move(newConnection)); + connections.back()->startHandshake(); + updateUi(); + + ++nextId; +} + +void MainWindow::on_shutdownButton_clicked() +{ + connections.clear(); + updateUi(); +} diff --git a/examples/network/secureudpclient/mainwindow.h b/examples/network/secureudpclient/mainwindow.h new file mode 100644 index 0000000000..b231b44627 --- /dev/null +++ b/examples/network/secureudpclient/mainwindow.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QSharedPointer> +#include <QVector> + +QT_BEGIN_NAMESPACE + +namespace Ui { + +class MainWindow; + +} + +class QHostAddress; +class QHostInfo; + +class DtlsAssociation; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + + void addErrorMessage(const QString &message); + void addWarningMessage(const QString &message); + void addInfoMessage(const QString &message); + void addServerResponse(const QString &clientInfo, const QByteArray &datagram, + const QByteArray &plainText); + + void on_connectButton_clicked(); + void on_shutdownButton_clicked(); + + void lookupFinished(const QHostInfo &hostInfo); + +private: + + void updateUi(); + void startNewConnection(const QHostAddress &address); + + Ui::MainWindow *ui = nullptr; + + using AssocPtr = QSharedPointer<DtlsAssociation>; + QVector<AssocPtr> connections; + + QString nameTemplate; + unsigned nextId = 0; + + quint16 port = 0; + int lookupId = -1; +}; + +#endif // MAINWINDOW_H diff --git a/examples/network/secureudpclient/mainwindow.ui b/examples/network/secureudpclient/mainwindow.ui new file mode 100644 index 0000000000..59a31974ee --- /dev/null +++ b/examples/network/secureudpclient/mainwindow.ui @@ -0,0 +1,198 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1200</width> + <height>550</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>1200</width> + <height>550</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>1200</width> + <height>550</height> + </size> + </property> + <property name="windowTitle"> + <string>DTLS client</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>590</width> + <height>400</height> + </size> + </property> + <property name="title"> + <string>DTLS info messages:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <widget class="QTextEdit" name="clientMessages"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>570</width> + <height>360</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>570</width> + <height>360</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>570</width> + <height>360</height> + </size> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="connectButton"> + <property name="text"> + <string>Connect ...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="shutdownButton"> + <property name="text"> + <string>Shutdown connections</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="minimumSize"> + <size> + <width>580</width> + <height>490</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>580</width> + <height>490</height> + </size> + </property> + <property name="title"> + <string>Received datagrams:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <widget class="QTextEdit" name="serverMessages"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>560</width> + <height>450</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>560</width> + <height>450</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>560</width> + <height>450</height> + </size> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1200</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> diff --git a/examples/network/secureudpclient/secureudpclient.pro b/examples/network/secureudpclient/secureudpclient.pro new file mode 100644 index 0000000000..44e4200994 --- /dev/null +++ b/examples/network/secureudpclient/secureudpclient.pro @@ -0,0 +1,22 @@ +QT += widgets network + +TARGET = secureudpclient +TEMPLATE = app + +SOURCES += \ + main.cpp \ + association.cpp \ + mainwindow.cpp \ + addressdialog.cpp + +HEADERS += \ + association.h \ + mainwindow.h \ + addressdialog.h + +FORMS += \ + mainwindow.ui \ + addressdialog.ui + +target.path = $$[QT_INSTALL_EXAMPLES]/network/secureudpclient +INSTALLS += target diff --git a/examples/network/secureudpserver/main.cpp b/examples/network/secureudpserver/main.cpp new file mode 100644 index 0000000000..1a29d9d7ec --- /dev/null +++ b/examples/network/secureudpserver/main.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QT_USE_NAMESPACE + + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/examples/network/secureudpserver/mainwindow.cpp b/examples/network/secureudpserver/mainwindow.cpp new file mode 100644 index 0000000000..d751ec931d --- /dev/null +++ b/examples/network/secureudpserver/mainwindow.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "nicselector.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow() + : ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + connect(&server, &DtlsServer::errorMessage, this, &MainWindow::addErrorMessage); + connect(&server, &DtlsServer::warningMessage, this, &MainWindow::addWarningMessage); + connect(&server, &DtlsServer::infoMessage, this, &MainWindow::addInfoMessage); + connect(&server, &DtlsServer::datagramReceived, this, &MainWindow::addClientMessage); + + updateUi(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_startButton_clicked() +{ + if (!server.isListening()) { + NicSelector ipDialog; + if (ipDialog.exec() == QDialog::Accepted) { + const QHostAddress address = ipDialog.selectedIp(); + const quint16 port = ipDialog.selectedPort(); + if (address.isNull()) { + addErrorMessage(tr("Failed to start listening, no valid address/port")); + } else if (server.listen(address, port)) { + addInfoMessage(tr("Server is listening on address %1 and port %2") + .arg(address.toString()) + .arg(port)); + } + } + } else { + server.close(); + addInfoMessage(tr("Server is not accepting new connections")); + } + + updateUi(); +} + +void MainWindow::on_quitButton_clicked() +{ + QCoreApplication::exit(0); +} + +void MainWindow::updateUi() +{ + server.isListening() ? ui->startButton->setText(tr("Stop listening")) + : ui->startButton->setText(tr("Start listening")); +} + +const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>")); + +void MainWindow::addErrorMessage(const QString &message) +{ + ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message)); +} + +void MainWindow::addWarningMessage(const QString &message) +{ + ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message)); +} + +void MainWindow::addInfoMessage(const QString &message) +{ + ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message)); +} + +void MainWindow::addClientMessage(const QString &peerInfo, const QByteArray &datagram, + const QByteArray &plainText) +{ + static const QString messageColor = QStringLiteral("DarkMagenta"); + static const QString formatter = QStringLiteral("<br>---------------" + "<br>A message from %1" + "<br>DTLS datagram:<br> %2" + "<br>As plain text:<br> %3"); + + const QString html = formatter.arg(peerInfo, QString::fromUtf8(datagram.toHex(' ')), + QString::fromUtf8(plainText)); + ui->messages->insertHtml(colorizer.arg(messageColor, html)); +} diff --git a/examples/network/secureudpserver/mainwindow.h b/examples/network/secureudpserver/mainwindow.h new file mode 100644 index 0000000000..0c914f5021 --- /dev/null +++ b/examples/network/secureudpserver/mainwindow.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "server.h" + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE + +namespace Ui { +class MainWindow; +} + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + + MainWindow(); + ~MainWindow(); + +private slots: + + void addErrorMessage(const QString &message); + void addWarningMessage(const QString &message); + void addInfoMessage(const QString &message); + void addClientMessage(const QString &peerInfo, const QByteArray &datagram, + const QByteArray &plainText); + + void on_startButton_clicked(); + void on_quitButton_clicked(); + +private: + + void updateUi(); + + Ui::MainWindow *ui = nullptr; + DtlsServer server; +}; + +#endif // MAINWINDOW_H diff --git a/examples/network/secureudpserver/mainwindow.ui b/examples/network/secureudpserver/mainwindow.ui new file mode 100644 index 0000000000..e25e19921b --- /dev/null +++ b/examples/network/secureudpserver/mainwindow.ui @@ -0,0 +1,207 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1090</width> + <height>670</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>1090</width> + <height>670</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>1090</width> + <height>670</height> + </size> + </property> + <property name="windowTitle"> + <string>DTLS server</string> + </property> + <widget class="QWidget" name="centralWidget"> + <widget class="QWidget" name="layoutWidget"> + <property name="geometry"> + <rect> + <x>20</x> + <y>20</y> + <width>1050</width> + <height>576</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QGroupBox" name="infoBox"> + <property name="minimumSize"> + <size> + <width>520</width> + <height>540</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>520</width> + <height>540</height> + </size> + </property> + <property name="title"> + <string>Dtls server info:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <widget class="QTextEdit" name="serverInfo"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>500</width> + <height>500</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>500</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>500</width> + <height>500</height> + </size> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="minimumSize"> + <size> + <width>520</width> + <height>540</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>520</width> + <height>540</height> + </size> + </property> + <property name="title"> + <string>Received messages:</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + <widget class="QTextEdit" name="messages"> + <property name="geometry"> + <rect> + <x>10</x> + <y>30</y> + <width>500</width> + <height>500</height> + </rect> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>500</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>500</width> + <height>500</height> + </size> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="startButton"> + <property name="text"> + <string>Start listening</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="quitButton"> + <property name="text"> + <string>Quit</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1090</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QToolBar" name="mainToolBar"> + <attribute name="toolBarArea"> + <enum>TopToolBarArea</enum> + </attribute> + <attribute name="toolBarBreak"> + <bool>false</bool> + </attribute> + </widget> + <widget class="QStatusBar" name="statusBar"/> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections/> +</ui> diff --git a/examples/network/secureudpserver/nicselector.cpp b/examples/network/secureudpserver/nicselector.cpp new file mode 100644 index 0000000000..ea26439d74 --- /dev/null +++ b/examples/network/secureudpserver/nicselector.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <limits> + +#include <QtCore> +#include <QtNetwork> + +#include "nicselector.h" +#include "ui_nicselector.h" + +NicSelector::NicSelector(QWidget *parent) : + QDialog(parent), + ui(new Ui::NicSelector) +{ + ui->setupUi(this); + auto portValidator = new QIntValidator(0, int(std::numeric_limits<quint16>::max()), + ui->portSelector); + ui->portSelector->setValidator(portValidator); + ui->portSelector->setText(QStringLiteral("22334")); + + const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); + availableAddresses.reserve(ipAddressesList.size()); + for (const QHostAddress &ip : ipAddressesList) { + if (ip != QHostAddress::LocalHost && ip.toIPv4Address()) { + availableAddresses.push_back(ip); + ui->ipSelector->addItem(ip.toString()); + } + } +} + +NicSelector::~NicSelector() +{ + delete ui; +} + +QHostAddress NicSelector::selectedIp() const +{ + if (!availableAddresses.size()) + return {}; + + return availableAddresses[ui->ipSelector->currentIndex()]; +} + +quint16 NicSelector::selectedPort() const +{ + return quint16(ui->portSelector->text().toUInt()); +} diff --git a/examples/network/secureudpserver/nicselector.h b/examples/network/secureudpserver/nicselector.h new file mode 100644 index 0000000000..7962a78318 --- /dev/null +++ b/examples/network/secureudpserver/nicselector.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NICSELECTOR_H +#define NICSELECTOR_H + +#include <QDialog> +#include <QHostAddress> +#include <QVector> + +QT_BEGIN_NAMESPACE + +namespace Ui { +class NicSelector; +} + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class NicSelector : public QDialog +{ + Q_OBJECT + +public: + explicit NicSelector(QWidget *parent = nullptr); + ~NicSelector(); + + QHostAddress selectedIp() const; + quint16 selectedPort() const; + +private: + Ui::NicSelector *ui = nullptr; + QVector<QHostAddress> availableAddresses; +}; + +#endif // NICSELECTOR_H diff --git a/examples/network/secureudpserver/nicselector.ui b/examples/network/secureudpserver/nicselector.ui new file mode 100644 index 0000000000..b0ba376b66 --- /dev/null +++ b/examples/network/secureudpserver/nicselector.ui @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>NicSelector</class> + <widget class="QDialog" name="NicSelector"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>373</width> + <height>213</height> + </rect> + </property> + <property name="windowTitle"> + <string>IP and port</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetFixedSize</enum> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Listen on address:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="ipSelector"> + <property name="minimumSize"> + <size> + <width>250</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetFixedSize</enum> + </property> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Port:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="portSelector"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>NicSelector</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>NicSelector</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/examples/network/secureudpserver/secureudpserver.pro b/examples/network/secureudpserver/secureudpserver.pro new file mode 100644 index 0000000000..910655aae4 --- /dev/null +++ b/examples/network/secureudpserver/secureudpserver.pro @@ -0,0 +1,21 @@ +QT += widgets network + +TARGET = secureudpserver +TEMPLATE = app + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + server.cpp \ + nicselector.cpp + +HEADERS += \ + mainwindow.h \ + server.h \ + nicselector.h + +FORMS = mainwindow.ui \ + nicselector.ui + +target.path = $$[QT_INSTALL_EXAMPLES]/network/secureudpserver +INSTALLS += target diff --git a/examples/network/secureudpserver/server.cpp b/examples/network/secureudpserver/server.cpp new file mode 100644 index 0000000000..918307013c --- /dev/null +++ b/examples/network/secureudpserver/server.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "server.h" + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace { + +QString peer_info(const QHostAddress &address, quint16 port) +{ + const static QString info = QStringLiteral("(%1:%2)"); + return info.arg(address.toString()).arg(port); +} + +QString connection_info(QSharedPointer<QDtls> connection) +{ + QString info(DtlsServer::tr("Session cipher: ")); + info += connection->sessionCipher().name(); + + info += DtlsServer::tr("; session protocol: "); + switch (connection->sessionProtocol()) { + case QSsl::DtlsV1_0: + info += DtlsServer::tr("DTLS 1.0."); + break; + case QSsl::DtlsV1_2: + info += DtlsServer::tr("DTLS 1.2."); + break; + case QSsl::DtlsV1_2OrLater: + info += DtlsServer::tr("DTLS 1.2 or later."); + break; + default: + info += DtlsServer::tr("Unknown protocol."); + } + + return info; +} + +} // unnamed namespace + +DtlsServer::DtlsServer() +{ + connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead); + + serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server"); + serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); +} + +DtlsServer::~DtlsServer() +{ + shutdown(); +} + +bool DtlsServer::listen(const QHostAddress &address, quint16 port) +{ + if (address != serverSocket.localAddress() || port != serverSocket.localPort()) { + shutdown(); + listening = serverSocket.bind(address, port); + if (!listening) + emit errorMessage(serverSocket.errorString()); + } else { + listening = true; + } + + return listening; +} + +bool DtlsServer::isListening() const +{ + return listening; +} + +void DtlsServer::close() +{ + listening = false; +} + +void DtlsServer::readyRead() +{ + const qint64 bytesToRead = serverSocket.pendingDatagramSize(); + if (bytesToRead <= 0) { + emit warningMessage(tr("A spurious read notification")); + return; + } + + QByteArray dgram(bytesToRead, Qt::Uninitialized); + QHostAddress peerAddress; + quint16 peerPort = 0; + const qint64 bytesRead = serverSocket.readDatagram(dgram.data(), dgram.size(), + &peerAddress, &peerPort); + if (bytesRead <= 0) { + emit warningMessage(tr("Failed to read a datagram: ") + serverSocket.errorString()); + return; + } + + dgram.resize(bytesRead); + + if (peerAddress.isNull() || !peerPort) { + emit warningMessage(tr("Failed to extract peer info (address, port)")); + return; + } + + const auto client = std::find_if(knownClients.begin(), knownClients.end(), + [&](const DtlsConnection &connection){ + return connection->remoteAddress() == peerAddress + && connection->remotePort() == peerPort; + }); + + if (client == knownClients.end()) + return handleNewConnection(peerAddress, peerPort, dgram); + + if ((*client)->connectionEncrypted()) { + decryptDatagram(*client, dgram); + if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError) + knownClients.erase(client); + return; + } + + doHandshake(*client, dgram); +} + +void DtlsServer::pskRequired(QSslPreSharedKeyAuthenticator *auth) +{ + Q_ASSERT(auth); + + emit infoMessage(tr("PSK callback, received a client's identity: '%1'") + .arg(QString::fromLatin1(auth->identity()))); + auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f")); +} + +void DtlsServer::handleNewConnection(const QHostAddress &peerAddress, + quint16 peerPort, const QByteArray &clientHello) +{ + if (!listening) + return; + + const QString peerInfo = peer_info(peerAddress, peerPort); + if (cookieSender.verifyClient(&serverSocket, clientHello, peerAddress, peerPort)) { + emit infoMessage(peerInfo + tr(": verified, starting a handshake")); + + DtlsConnection newConnection(new QDtls(QSslSocket::SslServerMode)); + newConnection->setDtlsConfiguration(serverConfiguration); + newConnection->setRemote(peerAddress, peerPort); + newConnection->connect(newConnection.data(), &QDtls::pskRequired, + this, &DtlsServer::pskRequired); + knownClients.push_back(newConnection); + doHandshake(newConnection, clientHello); + } else if (cookieSender.dtlsError() != QDtlsError::NoError) { + emit errorMessage(tr("DTLS error: ") + cookieSender.dtlsErrorString()); + } else { + emit infoMessage(peerInfo + tr(": not verified yet")); + } +} + +void DtlsServer::doHandshake(DtlsConnection newConnection, const QByteArray &clientHello) +{ + const bool result = newConnection->doHandshake(&serverSocket, clientHello); + if (!result) { + emit errorMessage(newConnection->dtlsErrorString()); + return; + } + + const QString peerInfo = peer_info(newConnection->remoteAddress(), + newConnection->remotePort()); + switch (newConnection->handshakeState()) { + case QDtls::HandshakeInProgress: + emit infoMessage(peerInfo + tr(": handshake is in progress ...")); + break; + case QDtls::HandshakeComplete: + emit infoMessage(tr("Connection with %1 encrypted. %2") + .arg(peerInfo, connection_info(newConnection))); + break; + default: + Q_UNREACHABLE(); + } +} + +void DtlsServer::decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage) +{ + Q_ASSERT(connection->connectionEncrypted()); + + const QString peerInfo = peer_info(connection->remoteAddress(), connection->remotePort()); + const QByteArray dgram = connection->decryptDatagram(&serverSocket, clientMessage); + if (dgram.size()) { + emit datagramReceived(peerInfo, clientMessage, dgram); + connection->writeDatagramEncrypted(&serverSocket, tr("to %1: ACK").arg(peerInfo).toLatin1()); + } else if (connection->dtlsError() == QDtlsError::NoError) { + emit warningMessage(peerInfo + ": " + tr("0 byte dgram, could be a re-connect attempt?")); + } else { + emit errorMessage(peerInfo + ": " + connection->dtlsErrorString()); + } +} + +void DtlsServer::shutdown() +{ + for (DtlsConnection &connection : knownClients) + connection->sendShutdownAlert(&serverSocket); + + knownClients.clear(); + serverSocket.close(); +} + +QT_END_NAMESPACE diff --git a/examples/network/secureudpserver/server.h b/examples/network/secureudpserver/server.h new file mode 100644 index 0000000000..33444f7407 --- /dev/null +++ b/examples/network/secureudpserver/server.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef SERVER_H +#define SERVER_H + +#include <QtCore> +#include <QtNetwork> + +#include <vector> + +QT_BEGIN_NAMESPACE + +class DtlsServer : public QObject +{ + Q_OBJECT + +public: + + DtlsServer(); + ~DtlsServer(); + + bool listen(const QHostAddress &address, quint16 port); + bool isListening() const; + void close(); + +signals: + + void errorMessage(const QString &message); + void warningMessage(const QString &message); + void infoMessage(const QString &message); + + void datagramReceived(const QString &peerInfo, const QByteArray &cipherText, + const QByteArray &plainText); + +private slots: + + void readyRead(); + void pskRequired(QSslPreSharedKeyAuthenticator *auth); + +private: + + void handleNewConnection(const QHostAddress &peerAddress, quint16 peerPort, + const QByteArray &clientHello); + + using DtlsConnection = QSharedPointer<QDtls>; + void doHandshake(DtlsConnection newConnection, const QByteArray &clientHello); + void decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage); + void shutdown(); + + bool listening = false; + QUdpSocket serverSocket; + + QSslConfiguration serverConfiguration; + QDtlsClientVerifier cookieSender; + QVector<DtlsConnection> knownClients; + + Q_DISABLE_COPY(DtlsServer) +}; + +QT_END_NAMESPACE + +#endif // SERVER_H |