Add a secure UDP client example

A simple application that establishes DTLS connection(s) and
sends/receives datagrams. Class DtlsAssociation is
essentially a QUdpSocket|QDtls pair: it initiates a handshake,
handles timeouts and errors. After establishing an encrypted
connection it sends messages to the server and processes responses.

Task-number: QTBUG-67596
Change-Id: I92d481b7dfd2459e6a93c754b338a2e897a7feaf
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
bb10
Timur Pocheptsov 2018-03-23 12:30:31 +01:00
parent f5fe9fc5a4
commit 033a1a2793
11 changed files with 1173 additions and 1 deletions

View File

@ -32,7 +32,8 @@ qtHaveModule(widgets) {
qtConfig(openssl) {
SUBDIRS += \
securesocketclient \
secureudpserver
secureudpserver \
secureudpclient
}
qtConfig(sctp): SUBDIRS += multistreamserver multistreamclient
}

View File

@ -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"));
}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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

View File

@ -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>

View File

@ -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