#9 add message crc checks, fix check calculation

pull/32/head
Geoffrey Merck 4 years ago
parent 0cd4d171c5
commit 18bb1f365b

@ -16,6 +16,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <boost/algorithm/string.hpp>
#include "RSMS1AMessageBuilder.h"
#include "StringUtils.h"
@ -25,14 +27,14 @@ void CRSMS1AMessageBuilder::buildMessage(std::string& message, const std::string
{
auto bodyCrc = calculateBodyCRC(body);
std::string bodyTmp;
escapeBody(bodyTmp, body + bodyCrc);
escapeBody(bodyTmp, body + (char)bodyCrc);
std::string header = CStringUtils::string_format("%s,%s,0011", sender.c_str(), recipient.c_str());
char c1, c2;
calcMsgIcomCRC(header, c1, c2);
header.push_back(c1);
header.push_back(c2);
message = "$$Msg," + header + bodyTmp + '\n';
message = "$$Msg," + header + bodyTmp + '\r';
}
char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body)
@ -40,12 +42,16 @@ char CRSMS1AMessageBuilder::calculateBodyCRC(const std::string& body)
if(body.length() == 1)
return body[0];
int num = 0;
unsigned int num = 0;
for(auto c : body) {
num += c;
}
return (char)((num & 255) - 128);
auto res = (num & 255);
if(res >= 128)
res -= 128;
return (char)res;
}
void CRSMS1AMessageBuilder::escapeBody(std::string& output, const std::string& body)
@ -82,3 +88,70 @@ char CRSMS1AMessageBuilder::doWhatever(char b2) {
}
return (char) i;
}
RSMS1A_PARSE_STATUS CRSMS1AMessageBuilder::parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message)
{
sender.clear();
recipient.clear();
body.clear();
if(!boost::starts_with(message, "$$Msg,"))
return RSMS_FAIL;
auto firstCommaPos = message.find_first_of(',');
if(firstCommaPos == std::string::npos || (firstCommaPos + 1) >= message.length())
return RSMS_FAIL;
auto secondCommaPos = message.find_first_of(',', firstCommaPos + 1);
if(secondCommaPos == std::string::npos || (secondCommaPos + 1) >= message.length())
return RSMS_FAIL;
auto thirdCommaPos = message.find_first_of(',', secondCommaPos + 1);
if(thirdCommaPos == std::string::npos || (thirdCommaPos + 1 + 6) >= message.length())
return RSMS_FAIL;
sender.assign(message.substr(firstCommaPos + 1, secondCommaPos - firstCommaPos - 1));
recipient.assign(message.substr(secondCommaPos + 1, thirdCommaPos - secondCommaPos - 1));
body.assign(message.substr(thirdCommaPos + 1 + 6));
unescapeBody(body, std::string(body));
if(body.length() >= 2U) body.resize(body.length() - 2U);
// build a message out of what we received in order to check if all checksums matches
// use only header of message to match checksum
std::string messagecheck;
buildMessage(messagecheck, sender, recipient, " ");
messagecheck.resize(messagecheck.length() - 3U);// get rid of body, body check byte and trailing \r
if(messagecheck != message.substr(0, messagecheck.length())) {
sender.clear();
recipient.clear();
body.clear();
return RSMS_FAIL; // we do not allow any errors in message header
}
//rebuild complete messsage with body
buildMessage(messagecheck, sender, recipient, body);
if(messagecheck != message)
return RSMS_ERRONEOUS_MSG; // we allow erros to occur in the message body
return RSMS_OK;
}
void CRSMS1AMessageBuilder::unescapeBody(std::string& output, const std::string& body)
{
output.clear();
if(body.empty())
return;
for(unsigned int i = 0U; i < body.length(); i++) {
auto c = body[i];
auto next = body[i + 1U];
if(c == 'o' && std::find(m_charsToEscape.begin(), m_charsToEscape.end(), next) != m_charsToEscape.end()) {
output.push_back(next);
i++;
continue;
}
output.push_back(c);
}
}

