diff options
Diffstat (limited to 'examples/network')
34 files changed, 2917 insertions, 207 deletions
diff --git a/examples/network/doc/images/secureudpclient-example.png b/examples/network/doc/images/secureudpclient-example.png Binary files differnew file mode 100644 index 0000000000..a566aa4ce5 --- /dev/null +++ b/examples/network/doc/images/secureudpclient-example.png diff --git a/examples/network/doc/images/secureudpserver-example.png b/examples/network/doc/images/secureudpserver-example.png Binary files differnew file mode 100644 index 0000000000..a117b02834 --- /dev/null +++ b/examples/network/doc/images/secureudpserver-example.png diff --git a/examples/network/doc/src/secureudpclient.qdoc b/examples/network/doc/src/secureudpclient.qdoc new file mode 100644 index 0000000000..dc8538cf85 --- /dev/null +++ b/examples/network/doc/src/secureudpclient.qdoc @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example secureudpclient + \title DTLS client + \ingroup examples-network + \brief This example demonstrates how to implement client-side DTLS connections. + + \image secureudpclient-example.png Screenshot of the DTLS client example. + + \note The DTLS client example is intended to be run alongside the \l{secureudpserver}{DTLS server} example. + + The example DTLS client can establish several DTLS connections to one + or many DTLS servers. A client-side DTLS connection is implemented by the + DtlsAssociation class. This class uses QUdpSocket to read and write datagrams + and QDtls for encryption: + + \snippet secureudpclient/association.h 0 + + The constructor sets the minimal TLS configuration for the new DTLS connection, + and sets the address and the port of the server: + + \dots + \snippet secureudpclient/association.cpp 1 + \dots + + The QDtls::handshakeTimeout() signal is connected to the handleTimeout() slot + to deal with packet loss and retransmission during the handshake phase: + + \dots + \snippet secureudpclient/association.cpp 2 + \dots + + To ensure we receive only the datagrams from the server, we connect our UDP socket to the server: + + \dots + \snippet secureudpclient/association.cpp 3 + \dots + + The QUdpSocket::readyRead() signal is connected to the readyRead() slot: + + \dots + \snippet secureudpclient/association.cpp 13 + \dots + + When a secure connection to a server is established, a DtlsAssociation object + will be sending short ping messages to the server, using a timer: + + \snippet secureudpclient/association.cpp 4 + + startHandshake() starts a handshake with the server: + + \snippet secureudpclient/association.cpp 5 + + The readyRead() slot reads a datagram sent by the server: + + \snippet secureudpclient/association.cpp 6 + + If the handshake was already completed, this datagram is decrypted: + + \snippet secureudpclient/association.cpp 7 + + otherwise, we try to continue the handshake: + + \snippet secureudpclient/association.cpp 8 + + When the handshake has completed, we send our first ping message: + + \snippet secureudpclient/association.cpp 9 + + The pskRequired() slot provides the Pre-Shared Key (PSK) needed during the handshake + phase: + + \snippet secureudpclient/association.cpp 14 + + \note For the sake of brevity, the definition of pskRequired() is oversimplified. + The documentation for the QSslPreSharedKeyAuthenticator class explains in detail + how this slot can be properly implemented. + + pingTimeout() sends an encrypted message to the server: + + \snippet secureudpclient/association.cpp 10 + + During the handshake phase the client must handle possible timeouts, which + can happen due to packet loss. The handshakeTimeout() slot retransmits + the handshake messages: + + \snippet secureudpclient/association.cpp 11 + + Before a client connection is destroyed, its DTLS connection must be shut down: + + \snippet secureudpclient/association.cpp 12 + + Error messages, informational messages, and decrypted responses from servers + are displayed by the UI: + + \snippet secureudpclient/mainwindow.cpp 0 +*/ + diff --git a/examples/network/doc/src/secureudpserver.qdoc b/examples/network/doc/src/secureudpserver.qdoc new file mode 100644 index 0000000000..0857f7065f --- /dev/null +++ b/examples/network/doc/src/secureudpserver.qdoc @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example secureudpserver + \title DTLS server + \ingroup examples-network + \brief This examples demonstrates how to implement a simple DTLS server. + + \image secureudpserver-example.png Screenshot of the DTLS server example. + + \note The DTLS server example is intended to be run alongside the \l{secureudpclient}{DTLS client} example. + + The server is implemented by the DtlsServer class. It uses QUdpSocket, + QDtlsClientVerifier, and QDtls to test each client's reachability, complete a handshake, + and read and write encrypted messages. + + \snippet secureudpserver/server.h 0 + + The constructor connects the QUdpSocket::readyRead() signal to its + readyRead() slot and sets the minimal needed TLS configuration: + + \snippet secureudpserver/server.cpp 1 + + \note The server is not using a certificate and is relying on Pre-Shared + Key (PSK) handshake. + + listen() binds QUdpSocket: + + \snippet secureudpserver/server.cpp 2 + + The readyRead() slot processes incoming datagrams: + + \dots + \snippet secureudpserver/server.cpp 3 + \dots + + After extracting an address and a port number, the server first tests + if it's a datagram from an already known peer: + + \dots + \snippet secureudpserver/server.cpp 4 + \dots + + If it is a new, unknown address and port, the datagram is processed as a + potential ClientHello message, sent by a DTLS client: + + \dots + \snippet secureudpserver/server.cpp 5 + \dots + + If it's a known DTLS client, the server either decrypts the datagram: + + \dots + \snippet secureudpserver/server.cpp 6 + \dots + + or continues a handshake with this peer: + + \dots + \snippet secureudpserver/server.cpp 7 + \dots + + handleNewConnection() verifies it's a reachable DTLS client, or sends a + HelloVerifyRequest: + + \snippet secureudpserver/server.cpp 8 + \dots + + If the new client was verified to be a reachable DTLS client, the server creates + and configures a new QDtls object, and starts a server-side handshake: + + \dots + \snippet secureudpserver/server.cpp 9 + \dots + + doHandshake() progresses through the handshake phase: + + \snippet secureudpserver/server.cpp 11 + + During the handshake phase, the QDtls::pskRequired() signal is emitted and + the pskRequired() slot provides the preshared key: + + \snippet secureudpserver/server.cpp 13 + + \note For the sake of brevity, the definition of pskRequired() is oversimplified. + The documentation for the QSslPreSharedKeyAuthenticator class explains in detail + how this slot can be properly implemented. + + After the handshake is completed for the network peer, an encrypted DTLS + connection is considered to be established and the server decrypts subsequent + datagrams, sent by the peer, by calling decryptDatagram(). The server also + sends an encrypted response to the peer: + + \snippet secureudpserver/server.cpp 12 + + The server closes its DTLS connections by calling QDtls::shutdown(): + + \snippet secureudpserver/server.cpp 14 + + During its operation, the server reports errors, informational messages, and + decrypted datagrams, by emitting signals errorMessage(), warningMessage(), + infoMessage(), and datagramReceived(). These messages are logged by the server's + UI: + + \snippet secureudpserver/mainwindow.cpp 0 +*/ diff --git a/examples/network/loopback/dialog.cpp b/examples/network/loopback/dialog.cpp index 99317b9c77..b4e6b0fd5e 100644 --- a/examples/network/loopback/dialog.cpp +++ b/examples/network/loopback/dialog.cpp @@ -48,11 +48,11 @@ ** ****************************************************************************/ -#include <QtWidgets> -#include <QtNetwork> - #include "dialog.h" +#include <QtNetwork> +#include <QtWidgets> + static const int TotalBytes = 50 * 1024 * 1024; static const int PayloadSize = 64 * 1024; // 64 KB @@ -71,15 +71,15 @@ Dialog::Dialog(QWidget *parent) buttonBox->addButton(startButton, QDialogButtonBox::ActionRole); buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); - connect(startButton, SIGNAL(clicked()), this, SLOT(start())); - connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); - connect(&tcpServer, SIGNAL(newConnection()), - this, SLOT(acceptConnection())); - connect(&tcpClient, SIGNAL(connected()), this, SLOT(startTransfer())); - connect(&tcpClient, SIGNAL(bytesWritten(qint64)), - this, SLOT(updateClientProgress(qint64))); - connect(&tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(displayError(QAbstractSocket::SocketError))); + connect(startButton, &QAbstractButton::clicked, this, &Dialog::start); + connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close); + connect(&tcpServer, &QTcpServer::newConnection, + this, &Dialog::acceptConnection); + connect(&tcpClient, &QAbstractSocket::connected, this, &Dialog::startTransfer); + connect(&tcpClient, &QIODevice::bytesWritten, + this, &Dialog::updateClientProgress); + connect(&tcpClient, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), + this, &Dialog::displayError); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(clientProgressBar); @@ -124,10 +124,18 @@ void Dialog::start() void Dialog::acceptConnection() { tcpServerConnection = tcpServer.nextPendingConnection(); - connect(tcpServerConnection, SIGNAL(readyRead()), - this, SLOT(updateServerProgress())); - connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(displayError(QAbstractSocket::SocketError))); + if (!tcpServerConnection) { + serverStatusLabel->setText(tr("Error: got invalid pending connection!")); + return; + } + + connect(tcpServerConnection, &QIODevice::readyRead, + this, &Dialog::updateServerProgress); + connect(tcpServerConnection, + QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), + this, &Dialog::displayError); + connect(tcpServerConnection, &QTcpSocket::disconnected, + tcpServerConnection, &QTcpSocket::deleteLater); serverStatusLabel->setText(tr("Accepted connection")); tcpServer.close(); @@ -136,13 +144,13 @@ void Dialog::acceptConnection() void Dialog::startTransfer() { // called when the TCP client connected to the loopback server - bytesToWrite = TotalBytes - (int)tcpClient.write(QByteArray(PayloadSize, '@')); + bytesToWrite = TotalBytes - int(tcpClient.write(QByteArray(PayloadSize, '@'))); clientStatusLabel->setText(tr("Connected")); } void Dialog::updateServerProgress() { - bytesReceived += (int)tcpServerConnection->bytesAvailable(); + bytesReceived += int(tcpServerConnection->bytesAvailable()); tcpServerConnection->readAll(); serverProgressBar->setMaximum(TotalBytes); @@ -161,17 +169,16 @@ void Dialog::updateServerProgress() void Dialog::updateClientProgress(qint64 numBytes) { - // callen when the TCP client has written some bytes - bytesWritten += (int)numBytes; + // called when the TCP client has written some bytes + bytesWritten += int(numBytes); // only write more if not finished and when the Qt write buffer is below a certain size. - if (bytesToWrite > 0 && tcpClient.bytesToWrite() <= 4*PayloadSize) - bytesToWrite -= (int)tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@')); + if (bytesToWrite > 0 && tcpClient.bytesToWrite() <= 4 * PayloadSize) + bytesToWrite -= tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@')); clientProgressBar->setMaximum(TotalBytes); clientProgressBar->setValue(bytesWritten); - clientStatusLabel->setText(tr("Sent %1MB") - .arg(bytesWritten / (1024 * 1024))); + clientStatusLabel->setText(tr("Sent %1MB").arg(bytesWritten / (1024 * 1024))); } void Dialog::displayError(QAbstractSocket::SocketError socketError) diff --git a/examples/network/loopback/dialog.h b/examples/network/loopback/dialog.h index b23cfa030b..a70c20550a 100644 --- a/examples/network/loopback/dialog.h +++ b/examples/network/loopback/dialog.h @@ -60,9 +60,6 @@ class QDialogButtonBox; class QLabel; class QProgressBar; class QPushButton; -class QTcpServer; -class QTcpSocket; -class QAction; QT_END_NAMESPACE class Dialog : public QDialog @@ -70,7 +67,7 @@ class Dialog : public QDialog Q_OBJECT public: - Dialog(QWidget *parent = 0); + Dialog(QWidget *parent = nullptr); public slots: void start(); @@ -81,21 +78,21 @@ public slots: void displayError(QAbstractSocket::SocketError socketError); private: - QProgressBar *clientProgressBar; - QProgressBar *serverProgressBar; - QLabel *clientStatusLabel; - QLabel *serverStatusLabel; + QProgressBar *clientProgressBar = nullptr; + QProgressBar *serverProgressBar = nullptr; + QLabel *clientStatusLabel = nullptr; + QLabel *serverStatusLabel = nullptr; - QPushButton *startButton; - QPushButton *quitButton; - QDialogButtonBox *buttonBox; + QPushButton *startButton = nullptr; + QPushButton *quitButton = nullptr; + QDialogButtonBox *buttonBox = nullptr; QTcpServer tcpServer; QTcpSocket tcpClient; - QTcpSocket *tcpServerConnection; - int bytesToWrite; - int bytesWritten; - int bytesReceived; + QTcpSocket *tcpServerConnection = nullptr; + int bytesToWrite = 0; + int bytesWritten = 0; + int bytesReceived = 0; }; #endif diff --git a/examples/network/loopback/main.cpp b/examples/network/loopback/main.cpp index 9959c472f4..094a11d4fc 100644 --- a/examples/network/loopback/main.cpp +++ b/examples/network/loopback/main.cpp @@ -48,10 +48,10 @@ ** ****************************************************************************/ -#include <QApplication> - #include "dialog.h" +#include <QApplication> + int main(int argc, char *argv[]) { QApplication app(argc, argv); 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..1556e26ab1 100644 --- a/examples/network/network.pro +++ b/examples/network/network.pro @@ -30,6 +30,7 @@ qtHaveModule(widgets) { } qtConfig(openssl): SUBDIRS += securesocketclient + qtConfig(dtls): SUBDIRS += 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..43792faa4b --- /dev/null +++ b/examples/network/secureudpclient/addressdialog.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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..c950260078 --- /dev/null +++ b/examples/network/secureudpclient/association.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** 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) +{ + //! [1] + auto configuration = QSslConfiguration::defaultDtlsConfiguration(); + configuration.setPeerVerifyMode(QSslSocket::VerifyNone); + crypto.setPeer(address, port); + crypto.setDtlsConfiguration(configuration); + //! [1] + + //! [2] + connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout); + //! [2] + connect(&crypto, &QDtls::pskRequired, this, &DtlsAssociation::pskRequired); + //! [3] + socket.connectToHost(address.toString(), port); + //! [3] + //! [13] + connect(&socket, &QUdpSocket::readyRead, this, &DtlsAssociation::readyRead); + //! [13] + //! [4] + pingTimer.setInterval(5000); + connect(&pingTimer, &QTimer::timeout, this, &DtlsAssociation::pingTimeout); + //! [4] +} + +//! [12] +DtlsAssociation::~DtlsAssociation() +{ + if (crypto.isConnectionEncrypted()) + crypto.shutdown(&socket); +} +//! [12] + +//! [5] +void DtlsAssociation::startHandshake() +{ + if (socket.state() != QAbstractSocket::ConnectedState) { + emit infoMessage(tr("%1: connecting UDP socket first ...").arg(name)); + connect(&socket, &QAbstractSocket::connected, this, &DtlsAssociation::udpSocketConnected); + return; + } + + if (!crypto.doHandshake(&socket)) + emit errorMessage(tr("%1: failed to start a handshake - %2").arg(name, crypto.dtlsErrorString())); + else + emit infoMessage(tr("%1: starting a handshake").arg(name)); +} +//! [5] + +void DtlsAssociation::udpSocketConnected() +{ + emit infoMessage(tr("%1: UDP socket is now in ConnectedState, continue with handshake ...").arg(name)); + startHandshake(); +} + +void DtlsAssociation::readyRead() +{ + //! [6] + QByteArray dgram(socket.pendingDatagramSize(), Qt::Uninitialized); + const qint64 bytesRead = socket.readDatagram(dgram.data(), dgram.size()); + if (bytesRead <= 0) { + emit warningMessage(tr("%1: spurious read notification?").arg(name)); + return; + } + + dgram.resize(bytesRead); + //! [6] + //! [7] + if (crypto.isConnectionEncrypted()) { + const QByteArray plainText = crypto.decryptDatagram(&socket, dgram); + if (plainText.size()) { + emit serverResponse(name, dgram, plainText); + return; + } + + if (crypto.dtlsError() == QDtlsError::RemoteClosedConnectionError) { + emit errorMessage(tr("%1: shutdown alert received").arg(name)); + socket.close(); + pingTimer.stop(); + return; + } + + emit warningMessage(tr("%1: zero-length datagram received?").arg(name)); + } else { + //! [7] + //! [8] + if (!crypto.doHandshake(&socket, dgram)) { + emit errorMessage(tr("%1: handshake error - %2").arg(name, crypto.dtlsErrorString())); + return; + } + //! [8] + + //! [9] + if (crypto.isConnectionEncrypted()) { + emit infoMessage(tr("%1: encrypted connection established!").arg(name)); + pingTimer.start(); + pingTimeout(); + } else { + //! [9] + emit infoMessage(tr("%1: continuing with handshake ...").arg(name)); + } + } +} + +//! [11] +void DtlsAssociation::handshakeTimeout() +{ + emit warningMessage(tr("%1: handshake timeout, trying to re-transmit").arg(name)); + if (!crypto.handleTimeout(&socket)) + emit errorMessage(tr("%1: failed to re-transmit - %2").arg(name, crypto.dtlsErrorString())); +} +//! [11] + +//! [14] +void DtlsAssociation::pskRequired(QSslPreSharedKeyAuthenticator *auth) +{ + Q_ASSERT(auth); + + emit infoMessage(tr("%1: providing pre-shared key ...").arg(name)); + auth->setIdentity(name.toLatin1()); + auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f")); +} +//! [14] + +//! [10] +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(tr("%1: failed to send a ping - %2").arg(name, crypto.dtlsErrorString())); + pingTimer.stop(); + return; + } + + ++ping; +} +//! [10] + +QT_END_NAMESPACE diff --git a/examples/network/secureudpclient/association.h b/examples/network/secureudpclient/association.h new file mode 100644 index 0000000000..be89ce695e --- /dev/null +++ b/examples/network/secureudpclient/association.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 + +//! [0] +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 udpSocketConnected(); + 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) +}; +//! [0] + +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..2fbf757c81 --- /dev/null +++ b/examples/network/secureudpclient/mainwindow.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** 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; +} + +//! [0] + +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)); +} + +//! [0] + +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..0d443fd376 --- /dev/null +++ b/examples/network/secureudpclient/mainwindow.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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..ef1974c311 --- /dev/null +++ b/examples/network/secureudpserver/mainwindow.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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")); +} + +//! [0] +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)); +} +//! [0] diff --git a/examples/network/secureudpserver/mainwindow.h b/examples/network/secureudpserver/mainwindow.h new file mode 100644 index 0000000000..b39d984d50 --- /dev/null +++ b/examples/network/secureudpserver/mainwindow.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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..0d83fa9b51 --- /dev/null +++ b/examples/network/secureudpserver/server.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 + +//! [1] +DtlsServer::DtlsServer() +{ + connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead); + + serverConfiguration = QSslConfiguration::defaultDtlsConfiguration(); + serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server"); + serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); +} +//! [1] + +DtlsServer::~DtlsServer() +{ + shutdown(); +} + +//! [2] +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; +} +//! [2] + +bool DtlsServer::isListening() const +{ + return listening; +} + +void DtlsServer::close() +{ + listening = false; +} + +void DtlsServer::readyRead() +{ + //! [3] + 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); + //! [3] + //! [4] + 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->peerAddress() == peerAddress + && connection->peerPort() == peerPort; + }); + //! [4] + + //! [5] + if (client == knownClients.end()) + return handleNewConnection(peerAddress, peerPort, dgram); + //! [5] + + //! [6] + if ((*client)->isConnectionEncrypted()) { + decryptDatagram(*client, dgram); + if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError) + knownClients.erase(client); + return; + } + //! [6] + + //! [7] + doHandshake(*client, dgram); + //! [7] +} + +//! [13] +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")); +} +//! [13] + +//! [8] +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")); + //! [8] + //! [9] + DtlsConnection newConnection(new QDtls(QSslSocket::SslServerMode)); + newConnection->setDtlsConfiguration(serverConfiguration); + newConnection->setPeer(peerAddress, peerPort); + newConnection->connect(newConnection.data(), &QDtls::pskRequired, + this, &DtlsServer::pskRequired); + knownClients.push_back(newConnection); + doHandshake(newConnection, clientHello); + //! [9] + } else if (cookieSender.dtlsError() != QDtlsError::NoError) { + emit errorMessage(tr("DTLS error: ") + cookieSender.dtlsErrorString()); + } else { + emit infoMessage(peerInfo + tr(": not verified yet")); + } +} + +//! [11] +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->peerAddress(), + newConnection->peerPort()); + 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(); + } +} +//! [11] + +//! [12] +void DtlsServer::decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage) +{ + Q_ASSERT(connection->isConnectionEncrypted()); + + const QString peerInfo = peer_info(connection->peerAddress(), connection->peerPort()); + 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()); + } +} +//! [12] + +//! [14] +void DtlsServer::shutdown() +{ + for (DtlsConnection &connection : knownClients) + connection->shutdown(&serverSocket); + + knownClients.clear(); + serverSocket.close(); +} +//! [14] + +QT_END_NAMESPACE diff --git a/examples/network/secureudpserver/server.h b/examples/network/secureudpserver/server.h new file mode 100644 index 0000000000..b720368e7b --- /dev/null +++ b/examples/network/secureudpserver/server.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 + +//! [0] +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) +}; +//! [0] + +QT_END_NAMESPACE + +#endif // SERVER_H |