summaryrefslogblamecommitdiffstats
path: root/src/shared/networkmessage.cpp
blob: 0bdf3c5821a3f4ff4021f4be5b2b3145b41abe15 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                           



                                    

                       

                               
 
                                      


                                                       
                                      

 
                                      


                                                       
                                      

 
                                     
 

                                         

 
                                     
 



                                                      







                                                                      
                                                                                     











                                         
                                                                  






                                                    
                                                        

                                                                              

                                 
                                                                                                            
                                              



                                                                                              
                               
                                                          
                                                  
                                        
                                                                     
                                                                                                
                                              

                                               
                                              


                                                                                 

                                                                                  

                                                                                                        
                                                                          
                                                                                             
                                                      
                         
                                                   

                                                                                                           
                                                
                                                         
                                                      

                 
                          

 
                                                        

                                                                              

                                 
                                                                                                            
                                              


                          
                               
                                                                     
                                      

                                     
                                      
                                              
                                                                                                            
                                      
         
                                                                                   



                                              
                                                                                           



                                                
                                         
                                        


                                                                      
                                                                                                                             








                                                          
                                                   
                                                   
                         
                                                   
                         
                                                                     
                                                                                                            
                                                                                                                  

                                     
                                                                                          






                                                                                                                            
                                                                 
 

                                 






                                                                                                        
                               




                                                                                         
                                                        
                             
                                   
                                        







                                                                                                

                                 






                                                                                                        
                               














                                                                                      
                                                                      
                                                                                                             
                                                








                                                                                                                 
                                                         
                      
                                                                     













                                                                 
/*
 * NetworkMessage.cpp
 *
 *  Created on: 18.01.2013
 *      Author: sr
 */

#include <QtCore>
#include <QtNetwork>
#include "networkmessage.h"

#define HEADER_LEN 8
#define MAX_MSG_LEN 60000

#define BYTE_SWAP4(x) \
    ((((x) & 0xFF000000u) >> 24) | \
     (((x) & 0x00FF0000u) >> 8)  | \
     (((x) & 0x0000FF00u) << 8)  | \
     (((x) & 0x000000FFu) << 24))

#define BYTE_SWAP2(x) \
    ((((x) & 0xFF00u) >> 8) | \
     (((x) & 0x00FFu) << 8))

static quint16 _htons(const quint16 x)
{
	if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
		return x;
	return (quint16)BYTE_SWAP2(x);
}

static quint32 _htonl(const quint32 x)
{
	if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
		return x;
	return (quint32)BYTE_SWAP4(x);
}

static quint16 _ntohs(const char *in)
{
	return quint16(quint8(in[0]) << 8
			| quint8(in[1]));
}

static quint32 _ntohl(const char *in)
{
	return quint32(quint8(in[0])) << 24
			| quint32(quint8(in[1])) << 16
			| quint32(quint8(in[2])) << 8
			| quint32(quint8(in[3]));
}

/*
 // ####################################################### \\ ° . °
 \\ ####################################################### // \___/
 */

NetworkMessage::NetworkMessage() :
	_buffer(nullptr), _bufferSize(0), _bufferPos(0), _lastBufferSize(0), _mode(0)
{
	//
}

NetworkMessage::~NetworkMessage()
{
	if (_buffer)
		delete[] _buffer;
}

inline void NetworkMessage::allocBuffer()
{
	if (_lastBufferSize < _bufferSize || _buffer == nullptr) {
		if (_buffer)
			delete[] _buffer;
		_lastBufferSize = _bufferSize;
		_buffer = new char[_lastBufferSize];
	}
}

int NetworkMessage::readMessage(QAbstractSocket* socket)
{
	// Check/Set the _mode variable, so read and write calls are not mixed
	if (_mode != 1) {
		if (_mode != 0) {
			qDebug("NetworkMessage::readMessage(TCP) called when class was in mode %d!", _mode);
			return NM_READ_FAILED;
		}
		_mode = 1;
	}
	// buffer size == 0 means the header hasn't been received yet. do so and set things up
	if (_bufferSize == 0) {
		if (socket->bytesAvailable() < HEADER_LEN)
			return NM_READ_INCOMPLETE;
		char header[HEADER_LEN];
		if (socket->read(header, HEADER_LEN) != HEADER_LEN) {
			qDebug("FIXME: Socket said 8 bytes available, but could not read 8...");
			return NM_READ_FAILED;
		}
		if (!this->parseHeader(header))
			return NM_READ_FAILED;
		//qDebug() << "Expecting message of " << _bufferSize << " bytes";
		allocBuffer();
	}
	if (_bufferSize > _bufferPos) {
		while (_bufferSize > _bufferPos && socket->bytesAvailable() > 0) {
			const qint64 ret = socket->read(_buffer + _bufferPos, _bufferSize - _bufferPos);
			//qDebug() << "Read " << ret << " bytes";
			if (ret < 0 || ret > (_bufferSize - _bufferPos)) {
				qDebug("Socket read failed (TCP), return code %d", (int)ret);
				return NM_READ_FAILED;
			}
			_bufferPos += (quint32)ret;
			//qDebug() << "Buffer has now " << _bufferPos << " of " << _bufferSize << " bytes";
		}
		if (_bufferSize == _bufferPos) {
			if (!this->parseMessage(_buffer))
				return NM_READ_FAILED;
		}
	}
	return NM_READ_OK;
}