@ -21,14 +21,22 @@
#include <string>
#include <vector>
enum RSMS1A_PARSE_STATUS {
RSMS_FAIL,
RSMS_ERRONEOUS_MSG,
RSMS_OK
};
class CRSMS1AMessageBuilder
{
public:
static void buildMessage(std::string& message, const std::string& sender, const std::string& recipient, const std::string body);
static RSMS1A_PARSE_STATUS parseMessage(std::string& sender, std::string& recipient, std::string& body, const std::string& message);
private:
static void calcMsgIcomCRC(const std::string& msg, char& c1, char& c2);
static void escapeBody(std::string& output, const std::string& body);
static void unescapeBody(std::string& output, const std::string& body);
static void escapeBytes(std::vector<char> output, const std::vector<char> input);
static char calculateBodyCRC(const std::string& body);
static char doWhatever(char b2);

@ -30,6 +30,7 @@
#include "Log.h"
#include "Utils.h"
#include "APRSUtils.h"
#include "RSMS1AMessageBuilder.h"
const unsigned int APRS_CSUM_LENGTH = 4U;
@ -47,39 +48,8 @@ bool CRSMS1AMessageCollector::isValidSentence(const std::string& sentence)
bool CRSMS1AMessageCollector::isValidMsg(const std::string& msg)
{
if(msg.empty() || !boost::starts_with(msg, "$$Msg"))
return false;
// CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length());
std::vector<std::string> splits;
boost::split(splits, msg, boost::is_any_of(","));
bool ret = splits.size() >= 4
&& !splits[1].empty()
&& !splits[2].empty();
if(ret) {
CUtils::dump("RS-MS1A:", (unsigned char *)msg.c_str(), msg.length() + 1U);
CLog::logDebug("RS-MS1A: %s", msg.c_str());
}
return ret;
//TODO 2022-01-01 figure out what the heck it is about thic strange CRCs
// CUtils::dump("RS-MS1A:", (unsigned char *)gpsa.c_str(), gpsa.length() + 1U);
// CLog::logDebug("RS-MS1A: %s", gpsa.c_str());
// auto thirdCommaPos = CStringUtils::find_nth(gpsa, 0U, ',', 3);
// auto csum = calcCRC(gpsa, thirdCommaPos + 6 + 1, gpsa.length() - thirdCommaPos - 2U - 6U);
// auto csumStr = CStringUtils::string_format("%06X", csum);
// CLog::logDebug("RS-MS1A CRC: %s", csumStr.c_str());
// auto expectedCsum = gpsa.substr(5U, APRS_CSUM_LENGTH);
// bool res = ::strcasecmp(csumStr.c_str(), expectedCsum.c_str()) == 0;
// return res;
// checking validity involves parsing, so we do minumum checks here. Big chekc done in getDataInt
return !msg.empty() && boost::starts_with(msg, "$$Msg");
}
unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned int length)
@ -87,40 +57,18 @@ unsigned int CRSMS1AMessageCollector::getDataInt(unsigned char * data, unsigned
if(data == nullptr || length == 0U || getSentence().empty())
return 0U;
auto sentence = getSentence();
std::string sender, recipient, body, sentence;
sentence = getSentence();
auto parseRes = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, sentence);
std::vector<std::string> splits;
boost::split(splits, sentence, boost::is_any_of(","));
CUtils::dump(CStringUtils::string_format("RS-MS1A Message parsed with result %s", parseRes == RSMS_OK ? "OK" : (parseRes == RSMS_ERRONEOUS_MSG ? "Error in msg body" : "failed")).c_str(),
(unsigned char *)sentence.c_str(), sentence.length());
bool ret = splits.size() >= 4
&& !splits[1].empty()
&& !splits[2].empty();
if(!ret) {
if(parseRes == RSMS_FAIL)
return 0U;
}
auto sender = splits[1];
auto recipient = CUtils::ToUpper(splits[2]);
for(unsigned int i = 0;i < 3; i++) {
splits.erase(splits.begin());
}
auto message = boost::join(splits, ",");
if(message.length() > 6)
message = message.substr(6);
auto seqNum = rand() % 0xFFFFFU;
CAPRSUtils::dstarCallsignToAPRS(sender);
CAPRSUtils::dstarCallsignToAPRS(recipient);
recipient.resize(9, ' ');
message.resize(std::max<int>(0 , ((int)message.length()) - 2));
//unescape commas in message body
boost::replace_all(message, "o,", ",");
auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X\r\n", sender.c_str(), recipient.c_str(), message.c_str(), seqNum);
auto aprsFrame = CStringUtils::string_format("%s-5>APDPRS,DSTAR*::%s:%s{%05X", sender.c_str(), recipient.c_str(), body.c_str(), seqNum);
auto aprsFrameLen = aprsFrame.length();

