1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2018 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the examples of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:BSD$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** BSD License Usage
19** Alternatively, you may use this file under the terms of the BSD license
20** as follows:
21**
22** "Redistribution and use in source and binary forms, with or without
23** modification, are permitted provided that the following conditions are
24** met:
25** * Redistributions of source code must retain the above copyright
26** notice, this list of conditions and the following disclaimer.
27** * Redistributions in binary form must reproduce the above copyright
28** notice, this list of conditions and the following disclaimer in
29** the documentation and/or other materials provided with the
30** distribution.
31** * Neither the name of The Qt Company Ltd nor the names of its
32** contributors may be used to endorse or promote products derived
33** from this software without specific prior written permission.
34**
35**
36** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
37** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
39** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
40** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
47**
48** $QT_END_LICENSE$
49**
50****************************************************************************/
51
52#include <QtNetwork>
53
54#include "client.h"
55#include "connection.h"
56#include "peermanager.h"
57
58static const qint32 BroadcastInterval = 2000;
59static const unsigned broadcastPort = 45000;
60
61PeerManager::PeerManager(Client *client)
62 : QObject(client)
63{
64 this->client = client;
65
66 static const char *envVariables[] = {
67 "USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME"
68 };
69
70 for (const char *varname : envVariables) {
71 username = qEnvironmentVariable(varname);
72 if (!username.isNull())
73 break;
74 }
75
76 if (username.isEmpty())
77 username = "unknown";
78
79 updateAddresses();
80 serverPort = 0;
81
82 broadcastSocket.bind(QHostAddress::Any, broadcastPort, QUdpSocket::ShareAddress
83 | QUdpSocket::ReuseAddressHint);
84 connect(&broadcastSocket, &QUdpSocket::readyRead,
85 this, &PeerManager::readBroadcastDatagram);
86
87 broadcastTimer.setInterval(BroadcastInterval);
88 connect(&broadcastTimer, &QTimer::timeout,
89 this, &PeerManager::sendBroadcastDatagram);
90}
91
92void PeerManager::setServerPort(int port)
93{
94 serverPort = port;
95}
96
97QString PeerManager::userName() const
98{
99 return username;
100}
101
102void PeerManager::startBroadcasting()
103{
104 broadcastTimer.start();
105}
106
107bool PeerManager::isLocalHostAddress(const QHostAddress &address) const
108{
109 for (const QHostAddress &localAddress : ipAddresses) {
110 if (address.isEqual(localAddress))
111 return true;
112 }
113 return false;
114}
115
116void PeerManager::sendBroadcastDatagram()
117{
118 QByteArray datagram;
119 {
120 QCborStreamWriter writer(&datagram);
121 writer.startArray(2);
122 writer.append(username);
123 writer.append(serverPort);
124 writer.endArray();
125 }
126
127 bool validBroadcastAddresses = true;
128 for (const QHostAddress &address : qAsConst(broadcastAddresses)) {
129 if (broadcastSocket.writeDatagram(datagram, address,
130 broadcastPort) == -1)
131 validBroadcastAddresses = false;
132 }
133
134 if (!validBroadcastAddresses)
135 updateAddresses();
136}
137
138void PeerManager::readBroadcastDatagram()
139{
140 while (broadcastSocket.hasPendingDatagrams()) {
141 QHostAddress senderIp;
142 quint16 senderPort;
143 QByteArray datagram;
144 datagram.resize(broadcastSocket.pendingDatagramSize());
145 if (broadcastSocket.readDatagram(datagram.data(), datagram.size(),
146 &senderIp, &senderPort) == -1)
147 continue;
148
149 int senderServerPort;
150 {
151 // decode the datagram
152 QCborStreamReader reader(datagram);
153 if (reader.lastError() != QCborError::NoError || !reader.isArray())
154 continue;
155 if (!reader.isLengthKnown() || reader.length() != 2)
156 continue;
157
158 reader.enterContainer();
159 if (reader.lastError() != QCborError::NoError || !reader.isString())
160 continue;
161 while (reader.readString().status == QCborStreamReader::Ok) {
162 // we don't actually need the username right now
163 }
164
165 if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger())
166 continue;
167 senderServerPort = reader.toInteger();
168 }
169
170 if (isLocalHostAddress(senderIp) && senderServerPort == serverPort)
171 continue;
172
173 if (!client->hasConnection(senderIp)) {
174 Connection *connection = new Connection(this);
175 emit newConnection(connection);
176 connection->connectToHost(senderIp, senderServerPort);
177 }
178 }
179}
180
181void PeerManager::updateAddresses()
182{
183 broadcastAddresses.clear();
184 ipAddresses.clear();
185 const QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
186 for (const QNetworkInterface &interface : interfaces) {
187 const QList<QNetworkAddressEntry> entries = interface.addressEntries();
188 for (const QNetworkAddressEntry &entry : entries) {
189 QHostAddress broadcastAddress = entry.broadcast();
190 if (broadcastAddress != QHostAddress::Null && entry.ip() != QHostAddress::LocalHost) {
191 broadcastAddresses << broadcastAddress;
192 ipAddresses << entry.ip();
193 }
194 }
195 }
196}
197