Pool Video Switch v2
Software video switch for distributed remote display in a lecture environment
serverdiscovery.cpp
Go to the documentation of this file.
1 
2 #include <QNetworkInterface>
3 #include "../../shared/settings.h"
4 #include "../../shared/network.h"
5 #include "../../shared/util.h"
6 #include "serverdiscovery.h"
7 #include "../util/util.h"
8 
13  : QObject(parent),
14  _minDiscoveryInterval(500),
15  _maxDiscoveryInterval(5000)
16 {
17  _hashErrorCount = 0;
18  _ipErrorCount = 0;
19 
20  /* Try to get a UDP port for server discovery */
21  int tries = 10;
22  while (tries-- != 0) {
23  quint16 port = quint16(16384 + slxrand() % 32768);
24  if (_discoverySocket.bind(QHostAddress::AnyIPv4, port))
25  break;
26  if (tries == 0)
27  qFatal("Could not bind to any UDP port for server discovery.");
28  }
29  // Handle incoming messages
30  connect(&_discoverySocket, &QUdpSocket::readyRead, this, &ServerDiscovery::onUdpReadyRead);
31 
32  /* Setup the discovery timer */
34  _discoveryTimer.setSingleShot(true);
35  //
36  connect(&_discoveryTimer, &QTimer::timeout, this, &ServerDiscovery::doDiscovery);
37 }
38 
43 
47 void ServerDiscovery::start(const QByteArray& sessionName, const QString& mgrIP)
48 {
49  if (!mgrIP.isEmpty()) {
50  _mgrIP.setAddress(mgrIP);
51  } else {
52  _mgrIP = QHostAddress::Null;
53  }
54 
55  //assert(!this->isActive());
56 
57  // Set the session which is searched
58  _nameBytes = sessionName;
59 
60  // Enable signal emittance
61  this->blockSignals(false);
62 
63  // Reset the error counters
65 
66  // reset anbd start the discovery timer
68  _discoveryTimer.start();
69 }
70 
75 {
76  assert(this->isActive());
77 
78  //Bock further signal emittance
79  this->blockSignals(true);
80  _discoveryTimer.stop();
81 }
82 
83 /*******************************************************************************
84  * SLOTS
85  ***************************************************************************/
89 {
90  // Send discovery
91  _packet.reset();
92  QByteArray iplist(Network::interfaceAddressesToString().toUtf8());
93  // qDebug
94  QByteArray salt1(SALT_LEN, 0);
95  if (_salt2.size() < SALT_LEN)
96  _salt2.resize(SALT_LEN);
97  for (int i = 0; i < SALT_LEN; ++i) {
98  salt1[i] = char(slxrand() & 0xff);
99  _salt2[i] = char(slxrand() & 0xff);
100  }
101  _packet.reset();
102  _packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist));
103  _packet.setField(_SALT1, salt1);
104  _packet.setField(_SALT2, _salt2);
105  _packet.setField(_IPLIST, iplist);
106 
107  // Check if specifig manager IP is given. If not broadcast in whole network.
108  if (_mgrIP != QHostAddress::Null) {
109  qDebug() << "Sending discovery to " << _mgrIP.toString();
111  qDebug("Failed");
112  } else {
113  foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
114  foreach (QNetworkAddressEntry entry, interface.addressEntries()) {
115  if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6) {
116  qDebug() << "Broadcasting to " << entry.broadcast().toString();
117  if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT))
118  qDebug("FAILED");
119  }
120  }
121  }
122  qDebug("Broadcasting to 255.255.255.255");
123  if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT))
124  qDebug("FAILED");
125  }
126 
127  // Start the timer again with a larger interval
128  if (_discoveryTimer.interval() < _maxDiscoveryInterval)
129  _discoveryTimer.setInterval(_discoveryTimer.interval() * 2);
130  _discoveryTimer.start();
131 }
132 
133 
138 {
139  char data[UDPBUFSIZ];
140  QHostAddress addr;
141  quint16 peerPort;
142  while (_discoverySocket.hasPendingDatagrams()) {
143  // Discard any packets if discovery is stopped
144  if (!this->isActive()) {
145  _discoverySocket.readDatagram(nullptr, 0);
146  continue;
147  }
148 
149  const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &peerPort);
150  if (size <= 0) //|| clientApp->connection() != nullptr) // TODO CHECK
151  continue;
152 
153  _packet.reset();
154  if (_packet.readMessage(data, quint32(size)) != NM_READ_OK) {
155  qDebug() << "Corrupt discovery reply from" << addr.toString();
156  continue;
157  }
158 
159  // Valid packet, process it:
160  const QByteArray hash(_packet.getFieldBytes(_HASH));
161  const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
162  const QByteArray port(_packet.getFieldBytes(_PORT));
163  const QByteArray cert(_packet.getFieldBytes(_CERT));
164 
165  // Check if the source IP of the packet matches any of the addresses given in the IP list
166  if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString())) {
167  qDebug() << "Received bogus discovery reply from" << addr.toString() << "... Not in" << iplist;
168  ++_ipErrorCount;
170  continue;
171  }
172 
173  // If so, check if the submitted hash seems valid
174  if (genSha1(&_nameBytes, &_salt2, &iplist, &port, &cert) != hash && _mgrIP != addr) {
175  // did not match local session name, or other data was spoofed
176  qDebug() << "Received bogus session name in discovery reply from" << addr.toString();
177  ++_hashErrorCount;
179  continue;
180  }
181 
182  /* Otherwise it's a valid reply */
183  qDebug() << "Server detected:"
184  << addr.toString() + ":" + QString::fromUtf8(port) + "/" + _nameBytes;
185 
186  // Tell that a server hs been found
187  bool ok = false;
188  const ushort iport = QString::fromUtf8(port).toUShort(&ok);
189  if (ok) {
190  emit serverDetected(addr.toString(), quint16(iport), _nameBytes, cert, (_mgrIP == addr));
191  } else {
192  qDebug() << "... but server advertises unparsable port" << port;
193  }
194 
195  // Stop the discovery
196  this->stop();
197  }
198 }
QUdpSocket _discoverySocket
ServerDiscovery(QObject *parent=nullptr)
Ctor.
void error(ErrorType e, int count)
bool writeMessage(QAbstractSocket *socket)
#define NM_READ_OK
#define SERVICE_DISCOVERY_PORT
Definition: settings.h:10
static quint16 hash(const QHostAddress &host)
hash
NetworkMessage _packet
QByteArray _salt2
const int _minDiscoveryInterval
void start(const QByteArray &sessionName, const QString &mgrIP)
start
QByteArray genSha1(const QByteArray *a, const QByteArray *b, const QByteArray *c, const QByteArray *d, const QByteArray *e)
Definition: util.cpp:13
const int _maxDiscoveryInterval
#define slxrand()
Definition: util.h:19
~ServerDiscovery() override
Dtor.
void onUdpReadyRead()
Handle incoming service discovery packets.
bool isAddressInList(const QString &list, const QString &address)
Definition: network.cpp:42
int readMessage(QAbstractSocket *socket)
static const int SALT_LEN
QHostAddress _mgrIP
QString interfaceAddressesToString()
Returns list of all addresses assigned to the interfaces of this machine.
Definition: network.cpp:22
QByteArray _nameBytes
static const int UDPBUFSIZ
void serverDetected(const QString &host, const quint16 port, const QByteArray &sessionName, const QByteArray &certHash, bool autoConnect)
QByteArray getFieldBytes(const QByteArray &key) const
void setField(const QByteArray &key, const QByteArray &value)
void doDiscovery()
ConnectWindow::doDiscovery.