@ -32,7 +32,7 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "ABC");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118ABCF\r");
}
TEST_F(RSMS1AMessageBuilder_buildMessage, A)
@ -40,7 +40,7 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "A");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\r");
}
TEST_F(RSMS1AMessageBuilder_buildMessage, AA)
@ -48,7 +48,7 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "AA");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118AA\02\r");
}
TEST_F(RSMS1AMessageBuilder_buildMessage, SalutCommentVasTu)
@ -56,7 +56,7 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "Salut, comment vas tu?");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r");
}
TEST_F(RSMS1AMessageBuilder_buildMessage, escapeComma)
@ -64,7 +64,7 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", ",");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118o,o,\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118o,o,\r");
}
TEST_F(RSMS1AMessageBuilder_buildMessage, INeedMoreDollars)
@ -72,6 +72,15 @@ namespace RSMS1AMessageBuilder
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL", "I need more $$$$");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118I need more o$o$o$o$\x08\n");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL,001118I need more o$o$o$o$\x08\r");
}
//"$$Msg,KC3FRA,F4FXL 7,00116Fhello\024\r"
TEST_F(RSMS1AMessageBuilder_buildMessage, hello)
{
std::string message;
CRSMS1AMessageBuilder::buildMessage(message, "KC3FRA", "F4FXL 7", "hello");
EXPECT_STREQ(message.c_str(), "$$Msg,KC3FRA,F4FXL 7,00116Fhello\024\r");
}
};

@ -0,0 +1,67 @@
/*
* Copyright (C) 2021-2022 by Geoffrey Merck F4FXL / KC3FRA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <gtest/gtest.h>
#include "../../RSMS1AMessageBuilder.h"
namespace RSMS1AMessageBuilder
{
class RSMS1AMessageBuilder_parseMessage : public ::testing::Test {
};
TEST_F(RSMS1AMessageBuilder_parseMessage, NoError)
{
std::string message = "$$Msg,KC3FRA,F4FXL,001118Saluto, comment vas tu?z\r";
std::string sender, recipient, body;
auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message);
EXPECT_EQ(ret, RSMS_OK);
EXPECT_STREQ(sender.c_str(), "KC3FRA");
EXPECT_STREQ(recipient.c_str(), "F4FXL");
EXPECT_STREQ(body.c_str(), "Salut, comment vas tu?");
}
TEST_F(RSMS1AMessageBuilder_parseMessage, ErrorInMessage)
{
std::string message = "$$Msg,KC3FRA,F4FXL,001118Saluto, comlent vas tu?z\r";
std::string sender, recipient, body;
auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message);
EXPECT_EQ(ret, RSMS_ERRONEOUS_MSG);
EXPECT_STREQ(sender.c_str(), "KC3FRA");
EXPECT_STREQ(recipient.c_str(), "F4FXL");
EXPECT_STREQ(body.c_str(), "Salut, comlent vas tu?");
}
TEST_F(RSMS1AMessageBuilder_parseMessage, ErrorInHeader)
{
std::string message = "$$Msg,KC3FRB,F4FXL,001118Saluto, comment vas tu?z\r";
std::string sender, recipient, body;
auto ret = CRSMS1AMessageBuilder::parseMessage(sender, recipient, body, message);
EXPECT_EQ(ret, RSMS_FAIL);
EXPECT_STREQ(sender.c_str(), "");
EXPECT_STREQ(recipient.c_str(), "");
EXPECT_STREQ(body.c_str(), "");
}
};
Loading…
Cancel
Save

Powered by TurnKey Linux.