From ed775e794c72b237d148fd7d3ea248c803c30075 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 15 Mar 2018 14:26:26 +0100 Subject: [PATCH] Add a secure UDP server example It's a simple DTLS server, implemented with QUdpSocket, QDtlsClientVerifier and QDtls. The server is configured to use PSK only (it has no certificate/key). The server uses a single QUdpSocket socket and de-multiplexes UDP datagrams internally (thus it can work with several clients simultaneously). Future update will probably add more options (like configuring with certificate/key, etc). For now - it's as minimalistic and simple as possible. Task-number: QTBUG-67596 Change-Id: Ic7d18dbab6dbcc9ed44c82e69a2b364df24aa256 Reviewed-by: Timur Pocheptsov --- examples/network/network.pro | 6 +- examples/network/secureudpserver/main.cpp | 64 +++++ .../network/secureudpserver/mainwindow.cpp | 136 ++++++++++ examples/network/secureudpserver/mainwindow.h | 95 +++++++ .../network/secureudpserver/mainwindow.ui | 207 ++++++++++++++ .../network/secureudpserver/nicselector.cpp | 95 +++++++ .../network/secureudpserver/nicselector.h | 84 ++++++ .../network/secureudpserver/nicselector.ui | 144 ++++++++++ .../secureudpserver/secureudpserver.pro | 21 ++ examples/network/secureudpserver/server.cpp | 252 ++++++++++++++++++ examples/network/secureudpserver/server.h | 109 ++++++++ 11 files changed, 1212 insertions(+), 1 deletion(-) create mode 100644 examples/network/secureudpserver/main.cpp create mode 100644 examples/network/secureudpserver/mainwindow.cpp create mode 100644 examples/network/secureudpserver/mainwindow.h create mode 100644 examples/network/secureudpserver/mainwindow.ui create mode 100644 examples/network/secureudpserver/nicselector.cpp create mode 100644 examples/network/secureudpserver/nicselector.h create mode 100644 examples/network/secureudpserver/nicselector.ui create mode 100644 examples/network/secureudpserver/secureudpserver.pro create mode 100644 examples/network/secureudpserver/server.cpp create mode 100644 examples/network/secureudpserver/server.h diff --git a/examples/network/network.pro b/examples/network/network.pro index d64b16c760..3187c1d43e 100644 --- a/examples/network/network.pro +++ b/examples/network/network.pro @@ -29,7 +29,11 @@ qtHaveModule(widgets) { } - qtConfig(openssl): SUBDIRS += securesocketclient + qtConfig(openssl) { + SUBDIRS += \ + securesocketclient \ + secureudpserver + } qtConfig(sctp): SUBDIRS += multistreamserver multistreamclient } 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 + +#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("%2
")); + +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("
---------------" + "
A message from %1" + "
DTLS datagram:
%2" + "
As plain text:
%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 + +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 @@ + + + MainWindow + + + + 0 + 0 + 1090 + 670 + + + + + 1090 + 670 + + + + + 1090 + 670 + + + + DTLS server + + + + + + 20 + 20 + 1050 + 576 + + + + + + + + + + 520 + 540 + + + + + 520 + 540 + + + + Dtls server info: + + + true + + + + + 10 + 30 + 500 + 500 + + + + + 500 + 500 + + + + + 500 + 500 + + + + true + + + + + + + + + 520 + 540 + + + + + 520 + 540 + + + + Received messages: + + + true + + + + + 10 + 30 + 500 + 500 + + + + + 500 + 500 + + + + + 500 + 500 + + + + true + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start listening + + + + + + + Quit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + 1090 + 22 + + + + + + TopToolBarArea + + + false + + + + + + + + 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 + +#include +#include + +#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::max()), + ui->portSelector); + ui->portSelector->setValidator(portValidator); + ui->portSelector->setText(QStringLiteral("22334")); + + const QList 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 +#include +#include + +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 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 @@ + + + NicSelector + + + + 0 + 0 + 373 + 213 + + + + IP and port + + + + + + QLayout::SetFixedSize + + + + + Listen on address: + + + + + + + + 250 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + QLayout::SetFixedSize + + + + + Port: + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NicSelector + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NicSelector + reject() + + + 316 + 260 + + + 286 + 274 + + + + + 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 + +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 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 +#include + +#include + +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; + 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 knownClients; + + Q_DISABLE_COPY(DtlsServer) +}; + +QT_END_NAMESPACE + +#endif // SERVER_H