summaryrefslogblamecommitdiffstats
path: root/src/client/net/serverdiscovery.cpp
blob: 33ddbf58221f87f00bbba5c2f45d6e5ce7f45885 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                                                

                                                 

                                       





                                                        
                              

                                                                       







                                                                                       
                                                           














                                                                                
                                                                         
 
                               

                                         
                                            

         
                                    










                                               
                                                           





























                                                                                

                                                   





                                                                       

                                                                                    
                                           


                                                                                             



                                                                                                                                                        



                                                                                                                                

                         


                                                                                                              
         

                                                       
                                                               












                                                                                
                                                        
                                                              
                                        




                                                                                                 
                                                                                  


                                 
                                                                           








                                                                                                         
                                                                                            





                                                                              
                                                                                                     







                                                                                      
                                                                                               

                                                   
                                                                                                                                   




                                     

#include <QNetworkInterface>
#include "../../shared/settings.h"
#include "../../shared/network.h"
#include "../../shared/util.h"
#include "serverdiscovery.h"
#include <assert.h>


/***************************************************************************//**
 * Ctor
 */
ServerDiscovery::ServerDiscovery(QObject *parent)
	:	QObject(parent),
	    _minDiscoveryInterval(500),
	    _maxDiscoveryInterval(5000)
{
	_hashErrorCount = 0;
	_ipErrorCount = 0;

	/* Try to get a UDP port for server discovery */
	int tries = 10;
	while (tries-- != 0) {
		quint16 port = (quint16)(16384 + qrand() % 32768);
		if (_discoverySocket.bind(QHostAddress::AnyIPv4, port))
			break;
		if (tries == 0)
			qFatal("Could not bind to any UDP port for server discovery.");
	}
	// Handle incoming messages
	connect(&_discoverySocket, SIGNAL(readyRead()), this, SLOT(onUdpReadyRead()));

	/* Setup the discovery timer */
	_discoveryTimer.setInterval(_minDiscoveryInterval);
	_discoveryTimer.setSingleShot(true);
	//
	connect(&_discoveryTimer, SIGNAL(timeout()), this, SLOT(doDiscovery()));
}

/***************************************************************************//**
 * Dtor
 */
ServerDiscovery::~ServerDiscovery()
{
}

/***************************************************************************//**
 * @brief start
 */
void ServerDiscovery::start(const QByteArray& sessionName, QString mgrIP)
{
	if (!mgrIP.isEmpty()) {
		_mgrIP.setAddress(mgrIP);
	} else {
		_mgrIP = QHostAddress::Null;
	}

	//assert(!this->isActive());

	// Set the session which is searched
	_nameBytes = sessionName;

	// Enable signal emittance
	this->blockSignals(false);

	// Reset the error counters
	_hashErrorCount = _ipErrorCount = 0;

	// reset anbd start the discovery timer
	_discoveryTimer.setInterval(_minDiscoveryInterval);
	_discoveryTimer.start();
}

/***************************************************************************//**
 * @brief stop
 */
void ServerDiscovery::stop()
{
	assert(this->isActive());

	//Bock further signal emittance
	this->blockSignals(true);
	_discoveryTimer.stop();
}

/*******************************************************************************
 * SLOTS
 ***************************************************************************//**
 * @brief ConnectWindow::doDiscovery
 */
void ServerDiscovery::doDiscovery()
{
	// Send discovery
	_packet.reset();
	QByteArray iplist(Network::interfaceAddressesToString().toUtf8());
	// qDebug
	QByteArray salt1(SALT_LEN, 0);
	if (_salt2.size() < SALT_LEN)
		_salt2.resize(SALT_LEN);
	for (int i = 0; i < SALT_LEN; ++i) {
		salt1[i] = (char)(qrand() & 0xff);
		_salt2[i] = (char)(qrand() & 0xff);
	}
	_packet.reset();
	_packet.setField(_HASH, genSha1(&_nameBytes, &salt1, &iplist));
	_packet.setField(_SALT1, salt1);
	_packet.setField(_SALT2, _salt2);
	_packet.setField(_IPLIST, iplist);

	// Check if specifig manager IP is given. If not broadcast in whole network.
	if (_mgrIP != QHostAddress::Null) {
		qDebug() << "Broadcasting to " << _mgrIP.toString();
		if (!_packet.writeMessage(&_discoverySocket, _mgrIP, SERVICE_DISCOVERY_PORT))
			qDebug("Failed");
	} else {
		foreach (QNetworkInterface interface, QNetworkInterface::allInterfaces()) {
			foreach (QNetworkAddressEntry entry, interface.addressEntries()) {
				if (!entry.broadcast().isNull() && entry.ip() != QHostAddress::LocalHost && entry.ip() != QHostAddress::LocalHostIPv6) {
					qDebug() << "Broadcasting to " << entry.broadcast().toString();
					if (!_packet.writeMessage(&_discoverySocket, entry.broadcast(), SERVICE_DISCOVERY_PORT))
						qDebug("FAILED");
				}
			}
		}
		qDebug("Broadcasting to 255.255.255.255");
		if (!_packet.writeMessage(&_discoverySocket, QHostAddress::Broadcast, SERVICE_DISCOVERY_PORT))
			qDebug("FAILED");
	}

	// Start the timer again with a larger interval
	if (_discoveryTimer.interval() < _maxDiscoveryInterval)
		_discoveryTimer.setInterval(_discoveryTimer.interval() * 2);
	_discoveryTimer.start();
}


/***************************************************************************//**
 * Handle incoming service discovery packets.
 */
void ServerDiscovery::onUdpReadyRead()
{
	char data[UDPBUFSIZ];
	QHostAddress addr;
	quint16 port;
	while (_discoverySocket.hasPendingDatagrams()) {
		// Discard any packets if discovery is stopped
		if (!this->isActive()) {
			_discoverySocket.readDatagram(NULL, 0);
			continue;
		}

		const qint64 size = _discoverySocket.readDatagram(data, UDPBUFSIZ, &addr, &port);
		if (size <= 0) //|| clientApp->connection() != NULL) // TODO CHECK
			continue;

		_packet.reset();
		if (_packet.readMessage(data, (quint32)size) != NM_READ_OK)
			continue;

		// Valid packet, process it:
		const QByteArray hash(_packet.getFieldBytes(_HASH));
		const QByteArray iplist(_packet.getFieldBytes(_IPLIST));
		const QByteArray port(_packet.getFieldBytes(_PORT));
		const QByteArray cert(_packet.getFieldBytes(_CERT));

		// Check if the source IP of the packet matches any of the addresses given in the IP list
		if (!Network::isAddressInList(QString::fromUtf8(iplist), addr.toString())) {
			++_ipErrorCount;
			emit error(ErrorType::InvalidIpList, _hashErrorCount);
			continue;
		}

		// If so, check if the submitted hash seems valid
		if (genSha1(&_nameBytes, &_salt2, &iplist, &port, &cert) != hash && _mgrIP != addr) {
			// did not match local session name, or other data was spoofed
			++_hashErrorCount;
			emit error(ErrorType::InvalidHash, _ipErrorCount);
			continue;
		}

		/* Otherwise it's a valid reply */
		qDebug() << "Server detected:"
		         << addr.toString() + ":" + QString::fromUtf8(port) + "/" + _nameBytes;

		// Tell that a server hs been found
		emit serverDetected(addr.toString(), (quint16)QString::fromUtf8(port).toInt(), _nameBytes, cert, (_mgrIP == addr));

		// Stop the discovery
		this->stop();
	}
}