diff --git a/packettypes.h b/packettypes.h new file mode 100644 index 0000000..703d81d --- /dev/null +++ b/packettypes.h @@ -0,0 +1,270 @@ +#ifndef PACKETTYPES_H +#define PACKETTYPES_H +#include + +#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(tx.length() & 0xff); + tx[1] = static_cast(tx.length() >> 8 & 0xff); + tx[18] = static_cast(sendAudioSeq >> 8 & 0xff); + tx[19] = static_cast(sendAudioSeq & 0xff); + tx[22] = static_cast(partial.length() >> 8 & 0xff); + tx[23] = static_cast(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 diff --git a/udphandler.cpp b/udphandler.cpp index 6c59b17..f9015cd 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -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(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(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(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(r.mid(0x08, 4)); - myId = qFromLittleEndian(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(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(r.mid(8, 4)); - sendOpenClose(false); - } - break; - default: - if (r.length() > 21) { - // First check if we are missing any packets? - uint16_t gotSeq = qFromLittleEndian(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(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(tx.length() & 0xff); - tx[1] = static_cast(tx.length() >> 8 & 0xff); - tx[18] = static_cast(sendAudioSeq >> 8 & 0xff); - tx[19] = static_cast(sendAudioSeq & 0xff); - tx[22] = static_cast(partial.length() >> 8 & 0xff); - tx[23] = static_cast(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(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(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(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(r.mid(f, 2)); - quint16 end = qFromLittleEndian(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(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(r.mid(f, 2)); + quint16 end = qFromLittleEndian(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(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; } diff --git a/udphandler.h b/udphandler.h index 5b04417..b096d77 100644 --- a/udphandler.h +++ b/udphandler.h @@ -21,6 +21,7 @@ #include #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; diff --git a/wfview.pro b/wfview.pro index 42cab9d..9d3c63e 100644 --- a/wfview.pro +++ b/wfview.pro @@ -98,7 +98,8 @@ HEADERS += wfmain.h \ calibrationwindow.h \ satellitesetup.h \ udpserversetup.h \ - udpserver.h + udpserver.h \ + packettypes.h FORMS += wfmain.ui \