From 1a5709501f94014d41987b956338bb6424b9f90c Mon Sep 17 00:00:00 2001 From: sr Date: Mon, 4 Feb 2013 19:50:31 +0100 Subject: Initial commit --- src/shared/network.cpp | 50 +++++++ src/shared/network.h | 25 ++++ src/shared/networkmessage.cpp | 296 ++++++++++++++++++++++++++++++++++++++++++ src/shared/networkmessage.h | 69 ++++++++++ src/shared/settings.h | 12 ++ src/shared/util.cpp | 22 ++++ src/shared/util.h | 15 +++ 7 files changed, 489 insertions(+) create mode 100644 src/shared/network.cpp create mode 100644 src/shared/network.h create mode 100644 src/shared/networkmessage.cpp create mode 100644 src/shared/networkmessage.h create mode 100644 src/shared/settings.h create mode 100644 src/shared/util.cpp create mode 100644 src/shared/util.h (limited to 'src/shared') diff --git a/src/shared/network.cpp b/src/shared/network.cpp new file mode 100644 index 0000000..6dd6a73 --- /dev/null +++ b/src/shared/network.cpp @@ -0,0 +1,50 @@ +/* + * network.cpp + * + * Created on: 28.01.2013 + * Author: sr + */ + +#include "network.h" +#include +#include +#include + +namespace Network +{ + + +/** + * Returns list of all addresses assigned to the interfaces of this machine. + * Every address is surrounded by '|', eg: + * |1.2.3.4|3.66.77.88|123:34::1| + */ +QString interfaceAddressesToString() +{ + QList list(QNetworkInterface::allAddresses()); + return interfaceAddressesToString(list); +} + +QString interfaceAddressesToString(const QList& list) +{ + QString ret; + ret.reserve(500); + for (QList::const_iterator it(list.begin()); it != list.end(); ++it) + { + if (*it == QHostAddress::LocalHost || *it == QHostAddress::LocalHostIPv6) + continue; // TODO: Filter other addresses/ranges? + ret.append("|"); + ret.append(it->toString()); + } + ret.append("|"); + return ret; +} + +bool isAddressInList(const QString& list, const QString& address) +{ + static const QString find("|%1|"); + return list.contains(find.arg(address), Qt::CaseSensitive); +} + + +} diff --git a/src/shared/network.h b/src/shared/network.h new file mode 100644 index 0000000..4a763be --- /dev/null +++ b/src/shared/network.h @@ -0,0 +1,25 @@ +/* + * network.h + * + * Created on: 28.01.2013 + * Author: sr + */ + +#ifndef NETWORK_H_ +#define NETWORK_H_ + +#include +#include + +namespace Network +{ + + +QString interfaceAddressesToString(); +QString interfaceAddressesToString(const QList& list); +bool isAddressInList(const QString& list, const QString& address); + + +} + +#endif /* NETWORK_H_ */ diff --git a/src/shared/networkmessage.cpp b/src/shared/networkmessage.cpp new file mode 100644 index 0000000..9a7ba9f --- /dev/null +++ b/src/shared/networkmessage.cpp @@ -0,0 +1,296 @@ +/* + * NetworkMessage.cpp + * + * Created on: 18.01.2013 + * Author: sr + */ + +#include +#include +#include "networkmessage.h" + +#define HEADER_LEN 8 +#define MAX_MSG_LEN 60000 + +#define BYTE_SWAP4(x) \ + (((x & 0xFF000000) >> 24) | \ + ((x & 0x00FF0000) >> 8) | \ + ((x & 0x0000FF00) << 8) | \ + ((x & 0x000000FF) << 24)) + +#define BYTE_SWAP2(x) \ + (((x & 0xFF00) >> 8) | \ + ((x & 0x00FF) << 8)) + +static quint16 _htons(quint16 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP2(x); +} + +static quint16 _ntohs(quint16 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP2(x); +} + +static quint32 _htonl(quint32 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP4(x); +} + +static quint32 _ntohl(quint32 x) +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return x; + return BYTE_SWAP4(x); +} + +/* + // ####################################################### \\ ° . ° + \\ ####################################################### // \___/ + */ + +NetworkMessage::NetworkMessage() : + _buffer(NULL), _bufferSize(0), _bufferPos(0), _lastBufferSize(0), _mode(0) +{ + // +} + +NetworkMessage::~NetworkMessage() +{ + if (_buffer) + delete[] _buffer; +} + +inline void NetworkMessage::allocBuffer() +{ + if (_lastBufferSize < _bufferSize || _buffer == NULL) + { + if (_buffer) + delete[] _buffer; + _lastBufferSize = _bufferSize; + _buffer = new char[_lastBufferSize]; + } +} + +bool 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 false; + } + _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 true; + 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 false; + } + if (!this->parseHeader(header)) + return false; + //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) + return false; + _bufferPos += ret; + //qDebug() << "Buffer has now " << _bufferPos << " of " << _bufferSize << " bytes"; + } + if (_bufferSize == _bufferPos) + { + if (!this->parseMessage(_buffer)) + return false; + } + } + return true; +} + +bool 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 false; + } + _mode = 1; + } + if (len < HEADER_LEN) + { + qDebug("UDP message shorter than 8 bytes. ignored."); + return false; + } + if (!this->parseHeader(data)) + return false; + if (len != _bufferSize + HEADER_LEN) + { + qDebug("UDP packet has wrong size. Is %d, expected %d", (int)_bufferSize, len - HEADER_LEN); + return false; + } + return this->parseMessage(data + HEADER_LEN); +} + +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(*(quint32*)(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(*(quint16*)(ptr)); + ptr += 2; + const quint16 valLen = _ntohs(*(quint16*)(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(QString::fromUtf8(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* 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) + return false; + _bufferPos += 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 ? _lastBufferSize : 200); + for (QHash::const_iterator it = _fields.begin(); it != _fields.end(); ++it) + { + const QByteArray &ba = it.key().toUtf8(); + 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 = buf.length() + HEADER_LEN; + allocBuffer(); + memcpy(_buffer + HEADER_LEN, buf.data(), 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); +} + +void NetworkMessage::buildErrorMessage(const char* error) +{ + this->buildErrorMessage(QString::fromUtf8(error)); +} diff --git a/src/shared/networkmessage.h b/src/shared/networkmessage.h new file mode 100644 index 0000000..5a8b2c2 --- /dev/null +++ b/src/shared/networkmessage.h @@ -0,0 +1,69 @@ +/* + * NetworkMessage.h + * + * Created on: 18.01.2013 + * Author: sr + */ + +#ifndef NETWORKMESSAGE_H_ +#define NETWORKMESSAGE_H_ + +#include + +class QAbstractSocket; +class QUdpSocket; + +// define qstrings for message ids. this prevents implicit instanciation of the same qstrings over and over again +#define MSGTYPE(name) static const QString _ ## name ( #name ) +MSGTYPE(ID); +MSGTYPE(IMG); +MSGTYPE(LOGIN); +MSGTYPE(THUMB); +MSGTYPE(X); +MSGTYPE(Y); +MSGTYPE(VNCSERVER); +MSGTYPE(VNCCLIENT); +MSGTYPE(LOCK); +MSGTYPE(HASH); +MSGTYPE(SALT1); +MSGTYPE(SALT2); +MSGTYPE(IPLIST); +MSGTYPE(PORT); +MSGTYPE(CERT); +MSGTYPE(CHALLENGE); +MSGTYPE(ERROR); + +class NetworkMessage +{ +private: + char *_buffer; + quint32 _bufferSize, _bufferPos, _lastBufferSize; + QHash _fields; + int _mode; // 0 = none, 1 = reading, 2 = writing, 3 = read complete, 4 = write complete + + void allocBuffer(); + bool parseHeader(char *header); + bool parseMessage(char *buffer); + void serializeMessage(); + +public: + NetworkMessage(); + virtual ~NetworkMessage(); + bool readMessage(QAbstractSocket* socket); + bool readMessage(char* data, quint32 len); + bool writeMessage(QAbstractSocket* socket); + bool writeMessage(QUdpSocket* socket, const QHostAddress& address, quint16 port); + void reset() { _fields.clear(); _bufferSize = 0; _mode = 0; } + const bool readComplete() const { return _mode == 3; } + const bool writeComplete() const { return _mode == 4; } + const bool hasField(const QString& key) const { return _fields.contains(key); } + const QString getFieldString(const QString& key) const { return QString::fromUtf8(_fields.value(key)); } + const QByteArray getFieldBytes(const QString& key) const { return _fields.value(key); } + void setField(const QString& key, const QByteArray& value) { if (_mode == 1 || _mode == 2) qFatal("setField called in bad state."); _fields.insert(key, value); _mode = 0; } + void setField(const QString& key, const QString& value) { setField(key, value.toUtf8()); } + // Convenience + void buildErrorMessage(const QString& error); + void buildErrorMessage(const char* error); +}; + +#endif /* NETWORKMESSAGE_H_ */ diff --git a/src/shared/settings.h b/src/shared/settings.h new file mode 100644 index 0000000..c1c45c1 --- /dev/null +++ b/src/shared/settings.h @@ -0,0 +1,12 @@ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#define CLIENT_PORT 5194 +static const QString CLIENT_PORT_STR(QString::number(CLIENT_PORT)); +static const QByteArray CLIENT_PORT_ARRAY(QString::number(CLIENT_PORT).toUtf8()); + +#define SERVICE_DISCOVERY_PORT 3492 + +#define PING_TIMEOUT_MS 30000 + +#endif diff --git a/src/shared/util.cpp b/src/shared/util.cpp new file mode 100644 index 0000000..d5c101e --- /dev/null +++ b/src/shared/util.cpp @@ -0,0 +1,22 @@ +/* + * util.cpp + * + * Created on: 30.01.2013 + * Author: sr + */ + +#include "util.h" +#include + +static QCryptographicHash sha1(QCryptographicHash::Sha1); + +QByteArray genSha1(const QByteArray* a, const QByteArray* b, const QByteArray* c, const QByteArray* d, const QByteArray* e) +{ + sha1.reset(); + sha1.addData(*a); + if (b) sha1.addData(*b); + if (c) sha1.addData(*c); + if (d) sha1.addData(*d); + if (e) sha1.addData(*e); + return sha1.result(); +} diff --git a/src/shared/util.h b/src/shared/util.h new file mode 100644 index 0000000..c2e5145 --- /dev/null +++ b/src/shared/util.h @@ -0,0 +1,15 @@ +/* + * util.h + * + * Created on: 30.01.2013 + * Author: sr + */ + +#ifndef UTIL_H_ +#define UTIL_H_ + +#include + +QByteArray genSha1(const QByteArray* a, const QByteArray* b = NULL, const QByteArray* c = NULL, const QByteArray* d = NULL, const QByteArray* e = NULL); + +#endif /* UTIL_H_ */ -- cgit v1.2.3-55-g7522