Change the way that packets are handled

Packets can now be cast to a union of structs that allows each part to be accessed individually. Still a work in progress!
merge-requests/2/head
Phil Taylor 2021-02-20 18:29:23 +00:00
rodzic 69db5e0efc
commit df2c76588d
4 zmienionych plików z 668 dodań i 365 usunięć

270
packettypes.h 100644
Wyświetl plik

@ -0,0 +1,270 @@
#ifndef PACKETTYPES_H
#define PACKETTYPES_H
#include <QObject>
#pragma pack(push, 1)
// Fixed Size Packets
#define CONTROL_SIZE 0x10
#define PING_SIZE 0x15
#define OPENCLOSE_SIZE 0x16
#define RETRANSMIT_SIZE 0x18
#define TOKEN_SIZE 0x40
#define STATUS_SIZE 0x50
#define LOGIN_RESPONSE_SIZE 0x60
#define LOGIN_SIZE 0x80
#define CONNINFO_SIZE 0x90
#define CAPABILITIES_SIZE 0xA8
// Variable size packets + payload
#define CIV_SIZE 0x15
#define TXAUDIO_SIZE 0x15
#define DATA_SIZE 0x15
// 0x10 length control packet (connect/disconnect/idle.)
typedef union control_packet {
struct {
quint32 len;
quint16 type;
quint16 seq;
quint32 sentid;
quint32 rcvdid;
};
char packet[CONTROL_SIZE];
} *control_packet_t;
// 0x15 length ping packet
typedef union ping_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char reply; // 0x10
union { // This contains differences between the send/receive packet
struct { // Ping
quint32 time; // 0x11
};
struct { // Send
quint16 datalen; // 0x11
quint16 sendseq; //0x13
};
};
};
char packet[PING_SIZE];
} *ping_packet_t, *data_packet_t, data_packet;
// 0x18 length txaudio packet
/* tx[0] = static_cast<quint8>(tx.length() & 0xff);
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
tx[23] = static_cast<quint8>(partial.length() & 0xff);*/
// 0x40 length token packet
typedef union token_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedc; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[16]; // 0x20
quint32 response; // 0x30
char unusede[12]; // 0x34
};
char packet[TOKEN_SIZE];
} *token_packet_t;
// 0x50 length login status packet
typedef union status_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedc; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[6]; // 0x20
quint16 unknown; // 0x26
char unusede; // 0x28
char unusedf[2]; // 0x29
char value[5]; // 0x2b
quint32 error; // 0x30
char unusedg[12]; // 0x34
char disc; // 0x40
char unusedh; // 0x41
quint32 civport; // 0x42 // Sent bigendian
quint32 audioport; // 0x46 // Sent bigendian
char unusedi[5]; // 0x4a
};
char packet[STATUS_SIZE];
} *status_packet_t;
// 0x60 length login status packet
typedef union login_response_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedc; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
quint16 authstartid; // 0x20
char unusedd[14]; // 0x22
quint32 error; // 0x30
char unusede[12]; // 0x34
char connection[16]; // 0x40
char unusedf[16]; // 0x50
};
char packet[LOGIN_RESPONSE_SIZE];
} *login_response_packet_t;
// 0x80 length login packet
typedef union login_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedb; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedc[32]; // 0x20
char username[16]; // 0x40
char password[16]; // 0x50
char name[16]; // 0x60
char unusedf[16]; // 0x70
};
char packet[LOGIN_SIZE];
} *login_packet_t;
// 0x90 length conninfo and stream request packet
typedef union conninfo_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedb; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
quint16 authstartid; // 0x20
char unusedd[5]; // 0x22
quint32 resb; // 0x27
char identa; // 0x2b
quint32 identb; // 0x2c
char unusedf[16]; // 0x30
char name[16]; // 0x40
char unusedg[16]; // 0x50
union { // This contains differences between the send/receive packet
struct { // Receive
quint32 ready; // 0x60
char computer[16]; // 0x64
char unusedi[16]; // 0x74
quint32 ipaddress; // 0x84
char unusedj[8]; // 0x78
};
struct { // Send
char ident[16]; // 0x60 // Not sure what this is?
char rxenable; // 0x70
char txenable; // 0x71
char rxcodec; // 0x72
char txcodec; // 0x73
quint32 rxsample; // 0x74
quint32 txsample; // 0x78
quint32 civport; // 0x7c
quint32 audioport; // 0x80
char unusedk; // 0x84
quint32 txbuffer; // 0x85
char unusedl[7]; // 0x89
};
};
};
char packet[CONNINFO_SIZE];
} *conninfo_packet_t;
// 0xA8 length capabilities packet
typedef union capabilities_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint16 innerseq; // 0x17
char unusedc; // 0x19
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[33]; // 0x20
char capa; // 0x41
char unusede[7]; // 0x42
quint16 capb; // 0x49
char unusedf[2]; // 0x4b
char capc; // 0x4d
quint32 capd; // 0x4e
char name[32]; // 0x52
char audio[32]; // 0x72
char caparray[10]; // 0x92
char unusedh[2]; // 0x9c
quint16 cape; // 0x9e
quint16 capf; // 0xa0
char unusedi; // 0xa2
quint16 capg; // 0xa3
char unusedj[3]; // 0xa5
};
char packet[CAPABILITIES_SIZE];
} *capabilities_packet_t;
#pragma pack(pop)
#endif // PACKETTYPES_H