int NetworkMessage::readMessage(char* data, quint32 len)
{
	// Check/Set the _mode variable, so read and write calls are not mixed
	if (_mode != 1) {
		if (_mode != 0) {
			qDebug("NetworkMessage::readMessage(UDP) called when class was in mode %d!", _mode);
			return NM_READ_FAILED;
		}
		_mode = 1;
	}
	if (len < HEADER_LEN) {
		qDebug("UDP message shorter than 8 bytes. ignored.");
		return NM_READ_FAILED;
	}
	if (!this->parseHeader(data))
		return NM_READ_FAILED;
	if (len != _bufferSize + HEADER_LEN) {
		qDebug("UDP packet has wrong size. Is %d, expected %d", (int)_bufferSize, len - HEADER_LEN);
		return NM_READ_FAILED;
	}
	return this->parseMessage(data + HEADER_LEN) ? NM_READ_OK : NM_READ_FAILED;
}

bool NetworkMessage::parseHeader(char *header)
{
	if (header[0] != 'P' || header[1] != 'V' || header[2] != 'S' || header[3] != '2') {
		qDebug("Protocol magic wrong.");
		return false;
	}
	_bufferPos = 0;
	_bufferSize = _ntohl(header + 4);
	if (_bufferSize > MAX_MSG_LEN) {
		qDebug("Disconnecting Client: MAX_MSG_LEN exceeded.");
		return false;
	}
	if (_bufferSize < 4) { // TODO: magic number. msg needs to be at least 4 bytes for 1 key/value pair of length 0 each.
		qDebug("A Client sent an empty message.");
		return false;
	}
	return true;
}

bool NetworkMessage::parseMessage(char *buffer)
{
	char *ptr = buffer;
	while (_bufferSize - (ptr - buffer) >= 4) {
		const quint16 keyLen = _ntohs(ptr);
		ptr += 2;
		const quint16 valLen = _ntohs(ptr);
		ptr += 2;
		if (_bufferSize - (ptr - buffer) < keyLen + valLen) {
			qDebug() << "Warning: Error parsing message. key(" << keyLen << ")+value(" << valLen
			         << ") length > total remaining bytes (" << (_bufferSize - (ptr - buffer)) << ")";
			return false;
		}
		_fields.insert(QByteArray(ptr, keyLen), QByteArray(ptr + keyLen, valLen));
		//qDebug() << "Got " << QString::fromUtf8(ptr, keyLen) << " -> " << QString::fromUtf8(ptr + keyLen, valLen);
		ptr += keyLen + valLen;
	}
	_mode = 3;
	return true;
}

bool NetworkMessage::writeMessage(QAbstractSocket * const socket)
{
	if (_mode != 2) {
		if (_mode == 1) {
			qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
			return false;
		}
		_mode = 1;
		_bufferPos = 0;
	}
	// key/value pairs have not been serialized yet...
	if (_bufferSize == 0) {
		this->serializeMessage();
	}
	const qint64 ret = socket->write(_buffer + _bufferPos, _bufferSize - _bufferPos);
	if (ret == 0)
		return true;
	if (ret < 0 || ret > (_bufferSize - _bufferPos))
		return false;
	_bufferPos += (quint32)ret;
	if (_bufferPos == _bufferSize) {
		_bufferPos = 0;
		_mode = 4;
	}
	return true;
}

bool NetworkMessage::writeMessage(QUdpSocket* socket, const QHostAddress& address, quint16 port)
{
	if (_mode != 4) {
		if (_mode == 1) {
			qDebug("NetworkMessage::writeMessage called when class was in mode %d!", _mode);
			return false;
		}
		_mode = 4;
		_bufferPos = 0;
	}
	// key/value pairs have not been serialized yet...
	if (_bufferSize == 0) {
		this->serializeMessage();
	}
	const qint64 ret = socket->writeDatagram(_buffer, _bufferSize, address, port);
	if (ret != _bufferSize)
		return false;
	if (ret < 0)
		return false;
	_bufferPos = 0;
	return true;
}

void NetworkMessage::serializeMessage()
{
	QByteArray buf;
	//qDebug() << "Default size: " << buf.capacity();
	buf.reserve(_lastBufferSize > 0 ? int(_lastBufferSize) : 200);
	for (QHash<QByteArray, QByteArray>::const_iterator it = _fields.begin(); it != _fields.end(); ++it) {
		const QByteArray &ba = it.key();
		const QByteArray &val = it.value();
		quint16 keyLen = _htons((quint16)ba.size());
		quint16 valLen = _htons((quint16)val.size());
		//qDebug() << "Adding to msg(" << ba.size() << "/" << val.size() << "): " << ba << " -> " << val;
		buf.append((char*)&keyLen, 2);
		buf.append((char*)&valLen, 2);
		buf.append(ba);
		buf.append(val);
	}
	_bufferSize = quint32(buf.length() + HEADER_LEN);
	allocBuffer();
	memcpy(_buffer + HEADER_LEN, buf.data(), size_t(buf.size()));
	_buffer[0] = 'P';
	_buffer[1] = 'V';
	_buffer[2] = 'S';
	_buffer[3] = '2';
	const quint32 flipped = _htonl(_bufferSize - HEADER_LEN);
	memcpy(_buffer + 4, &flipped, 4);
}

void NetworkMessage::buildErrorMessage(const QString& error)
{
	this->reset();
	this->setField(_ID, _ERROR);
	this->setField(_ERROR, error);
}