Wyświetl plik

@ -112,212 +112,237 @@ void udpHandler::dataReceived()
while (udp->hasPendingDatagrams()) {
lastReceived = time(0);
QNetworkDatagram datagram = udp->receiveDatagram();
//qDebug() << "Received: " << datagram.data();
QByteArray r = datagram.data();
switch (r.length())
{
case (16):
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x04\x00\x00\x00")) {
// If timer is active, stop it as they are obviously there!
if (areYouThereTimer.isActive()) {
areYouThereTimer.stop();
// send ping packets every second
pingTimer.start(PING_PERIOD);
idleTimer.start(IDLE_PERIOD);
}
}
// This is "I am ready" in response to "Are you ready" so send login.
else if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
case (CONTROL_SIZE): // control packet
{
qDebug() << this->metaObject()->className() << ": Received I am ready";
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
sendLogin(); // second login packet
}
break;
case (21): // pkt7,
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00") && r[16] == (char)0x01 && serialAndAudioOpened)
{
// This is a response to our ping request so measure latency
latency += lastPingSentTime.msecsTo(QDateTime::currentDateTime());
latency /= 2;
quint32 totalsent = packetsSent;
quint32 totallost = packetsLost/2;
if (audio != Q_NULLPTR) {
totalsent = totalsent + audio->packetsSent;
totallost = totallost + audio->packetsLost/2;
}
if (civ != Q_NULLPTR) {
totalsent = totalsent + civ->packetsSent;
totallost = totallost + civ->packetsLost/2;
}
//double perclost = 1.0 * totallost / totalsent * 100.0 ;
emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms, loss: (" +QString::number(packetsLost)+ "/"+ QString::number(packetsSent) +")");
}
break;
case (64): // Response to Auth packet?
if (r.mid(0, 6) == QByteArrayLiteral("\x40\x00\x00\x00\x00\x00") && r[21] == (char)0x05) {
if (r.mid(0x30, 4) == QByteArrayLiteral("\x00\x00\x00\x00")) {
qDebug() << this->metaObject()->className() << ": Token renewal successful";
tokenTimer.start(TOKEN_RENEWAL);
gotAuthOK = true;
if (!serialAndAudioOpened)
{
sendRequestSerialAndAudio();
}
}
else if (r.mid(0x30, 4) == QByteArrayLiteral("\xff\xff\xff\xff"))
{
qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login";
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
isAuthenticated = false;
sendLogin(); // Try sending login packet (didn't seem to work?)
}
else
{
qWarning() << this->metaObject()->className() << ": Unknown response to token renewal? " << r.mid(0x30,4);
}
}
break;
case (80): // Status packet
if (r.mid(0, 6) == QByteArrayLiteral("\x50\x00\x00\x00\x00\x00"))
{
if (r.mid(48, 3) == QByteArrayLiteral("\xff\xff\xff"))
{
if (!serialAndAudioOpened)
{
emit haveNetworkError(radioIP.toString(), "Auth failed, try rebooting the radio.");
qDebug() << this->metaObject()->className() << ": Auth failed, try rebooting the radio.";
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x04) {
// If timer is active, stop it as they are obviously there!
if (areYouThereTimer.isActive()) {
areYouThereTimer.stop();
// send ping packets every second
pingTimer.start(PING_PERIOD);
idleTimer.start(IDLE_PERIOD);
}
}
if (r.mid(48, 3) == QByteArrayLiteral("\x00\x00\x00") && r[64] == (char)0x01)
// This is "I am ready" in response to "Are you ready" so send login.
else if (in->type == 0x06)
{
qDebug() << this->metaObject()->className() << ": Received I am ready";
sendLogin(); // send login packet
}
break;
}
case (PING_SIZE): // ping packet
{
ping_packet_t in = (ping_packet_t)r.constData();
if (in->type == 0x07 && in->reply == 0x01 && streamOpened)
{
// This is a response to our ping request so measure latency
latency += lastPingSentTime.msecsTo(QDateTime::currentDateTime());
latency /= 2;
quint32 totalsent = packetsSent;
quint32 totallost = packetsLost / 2;
if (audio != Q_NULLPTR) {
totalsent = totalsent + audio->packetsSent;
totallost = totallost + audio->packetsLost / 2;
}
if (civ != Q_NULLPTR) {
totalsent = totalsent + civ->packetsSent;
totallost = totallost + civ->packetsLost / 2;
}
//double perclost = 1.0 * totallost / totalsent * 100.0 ;
emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms, loss: (" + QString::number(packetsLost) + "/" + QString::number(packetsSent) + ")");
}
break;
}
case (TOKEN_SIZE): // Response to Token request
{
token_packet_t in = (token_packet_t)r.constData();
if (in->res == 0x05)
{
if (in->response == 0x0000)
{
qDebug() << this->metaObject()->className() << ": Token renewal successful";
tokenTimer.start(TOKEN_RENEWAL);
gotAuthOK = true;
if (!streamOpened)
{
sendRequestStream();
}
}
else if (in->response == 0xffffffff)
{
qWarning() << this->metaObject()->className() << ": Radio rejected token renewal, performing login";
remoteId = in->sentid;
isAuthenticated = false;
sendLogin(); // Try sending login packet (didn't seem to work?)
}
else
{
qWarning() << this->metaObject()->className() << ": Unknown response to token renewal? " << in->response;
}
}
break;
}
case (STATUS_SIZE): // Status packet
{
status_packet_t in = (status_packet_t)r.constData();
if (in->error == 0x00ffffff && !streamOpened)
{
emit haveNetworkError(radioIP.toString(), "Auth failed, try rebooting the radio.");
qDebug() << this->metaObject()->className() << ": Auth failed, try rebooting the radio.";
}
else if (in->error == 0x00000000 && in->disc == 0x01)
{
emit haveNetworkError(radioIP.toString(), "Got radio disconnected.");
qDebug() << this->metaObject()->className() << ": Got radio disconnected.";
}
break;
}
break;
case(96): // Response to Login packet.
if (r.mid(0, 6) == QByteArrayLiteral("\x60\x00\x00\x00\x00\x00"))
case(LOGIN_RESPONSE_SIZE): // Response to Login packet.
{
if (r.mid(48, 4) == QByteArrayLiteral("\xff\xff\xff\xfe"))
login_response_packet_t in = (login_response_packet_t)r.constData();
if (in->error == 0xfeffffff)
{
emit haveNetworkStatus("Invalid Username/Password");
qDebug() << this->metaObject()->className() << ": Invalid Username/Password";
}
else if (!isAuthenticated)
{
emit haveNetworkStatus("Radio Login OK!");
qDebug() << this->metaObject()->className() << ": Received Login OK";
authId = r.mid(0x1a, 6);
sendToken(0x02);
tokenTimer.start(TOKEN_RENEWAL); // Start token request timer
isAuthenticated = true;
if (in->tokrequest == tokRequest)
{
emit haveNetworkStatus("Radio Login OK!");
qDebug() << this->metaObject()->className() << ": Received matching token response to our request";
token = in->token;
sendToken(0x02);
tokenTimer.start(TOKEN_RENEWAL); // Start token request timer
isAuthenticated = true;
}
else
{
qDebug() << this->metaObject()->className() << ": Token response did not match, sent:" << tokRequest << " got " << in->tokrequest;
}
}
if (r.mid(0x40, 4) == QByteArrayLiteral("FTTH"))
if (!strcmp(in->connection, "FTTH"))
{
highBandwidthConnection = true;
}
qDebug() << this->metaObject()->className() << ": Detected connection speed " << QString::fromUtf8(parseNullTerminatedString(r, 0x40));
break;
}
break;
case (144):
if (!serialAndAudioOpened && r.mid(0, 6) == QByteArrayLiteral("\x90\x00\x00\x00\x00\x00") && r[0x60] == (char)0x00)
case (CONNINFO_SIZE):
{
devName = parseNullTerminatedString(r, 0x40);
QHostAddress ip = QHostAddress(qFromBigEndian<quint32>(r.mid(0x84, 4)));
if (!ip.isEqual(QHostAddress("0.0.0.0")) && parseNullTerminatedString(r, 0x64) != compName) // || ip != localIP ) // TODO: More testing of IP address detection code!
conninfo_packet_t in = (conninfo_packet_t)r.constData();
if (!streamOpened && !in->ready)
{
emit haveNetworkStatus(QString::fromUtf8(devName) + "in use by: " + QString::fromUtf8(parseNullTerminatedString(r, 0x64)) + " (" + ip.toString() + ")");
}
else {
emit haveNetworkStatus(QString::fromUtf8(devName) + " available");
sendRequestSerialAndAudio();
}
}
else if (!serialAndAudioOpened && r.mid(0, 6) == QByteArrayLiteral("\x90\x00\x00\x00\x00\x00") && r[0x60] == (char)0x01)
{
devName = parseNullTerminatedString(r, 0x40);
devName = parseNullTerminatedString(in->computer,0x00);
QHostAddress ip = QHostAddress(in->ipaddress);
if (!ip.isEqual(QHostAddress("0.0.0.0")) && parseNullTerminatedString(r, 0x64) != compName) // || ip != localIP ) // TODO: More testing of IP address detection code!
{
emit haveNetworkStatus(QString::fromUtf8(devName) + "in use by: " + QString::fromUtf8(parseNullTerminatedString(r, 0x64)) + " (" + ip.toString() + ")");
sendControl(false, 0x00, in->seq); // Respond with an idle
}
else {
emit haveNetworkStatus(QString::fromUtf8(devName) + " available");
identa = in->identa;
identb = in->identb;
sendRequestStream();
}
}
else if (!streamOpened && in->ready)
{
devName = parseNullTerminatedString(in->computer, 0);
civ = new udpCivData(localIP, radioIP, civPort);
audio = new udpAudio(localIP, radioIP, audioPort,rxBufferSize,rxSampleRate, rxCodec,txSampleRate,txCodec);
audio = new udpAudio(localIP, radioIP, audioPort, rxBufferSize, rxSampleRate, rxCodec, txSampleRate, txCodec);
QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray)));
QObject::connect(this, SIGNAL(haveChangeBufferSize(quint16)), audio, SLOT(changeBufferSize(quint16)));
serialAndAudioOpened = true;
streamOpened = true;
emit haveNetworkStatus(QString::fromUtf8(devName));
qDebug() << this->metaObject()->className() << "Got serial and audio request success, device name: " << QString::fromUtf8(devName);
// Stuff can change in the meantime because of a previous login...
remoteId = qFromLittleEndian<quint32>(r.mid(0x08, 4));
myId = qFromLittleEndian<quint32>(r.mid(0x0c, 4));
authId = r.mid(0x1a, 6);
// Is there already somebody connected to the radio?
remoteId = in->sentid;
myId = in->rcvdid;
tokRequest = in->tokrequest;
token = in->token;
}
break;
}
break;
case (168):
audioType = parseNullTerminatedString(r, 0x72);
devName = parseNullTerminatedString(r, 0x52);
replyId = r.mid(0x42, 16);
qDebug() << this->metaObject()->className() << "Received radio capabilities, Name:" <<
QString::fromUtf8(devName) << " Audio:" <<
QString::fromUtf8(audioType);
break;
case (CAPABILITIES_SIZE):
{
capabilities_packet_t in = (capabilities_packet_t)r.constData();
audioType = parseNullTerminatedString(in->audio, 0);
devName = parseNullTerminatedString(in->name, 0);
//replyId = r.mid(0x42, 16);
qDebug() << this->metaObject()->className() << "Received radio capabilities, Name:" <<
QString::fromUtf8(devName) << " Audio:" <<
QString::fromUtf8(audioType);
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
datagram.clear();
}
return;
}
#define SERIALAUDIO_SIZE 0x90
void udpHandler::sendRequestSerialAndAudio()
void udpHandler::sendRequestStream()
{
QByteArray usernameEncoded;
passcode(username, usernameEncoded);
int txSeqBufLengthMs = 50;
int txSeqBufLengthMs = 300;
quint8 p[SERIALAUDIO_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian(sizeof(p), p + 0x00);
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
memcpy(p + 0x13, QByteArrayLiteral("\x80\x01\x03").constData(), 3);
qToLittleEndian(authInnerSendSeq, p + 0x16);
memcpy(p + 0x1a, authId.constData(), authId.length());
memcpy(p + 0x20, replyId.constData(), replyId.length());
memcpy(p + 0x40, devName.constData(), devName.length());
memcpy(p + 0x60, usernameEncoded.constData(), usernameEncoded.length());
memcpy(p + 0x70, QByteArrayLiteral("\x01\x01").constData(), 2);
p[0x72] = rxCodec;
p[0x73] = txCodec;
qToBigEndian(rxSampleRate, p + 0x76);
qToBigEndian(txSampleRate, p + 0x7a);
qToBigEndian(civPort, p + 0x7e);
qToBigEndian(audioPort, p + 0x82);
qToBigEndian(txSeqBufLengthMs, p + 0x86);
p[0x88] = 0x01;
conninfo_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.code = 0x0180;
p.res = 0x03;
p.resb = 0x8010;
p.identa = identa;
p.identb = identb;
p.innerseq = authInnerSendSeq;
p.tokrequest = tokRequest;
p.token = token;
memcpy(&p.name, devName.constData(), devName.length());
p.rxenable = 1;
p.txenable = 1;
p.rxcodec = rxCodec;
p.txcodec = txCodec;
memcpy(&p.ident, QByteArrayLiteral("&96D7").constData(), 5);
p.rxsample = qToBigEndian((quint32)rxSampleRate);
p.txsample = qToBigEndian((quint32)txSampleRate);
p.civport = qToBigEndian((quint32)civPort);
p.audioport = qToBigEndian((quint32)audioPort);
p.txbuffer = qToBigEndian((quint32)txSeqBufLengthMs);
authInnerSendSeq++;
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
return;
}
@ -334,53 +359,52 @@ void udpHandler::sendAreYouThere()
udpBase::sendControl(false,0x03,0x00);
}
#define LOGIN_SIZE 0x80
void udpHandler::sendLogin() // Only used on control stream.
{
qDebug() << this->metaObject()->className() << ": Sending login packet";
uint16_t authStartID = rand() | rand() << 8;
//uint16_t authStartID = 0x7866;
tokRequest = static_cast<quint16>(rand() | rand() << 8); // Generate random token request.
QByteArray usernameEncoded;
QByteArray passwordEncoded;
passcode(username, usernameEncoded);
passcode(password, passwordEncoded);
quint8 p[LOGIN_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian(sizeof(p), p + 0x00);
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
memcpy(p + 0x13, QByteArrayLiteral("\x70\x01").constData(), 2);
qToLittleEndian(authInnerSendSeq, p + 0x17);
qToLittleEndian(authStartID, p + 0x20);
memcpy(p + 0x40, usernameEncoded.constData(), usernameEncoded.length());
memcpy(p + 0x50, passwordEncoded.constData(), passwordEncoded.length());
memcpy(p + 0x60, compName.constData(), compName.length());
login_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.code = 0x0170; // Not sure what this is?
p.innerseq = authInnerSendSeq;
p.tokrequest = tokRequest;
memcpy(p.username, usernameEncoded.constData(), usernameEncoded.length());
memcpy(p.password, passwordEncoded.constData(), passwordEncoded.length());
memcpy(p.name, compName.constData(), compName.length());
authInnerSendSeq++;
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
return;
}
#define TOKEN_SIZE 0x40
void udpHandler::sendToken(uint8_t magic)
{
qDebug() << this->metaObject()->className() << "Sending Token request: " << magic;
quint8 p[TOKEN_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian(sizeof(p), p + 0x00);
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
memcpy(p + 0x13, QByteArrayLiteral("\x30\x01").constData(), 2);
qToLittleEndian(magic, p + 0x15);
qToLittleEndian(authInnerSendSeq, p + 0x17);
memcpy(p + 0x1a, authId.constData(), authId.length());
token_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.code = 0x0130; // Not sure what this is?
p.res = magic;
p.innerseq = authInnerSendSeq;
p.tokrequest = tokRequest;
p.token = token;
authInnerSendSeq++;
sendTrackedPacket(QByteArray::fromRawData((const char *)p, sizeof(p)));
sendTrackedPacket(QByteArray::fromRawData((const char *)p.packet, sizeof(p)));
tokenTimer.start(100); // Set 100ms timer for retry (this will be cancelled if a response is received)
return;
}
@ -416,21 +440,19 @@ udpCivData::~udpCivData() {
sendOpenClose(true);
}
#define CIV_SIZE 0x15
void udpCivData::send(QByteArray d)
{
// qDebug() << "Sending: (" << d.length() << ") " << d;
data_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.reply = (char)0xc1;
p.datalen = d.length();
p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN!
quint8 p[CIV_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian((quint16)sizeof(p) + d.length(), p + 0x00);
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
p[0x10] = 0xc1;
qToLittleEndian((quint16)d.length(), p + 0x11);
qToBigEndian(sendSeqB, p + 0x13); // THIS IS BIG ENDIAN!
QByteArray t = QByteArray::fromRawData((const char*)p, sizeof(p));
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d);
sendTrackedPacket(t);
sendSeqB++;
@ -438,7 +460,6 @@ void udpCivData::send(QByteArray d)
}
#define OPENCLOSE_SIZE 0x16
void udpCivData::sendOpenClose(bool close)
{
uint8_t magic = 0x05;
@ -467,45 +488,49 @@ void udpCivData::sendOpenClose(bool close)
void udpCivData::dataReceived()
{
while (udp->hasPendingDatagrams()) {
while (udp->hasPendingDatagrams())
{
QNetworkDatagram datagram = udp->receiveDatagram();
//qDebug() << "Received: " << datagram.data();
QByteArray r = datagram.data();
switch (r.length())
{
case (16): // Response to pkt0
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
case (CONTROL_SIZE): // Control packet
{
// Update remoteId
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
sendOpenClose(false);
}
break;
default:
if (r.length() > 21) {
// First check if we are missing any packets?
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
if (lastReceivedSeq == 0 || lastReceivedSeq > gotSeq) {
lastReceivedSeq = gotSeq;
}
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
// Do we need to request a retransmit?
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
}
lastReceivedSeq = gotSeq;
quint8 temp = r[0] - 0x15;
if ((quint8)r[16] == 0xc1 && (quint8)r[17] == temp)
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x06)
{
emit receive(r.mid(0x15));
}
// Update remoteId
remoteId = in->sentid;
sendOpenClose(false);
}
}
break;
default:
{
if (r.length() > 21) {
// First check if we are missing any packets?
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
if (lastReceivedSeq == 0 || lastReceivedSeq > gotSeq) {
lastReceivedSeq = gotSeq;
}
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
// Do we need to request a retransmit?
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
}
lastReceivedSeq = gotSeq;
quint8 temp = r[0] - 0x15;
if ((quint8)r[16] == 0xc1 && (quint8)r[17] == temp)
{
emit receive(r.mid(0x15));
}
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
@ -617,29 +642,25 @@ udpAudio::~udpAudio()
#define TXAUDIO_SIZE 0x18
void udpAudio::sendTxAudio()
{
quint8 p[TXAUDIO_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
p[0x10] = 0x80;
if (txaudio->chunkAvailable) {
QByteArray audio;
txaudio->getNextAudioChunk(audio);
int counter = 0;
while (counter < audio.length()) {
QByteArray tx = QByteArray::fromRawData((const char*)p, sizeof(p));
QByteArray partial = audio.mid(counter, 1364);
ping_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.reply = (char)0x80;
p.datalen = partial.length();
p.sendseq = qToBigEndian(sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial);
tx[0] = static_cast<quint8>(tx.length() & 0xff);
tx[1] = static_cast<quint8>(tx.length() >> 8 & 0xff);
tx[18] = static_cast<quint8>(sendAudioSeq >> 8 & 0xff);
tx[19] = static_cast<quint8>(sendAudioSeq & 0xff);
tx[22] = static_cast<quint8>(partial.length() >> 8 & 0xff);
tx[23] = static_cast<quint8>(partial.length() & 0xff);
counter = counter + partial.length();
//qDebug() << "Sending audio packet length: " << tx.length();
sendTrackedPacket(tx);
@ -664,10 +685,11 @@ void udpAudio::dataReceived()
switch (r.length())
{
case (16): // Response to idle packet handled in udpBase
case (16): // Response to control packet handled in udpBase
break;
default:
{
/* Audio packets start as follows:
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
@ -675,7 +697,7 @@ void udpAudio::dataReceived()
*/
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
r.mid(0, 2) == QByteArrayLiteral("\x44\x02") ||
r.mid(0, 2) == QByteArrayLiteral("\xd8\x03") ||
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
@ -686,7 +708,7 @@ void udpAudio::dataReceived()
lastReceivedSeq = gotSeq;
}
for (uint16_t f = lastReceivedSeq+1 ; f < gotSeq; f++) {
for (uint16_t f = lastReceivedSeq + 1; f < gotSeq; f++) {
// Do we need to request a retransmit?
qDebug() << this->metaObject()->className() << ": Missing Sequence: (" << r.length() << ") " << f;
}
@ -697,6 +719,7 @@ void udpAudio::dataReceived()
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
@ -708,6 +731,7 @@ void udpAudio::dataReceived()
void udpBase::init()
{
timeStarted.start();
udp = new QUdpSocket(this);
udp->bind(); // Bind to random port.
localPort = udp->localPort();
@ -740,138 +764,142 @@ void udpBase::dataReceived(QByteArray r)
{
switch (r.length())
{
case (16): // Empty response used for simple comms and retransmit requests.
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x04\x00\x00\x00")) {
// If timer is active, stop it as they are obviously there!
qDebug() << this->metaObject()->className() << ": Received I am here";
areYouThereCounter = 0;
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
remoteId = qFromLittleEndian<quint32>(r.mid(8, 4));
sendControl(false,0x06,0x01); // Send Are you ready - untracked.
}
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x00\x00")) {
// Just get the seqnum and ignore the rest.
lastReceivedSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
}
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x01\x00")) {
// retransmit request
// Send an idle with the requested seqnum if not found.
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
bool found=false;
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests.
{
control_packet_t in = (control_packet_t)r.constData();
// We should check for missing packets here
// for now just store received seq.
lastReceivedSeq = in->seq;
if (in->type == 0x04) {
qDebug() << this->metaObject()->className() << ": Received I am here";
areYouThereCounter = 0;
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
remoteId = in->sentid;
sendControl(false, 0x06, 0x01); // Send Are you ready - untracked.
}
else if (in->type == 0x06)
{
packetsLost++;
if (txSeqBuf[f].seqNum == gotSeq) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker(&mutex);
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
found = true;
break;
// Just get the seqnum and ignore the rest.
}
else if (in->type == 0x01) // retransmit request
{
// retransmit request
// Send an idle with the requested seqnum if not found.
bool found = false;
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
{
packetsLost++;
if (txSeqBuf[f].seqNum == in->seq) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker(&mutex);
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
found = true;
break;
}
}
if (!found)
{
// Packet was not found in buffer
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl(false, 0, in->seq);
}
}
if (!found)
{
// Packet was not found in buffer
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl(false, 0, gotSeq);
}
}
else if (r.mid(0, 6) == QByteArrayLiteral("\x18\x00\x00\x00\x01\x00"))
{ // retransmit range request, can contain multiple ranges.
for (int f = 16; f < r.length() - 4; f = f + 4)
case (PING_SIZE): // ping packet
{
ping_packet_t in = (ping_packet_t)r.constData();
if (in->type == 0x07)
{
quint16 start = qFromLittleEndian<quint16>(r.mid(f, 2));
quint16 end = qFromLittleEndian<quint16>(r.mid(f + 2, 2));
packetsLost=packetsLost + (end-start);
qDebug() << this->metaObject()->className() << ": Retransmit range request for:" << start << " to " << end;
for (quint16 gotSeq = start; gotSeq <= end; gotSeq++)
{
bool found=false;
for (int h = txSeqBuf.length() - 1; h >= 0; h--)
if (txSeqBuf[h].seqNum == gotSeq) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker(&mutex);
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
found = true;
break;
}
if (!found)
// It is a ping request/response
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
if (in->reply == 0x00)
{
ping_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = 0x07;
p.sentid = myId;
p.rcvdid = remoteId;
p.reply = 0x01;
p.seq = in->seq;
p.time = in->time;
QMutexLocker locker(&mutex);
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
}
else if (r[0x10] == (char)0x01) {
if (in->seq == pingSendSeq)
{
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl(false, 0, gotSeq);
// This is response to OUR request so increment counter
pingSendSeq++;
}
else {
// Not sure what to do here, need to spend more time with the protocol but try sending ping with same seq next time?
//qDebug() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << gotSeq;
}
}
else {
qDebug() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
}
}
break;
}
case (0x18):
{
if (r.mid(0, 6) == QByteArrayLiteral("\x18\x00\x00\x00\x01\x00"))
{ // retransmit range request, can contain multiple ranges.
for (int f = 16; f < r.length() - 4; f = f + 4)
{
quint16 start = qFromLittleEndian<quint16>(r.mid(f, 2));
quint16 end = qFromLittleEndian<quint16>(r.mid(f + 2, 2));
packetsLost = packetsLost + (end - start);
qDebug() << this->metaObject()->className() << ": Retransmit range request for:" << start << " to " << end;
for (quint16 gotSeq = start; gotSeq <= end; gotSeq++)
{
bool found = false;
for (int h = txSeqBuf.length() - 1; h >= 0; h--)
if (txSeqBuf[h].seqNum == gotSeq) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker(&mutex);
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
found = true;
break;
}
if (!found)
{
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl(false, 0, gotSeq);
}
}
}
}
break;
}
break;
case (21):
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00"))
{
// It is a ping request/response
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
if (r[0x10] == (char)0x00)
{
quint8 p[0x15];
memset(p, 0x0, sizeof(p));
qToLittleEndian(sizeof(p), p + 0x00);
p[0x04] = 0x07;
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
p[0x10] = (char)0x01;
p[0x11] = (char)r[0x11];
p[0x12] = (char)r[0x12];
p[0x13] = (char)r[0x13];
p[0x14] = (char)r[0x14];
QMutexLocker locker(&mutex);
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
}
else if (r[0x10] == (char)0x01) {
if (gotSeq == pingSendSeq)
{
// This is response to OUR request so increment counter
pingSendSeq++;
}
else {
// Not sure what to do here, need to spend more time with the protocol but try sending ping with same seq next time?
//qDebug() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << gotSeq;
}
}
else {
qDebug() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
}
}
break;
default:
break;
default:
break;
}
}
#define CONTROL_SIZE 0x10
// Used to send idle and other "control" style messages
void udpBase::sendControl(bool tracked=true, quint8 id=0, quint16 seq=0)
void udpBase::sendControl(bool tracked=true, quint8 type=0, quint16 seq=0)
{
quint8 p[CONTROL_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian((quint16)sizeof(p), p + 0x00);
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
p[0x04] = id;
control_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = type;
p.sentid = myId;
p.rcvdid = remoteId;
lastControlPacketSentTime = QDateTime::currentDateTime(); // Is this used?
if (!tracked) {
qToLittleEndian(seq, p + 0x06);
p.seq = seq;
QMutexLocker locker(&mutex);
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
}
else {
sendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
}
if (idleTimer.isActive()) {
idleTimer.start(IDLE_PERIOD); // Reset idle counter if it's running
@ -879,22 +907,20 @@ void udpBase::sendControl(bool tracked=true, quint8 id=0, quint16 seq=0)
return;
}
#define PING_SIZE 0x15
// Send periodic ping packets
void udpBase::sendPing()
{
quint32 pingSeq = (quint32)((quint8)(rand() & 0xff)) | (quint16)innerSendSeq << 8 | (quint8)0x06 << 24;
quint8 p[PING_SIZE];
memset(p, 0x0, sizeof(p));
qToLittleEndian((quint16)sizeof(p), p + 0x00);
p[0x04] = 0x07;
qToLittleEndian(myId, p + 0x08);
qToLittleEndian(remoteId, p + 0x0c);
qToLittleEndian(pingSeq, p + 0x11);
ping_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = 0x07;
p.sentid = myId;
p.rcvdid = remoteId;
p.seq = pingSendSeq;
p.time = timeStarted.msecsSinceStartOfDay();
lastPingSentTime = QDateTime::currentDateTime();
QMutexLocker locker(&mutex);
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
innerSendSeq++;
return;
}

Wyświetl plik

@ -21,6 +21,7 @@
#include <QDebug>
#include "audiohandler.h"
#include "packettypes.h"
#define PURGE_SECONDS 5
#define TOKEN_RENEWAL 60000
@ -46,6 +47,8 @@ public:
void sendPing(); // Periodic type 0x07 ping packet sending
void sendControl(bool tracked,quint8 id, quint16 seq);
QTime timeStarted;
QUdpSocket* udp=Q_NULLPTR;
uint32_t myId = 0;
uint32_t remoteId = 0;
@ -89,8 +92,6 @@ public:
QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0;
QDateTime lastControlPacketSentTime;
quint16 areYouThereCounter=0;
quint32 packetsSent=0;
@ -184,7 +185,7 @@ public:
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec);
~udpHandler();
bool serialAndAudioOpened = false;
bool streamOpened = false;
udpCivData* civ = Q_NULLPTR;
udpAudio* audio = Q_NULLPTR;
@ -203,12 +204,13 @@ signals:
void haveChangeBufferSize(quint16 value);
private:
void sendAreYouThere();
void dataReceived();
void sendRequestSerialAndAudio();
void sendRequestStream();
void sendLogin();
void sendToken(uint8_t magic);
@ -236,8 +238,12 @@ private:
QByteArray compName;
QByteArray audioType;
QByteArray replyId;
QByteArray authId;
quint16 tokRequest;
quint32 token;
// These are for stream ident info.
char identa;
quint32 identb;
QTimer tokenTimer;
QTimer areYouThereTimer;

Wyświetl plik

@ -98,7 +98,8 @@ HEADERS += wfmain.h \
calibrationwindow.h \
satellitesetup.h \
udpserversetup.h \
udpserver.h
udpserver.h \
packettypes.h
FORMS += wfmain.ui \