Issue 84. IGate was truncating packets that contained nul character

in the information/comment part.
This commit is contained in:
WB2OSZ 2017-03-05 15:15:33 -05:00
parent 785c8deffa
commit b6254da203
5 changed files with 300 additions and 96 deletions

View File

@ -2,6 +2,23 @@
# Revision History #
## Version 1.4 -- Development snapshot H -- March 2017 ##
**This is beta test quality. If no significant issues are reported this will be the version 1.4 release.**
### New Features: ###
- Take advantage of new 'gpio' group and new /sys/class/gpio ownership in Raspbian Jessie.
- Handle more complicated gpio naming for CubieBoard, etc.
### Bugs Fixed: ###
- IGate did not retain nul characters in the information part of a packet. This should never happen with a valid APRS packet but there are a couple cases where it has. If we encounter these malformed packets, pass them along as-is, rather than truncating.
- Don't digipeat packets when the source is my call.
----------
## Version 1.4 -- Development snapshot G -- January 2017 ##

View File

@ -335,9 +335,18 @@ void ax25_delete (packet_t this_p)
* Purpose: Parse a frame in human-readable monitoring format and change
* to internal representation.
*
* Input: monitor - "TNC-2" format of a monitored packet. i.e.
* Input: monitor - "TNC-2" monitor format for packet. i.e.
* source>dest[,repeater1,repeater2,...]:information
*
* The information part can have non-printable characters
* in the form of <0xff>. This will be converted to single
* bytes. e.g. <0x0d> is carriage return.
* In version 1.4H we will allow nul characters which means
* we have to maintain a length rather than using strlen().
* I maintain that it violates the spec but want to handle it
* because it does happen and we want to preserve it when
* acting as an IGate rather than corrupting it.
*
* strict - True to enforce rules for packets sent over the air.
* False to be more lenient for packets from IGate server.
*
@ -369,17 +378,11 @@ packet_t ax25_from_text (char *monitor, int strict)
char *pa;
char *saveptr; /* Used with strtok_r because strtok is not thread safe. */
static int first_time = 1;
static regex_t unhex_re;
int e;
char emsg[100];
#define MAXMATCH 1
regmatch_t match[MAXMATCH];
int keep_going;
char temp[512];
int ssid_temp, heard_temp;
char atemp[AX25_MAX_ADDR_LEN];
char info_part[AX25_MAX_INFO_LEN+1];
int info_len;
packet_t this_p = ax25_new ();
@ -392,58 +395,15 @@ packet_t ax25_from_text (char *monitor, int strict)
/* Is it possible to have a nul character (zero byte) in the */
/* information field of an AX.25 frame? */
/* Yes, but it would be difficult in the from-text case. */
/* At this point, we have a normal C string. */
/* It is possible that will convert <0x00> to a nul character later. */
/* There we need to maintain a separate length and not use normal C string functions. */
strlcpy (stuff, monitor, sizeof(stuff));
/*
* Translate hexadecimal values like <0xff> to non-printing characters.
* MIC-E message type uses 5 different non-printing characters.
*/
if (first_time)
{
e = regcomp (&unhex_re, "<0x[0-9a-fA-F][0-9a-fA-F]>", 0);
if (e) {
regerror (e, &unhex_re, emsg, sizeof(emsg));
text_color_set(DW_COLOR_ERROR);
dw_printf ("%s:%d: %s\n", __FILE__, __LINE__, emsg);
}
first_time = 0;
}
#if 0
text_color_set(DW_COLOR_DEBUG);
dw_printf ("BEFORE: %s\n", stuff);
ax25_safe_print (stuff, -1, 0);
dw_printf ("\n");
#endif
keep_going = 1;
while (keep_going) {
if (regexec (&unhex_re, stuff, MAXMATCH, match, 0) == 0) {
int n;
char *p;
stuff[match[0].rm_so + 5] = '\0';
n = strtol (stuff + match[0].rm_so + 3, &p, 16);
stuff[match[0].rm_so] = n;
strlcpy (temp, stuff + match[0].rm_eo, sizeof(temp));
strlcpy (stuff + match[0].rm_so + 1, temp, sizeof(stuff)-match[0].rm_so-1);
}
else {
keep_going = 0;
}
}
#if 0
text_color_set(DW_COLOR_DEBUG);
dw_printf ("AFTER: %s\n", stuff);
ax25_safe_print (stuff, -1, 0);
dw_printf ("\n");
#endif
/*
* Initialize the packet with two addresses and control/pid
* Initialize the packet structure with two addresses and control/pid
* for APRS.
*/
memset (this_p->frame_data + AX25_DESTINATION*7, ' ' << 1, 6);
@ -473,12 +433,6 @@ packet_t ax25_from_text (char *monitor, int strict)
*pinfo = '\0';
pinfo++;
if (strlen(pinfo) > AX25_MAX_INFO_LEN) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Warning: Information part truncated to %d characters.\n", AX25_MAX_INFO_LEN);
pinfo[AX25_MAX_INFO_LEN] = '\0';
}
/*
* Separate the addresses.
* Note that source and destination order is swappped.
@ -535,7 +489,6 @@ packet_t ax25_from_text (char *monitor, int strict)
*/
while (( pa = strtok_r (NULL, ",", &saveptr)) != NULL && this_p->num_addr < AX25_MAX_ADDRS ) {
//char *last;
int k;
k = this_p->num_addr;
@ -561,11 +514,61 @@ packet_t ax25_from_text (char *monitor, int strict)
}
}
/*
* Finally, process the information part.
*
* Translate hexadecimal values like <0xff> to single bytes.
* MIC-E format uses 5 different non-printing characters.
* We might want to manually generate UTF-8 characters such as degree.
*/
//#define DEBUG14H 1
#if DEBUG14H
text_color_set(DW_COLOR_DEBUG);
dw_printf ("BEFORE: %s\nSAFE: ", pinfo);
ax25_safe_print (pinfo, -1, 0);
dw_printf ("\n");
#endif
info_len = 0;
while (*pinfo != '\0' && info_len < AX25_MAX_INFO_LEN) {
if (strlen(pinfo) >= 6 &&
pinfo[0] == '<' &&
pinfo[1] == '0' &&
pinfo[2] == 'x' &&
isxdigit(pinfo[3]) &&
isxdigit(pinfo[4]) &&
pinfo[5] == '>') {
char *p;
info_part[info_len] = strtol (pinfo + 3, &p, 16);
info_len++;
pinfo += 6;
}
else {
info_part[info_len] = *pinfo;
info_len++;
pinfo++;
}
}
info_part[info_len] = '\0';
#if DEBUG14H
text_color_set(DW_COLOR_DEBUG);
dw_printf ("AFTER: %s\nSAFE: ", info_part);
ax25_safe_print (info_part, info_len, 0);
dw_printf ("\n");
#endif
/*
* Append the info part.
*/
strlcpy ((char*)(this_p->frame_data+this_p->frame_len), pinfo, sizeof(this_p->frame_data)-this_p->frame_len);
this_p->frame_len += strlen(pinfo);
memcpy ((char*)(this_p->frame_data+this_p->frame_len), info_part, info_len);
this_p->frame_len += info_len;
return (this_p);
}
@ -2536,7 +2539,7 @@ unsigned short ax25_m_m_crc (packet_t pp)
*
* Inputs: pstr - Pointer to string.
*
* len - Maximum length if not -1.
* len - Number of bytes. If < 0 we use strlen().
*
* ascii_only - Restrict output to only ASCII.
* Normally we allow UTF-8.
@ -2587,7 +2590,6 @@ void ax25_safe_print (char *pstr, int len, int ascii_only)
if (len > MAXSAFE)
len = MAXSAFE;
//while (len > 0 && *pstr != '\0')
while (len > 0)
{
ch = *((unsigned char *)pstr);

View File

@ -199,6 +199,33 @@ void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet)
ax25_get_addr_with_ssid (pp, AX25_SOURCE, A->g_src);
ax25_get_addr_with_ssid (pp, AX25_DESTINATION, dest);
/*
* Report error if the information part contains a nul character.
* There are two known cases where this can happen.
*
* - The Kenwood TM-D710A sometimes sends packets like this:
*
* VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
* K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
*
* Notice that the data type indicator of "4" is not valid. If we remove
* 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
* This same thing has been observed from others and is intermittent.
*
* - AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
* This is wrong, it should be using UTF-8.
*/
if ( ( ! A->g_quiet ) && ( (int)strlen((char*)pinfo) != info_len) ) {
text_color_set(DW_COLOR_ERROR);
dw_printf("'nul' character found in Information part. This should never happen.\n");
dw_printf("It seems that %s is transmitting with defective software.\n", A->g_src);
if (strcmp((char*)pinfo, "4P") == 0) {
dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
}
}
switch (*pinfo) { /* "DTI" data type identifier. */
@ -3907,7 +3934,7 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
* clen - Length of comment or -1 to take it all.
*
* Outputs: A->g_telemetry - Base 91 telemetry |ss1122|
* A->g_altitude_ft - from /A=123456
* A->g_altitude_ft - from /A=123456
* A->g_lat - Might be adjusted from !DAO!
* A->g_lon - Might be adjusted from !DAO!
* A->g_aprstt_loc - Private extension to !DAO!
@ -3938,6 +3965,49 @@ static void substr_se (char *dest, const char *src, int start, int endp1)
*
* /A=123456 Altitude
*
* What can appear in a comment?
*
* Chapter 5 of the APRS spec ( http://www.aprs.org/doc/APRS101.PDF ) says:
*
* "The comment may contain any printable ASCII characters (except | and ~,
* which are reserved for TNC channel switching)."
*
* "Printable" would exclude character values less than space (00100000), e.g.
* tab, carriage return, line feed, nul. Sometimes we see carriage return
* (00001010) at the end of APRS packets. This would be in violation of the
* specification.
*
* The base 91 telemetry format (http://he.fi/doc/aprs-base91-comment-telemetry.txt ),
* which is not part of the APRS spec, uses the | character in the comment to delimit encoded
* telemetry data. This would be in violation of the original spec.
*
* The APRS Spec Addendum 1.2 Proposals ( http://www.aprs.org/aprs12/datum.txt)
* adds use of UTF-8 (https://en.wikipedia.org/wiki/UTF-8 )for the free form text in
* messages and comments. It can't be used in the fixed width fields.
*
* Non-ASCII characters are represented by multi-byte sequences. All bytes in these
* multi-byte sequences have the most significant bit set to 1. Using UTF-8 would not
* add any nul (00000000) bytes to the stream.
*
* There are two known cases where we can have a nul character value.
*
* * The Kenwood TM-D710A sometimes sends packets like this:
*
* VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
* K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
*
* Notice that the data type indicator of "4" is not valid. If we remove
* 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
* This same thing has been observed from others and is intermittent.
*
* * AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
* This is wrong. It should be using UTF-8 and I'm not going to accomodate it here.
*
*
* The digipeater and IGate functions should pass along anything exactly the
* we received it, even if it is invalid. If different implementations try to fix it up
* somehow, like changing unprintable characters to spaces, we will only make things
* worse and thwart the duplicate detection.
*
*------------------------------------------------------------------*/

View File

@ -260,7 +260,7 @@ int main (int argc, char *argv[])
text_color_init(t_opt);
text_color_set(DW_COLOR_INFO);
//dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__);
dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__);
//dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION);
#if defined(ENABLE_GPSD) || defined(USE_HAMLIB)

169
igate.c
View File

@ -118,7 +118,7 @@ static packet_t dp_queue_head;
static void satgate_delay_packet (packet_t pp, int chan);
static void send_packet_to_server (packet_t pp, int chan);
static void send_msg_to_server (const char *msg);
static void send_msg_to_server (const char *msg, int msg_len);
static void maybe_xmit_packet_from_igate (char *message, int chan);
static void rx_to_ig_init (void);
@ -266,7 +266,7 @@ int main (int argc, char *argv[])
SLEEP_SEC (20);
text_color_set(DW_COLOR_INFO);
dw_printf ("Send received packet\n");
send_msg_to_server ("W1ABC>APRS:?");
send_msg_to_server ("W1ABC>APRS:?", strlen("W1ABC>APRS:?");
}
#endif
return 0;
@ -797,7 +797,7 @@ static void * connnect_thread (void *arg)
strlcat (stemp, " filter ", sizeof(stemp));
strlcat (stemp, save_igate_config_p->t2_filter, sizeof(stemp));
}
send_msg_to_server (stemp);
send_msg_to_server (stemp, strlen(stemp));
/* Delay until it is ok to start sending packets. */
@ -827,7 +827,7 @@ static void * connnect_thread (void *arg)
strlcpy (heartbeat, "#", sizeof(heartbeat));
/* This will close the socket if any error. */
send_msg_to_server (heartbeat);
send_msg_to_server (heartbeat, strlen(heartbeat));
}
}
@ -861,7 +861,9 @@ static void * connnect_thread (void *arg)
*
*--------------------------------------------------------------------*/
#define IGATE_MAX_MSG 520 /* Message to IGate max 512 characters. */
#define IGATE_MAX_MSG 512 /* "All 'packets' sent to APRS-IS must be in the TNC2 format terminated */
/* by a carriage return, line feed sequence. No line may exceed 512 bytes */
/* including the CR/LF sequence." */
void igate_send_rec_packet (int chan, packet_t recv_pp)
{
@ -993,6 +995,7 @@ void igate_send_rec_packet (int chan, packet_t recv_pp)
/*
* Cut the information part at the first CR or LF.
* Do NOT trim trailing spaces.
*/
info_len = ax25_get_info (pp, &pinfo);
@ -1074,13 +1077,13 @@ static void send_packet_to_server (packet_t pp, int chan)
info_len = ax25_get_info (pp, &pinfo);
(void)(info_len);
/*
* We will often see the same packet multiple times close together due to digipeating.
* The consensus seems to be that we should just send the first and drop the later duplicates.
* There is some dissent on this issue. http://www.tapr.org/pipermail/aprssig/2016-July/045907.html
* There could be some value to sending them all to provide information about digipeater paths.
* However, the servers should drop all duplicates so we wasting everyone's time but sending duplicates.
* If you feel strongly about this issue, you could remove the following section.
* Currently rx_to_ig_allow only checks for recent duplicates.
*/
@ -1132,9 +1135,74 @@ static void send_packet_to_server (packet_t pp, int chan)
strlcat (msg, save_audio_config_p->achan[chan].mycall, sizeof(msg));
strlcat (msg, ":", sizeof(msg));
strlcat (msg, (char*)pinfo, sizeof(msg));
send_msg_to_server (msg);
// It was reported that APRS packets, containing a nul byte in the information part,
// are being truncated. https://github.com/wb2osz/direwolf/issues/84
//
// One might argue that the packets are invalid and the proper behavior would be
// to simply discard them, the same way we do if the CRC is bad. One might argue
// that we should simply pass along whatever we receive even if we don't like it.
// We really shouldn't modify it and make the situation even worse.
//
// Chapter 5 of the APRS spec ( http://www.aprs.org/doc/APRS101.PDF ) says:
//
// "The comment may contain any printable ASCII characters (except | and ~,
// which are reserved for TNC channel switching)."
//
// "Printable" would exclude character values less than space (00100000), e.g.
// tab, carriage return, line feed, nul. Sometimes we see carriage return
// (00001010) at the end of APRS packets. This would be in violation of the
// specification.
//
// The MIC-E position format can have non printable characters (0x1c ... 0x1f, 0x7f)
// in the information part. An unfortunate decision, but it is not in the comment part.
//
// The base 91 telemetry format (http://he.fi/doc/aprs-base91-comment-telemetry.txt ),
// which is not part of the APRS spec, uses the | character in the comment to delimit encoded
// telemetry data. This would be in violation of the original spec. No one cares.
//
// The APRS Spec Addendum 1.2 Proposals ( http://www.aprs.org/aprs12/datum.txt)
// adds use of UTF-8 (https://en.wikipedia.org/wiki/UTF-8 )for the free form text in
// messages and comments. It can't be used in the fixed width fields.
//
// Non-ASCII characters are represented by multi-byte sequences. All bytes in these
// multi-byte sequences have the most significant bit set to 1. Using UTF-8 would not
// add any nul (00000000) bytes to the stream.
//
// Based on all of that, we would not expect to see a nul character in the information part.
//
// There are two known cases where we can have a nul character value.
//
// * The Kenwood TM-D710A sometimes sends packets like this:
//
// VA3AJ-9>T2QU6X,VE3WRC,WIDE1,K8UNS,WIDE2*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`nW<0x1f>oS8>/]"6M}driving fast=
// K4JH-9>S5UQ6X,WR4AGC-3*,WIDE1*:4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00>`jP}l"&>/]"47}QRV from the EV =
//
// Notice that the data type indicator of "4" is not valid. If we remove
// 4P<0x00><0x0f>4T<0x00><0x0f>4X<0x00><0x0f>4\<0x00> we are left with a good MIC-E format.
// This same thing has been observed from others and is intermittent.
//
// * AGW Tracker can send UTF-16 if an option is selected. This can introduce nul bytes.
// This is wrong, it should be using UTF-8.
//
// Rather than using strlcat here, we need to use memcpy and maintain our
// own lengths, being careful to avoid buffer overflow.
int msg_len = strlen(msg); // What we have so far before info part.
if (info_len > IGATE_MAX_MSG - msg_len - 2) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Rx IGate: Too long. Truncating.\n");
info_len = IGATE_MAX_MSG - msg_len - 2;
}
if (info_len > 0) {
memcpy (msg + msg_len, pinfo, info_len);
msg_len += info_len;
}
send_msg_to_server (msg, msg_len);
stats_uplink_packets++;
/*
@ -1152,58 +1220,74 @@ static void send_packet_to_server (packet_t pp, int chan)
*
* Name: send_msg_to_server
*
* Purpose: Send to the IGate server.
* Purpose: Send something to the IGate server.
* This one function should be used for login, hearbeats,
* and packets.
*
* Inputs: imsg - Message. We will add CR/LF.
* Inputs: imsg - Message. We will add CR/LF here.
*
* imsg_len - Length of imsg in bytes.
* It could contain nul characters so we can't
* use the normal C string functions.
*
* Description: Send message to IGate Server if connected.
* Disconnect from server, and notify user, if any error.
* Should use a word other than message because that has
* a specific meaning for APRS.
*
*--------------------------------------------------------------------*/
static void send_msg_to_server (const char *imsg)
static void send_msg_to_server (const char *imsg, int imsg_len)
{
int err;
char stemp[IGATE_MAX_MSG];
char stemp[IGATE_MAX_MSG+1];
int stemp_len;
if (igate_sock == -1) {
return; /* Silently discard if not connected. */
}
strlcpy(stemp, imsg, sizeof(stemp));
stemp_len = imsg_len;
if (stemp_len + 2 > IGATE_MAX_MSG) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Rx IGate: Too long. Truncating.\n");
stemp_len = IGATE_MAX_MSG - 2;
}
memcpy (stemp, imsg, stemp_len);
if (s_debug >= 1) {
text_color_set(DW_COLOR_XMIT);
dw_printf ("[rx>ig] ");
ax25_safe_print (stemp, strlen(stemp), 0);
ax25_safe_print (stemp, stemp_len, 0);
dw_printf ("\n");
}
strlcat (stemp, "\r\n", sizeof(stemp));
stemp[stemp_len++] = '\r';
stemp[stemp_len++] = '\n';
stemp[stemp_len] = '\0';
stats_uplink_bytes += stemp_len;
stats_uplink_bytes += strlen(stemp);
#if __WIN32__
err = send (igate_sock, stemp, strlen(stemp), 0);
err = send (igate_sock, stemp, stemp_len, 0);
if (err == SOCKET_ERROR)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError %d sending message to IGate server. Closing connection.\n\n", WSAGetLastError());
dw_printf ("\nError %d sending to IGate server. Closing connection.\n\n", WSAGetLastError());
//dw_printf ("DEBUG: igate_sock=%d, line=%d\n", igate_sock, __LINE__);
closesocket (igate_sock);
igate_sock = -1;
WSACleanup();
}
#else
err = write (igate_sock, stemp, strlen(stemp));
err = write (igate_sock, stemp, stemp_len);
if (err <= 0)
{
text_color_set(DW_COLOR_ERROR);
dw_printf ("\nError sending message to IGate server. Closing connection.\n\n");
dw_printf ("\nError sending to IGate server. Closing connection.\n\n");
close (igate_sock);
igate_sock = -1;
}
@ -1295,7 +1379,7 @@ static void * igate_recv_thread (void *arg)
#endif
{
unsigned char ch;
unsigned char message[1000]; // Spec says max 500 or so.
unsigned char message[1000]; // Spec says max 512.
int len;
@ -1313,14 +1397,27 @@ static void * igate_recv_thread (void *arg)
ch = get1ch();
stats_downlink_bytes++;
if (len < (int)(sizeof(message)))
{
message[len] = ch;
// I never expected to see a nul character but it can happen.
// If found, change it to <0x00> and ax25_from_text will change it back to a single byte.
// Along the way we can use the normal C string handling.
if (ch == 0 && len < (int)(sizeof(message)) - 5) {
message[len++] = '<';
message[len++] = '0';
message[len++] = 'x';
message[len++] = '0';
message[len++] = '0';
message[len++] = '>';
}
else if (len < (int)(sizeof(message)))
{
message[len++] = ch;
}
len++;
} while (ch != '\n');
message[sizeof(message)-1] = '\0';
/*
* We have a complete message terminated by LF.
*
@ -1335,10 +1432,13 @@ static void * igate_recv_thread (void *arg)
* I've seen a case where the original RF packet had a trailing CR but
* after someone else sent it to the server and it came back to me, that
* CR was now a trailing space.
*
* At first I was tempted to trim a trailing space as well.
* By fixing this one case it might corrupt the data in other cases.
* We compensate for this by ignoring trailing spaces when performing
* the duplicate detection and removal.
*
* We need to transmit exactly as we get it.
*/
/*
@ -1384,6 +1484,19 @@ static void * igate_recv_thread (void *arg)
ax25_safe_print ((char *)message, len, 0);
dw_printf ("\n");
if ((int)strlen((char*)message) != len) {
// Invalid. Either drop it or pass it along as-is. Don't change.
text_color_set(DW_COLOR_ERROR);
dw_printf("'nul' character found in packet from IS. This should never happen.\n");
dw_printf("The source station is probably transmitting with defective software.\n");
//if (strcmp((char*)pinfo, "4P") == 0) {
// dw_printf("The TM-D710 will do this intermittently. A firmware upgrade is needed to fix it.\n");
//}
}
/*
* Record that we heard from the source address.
*/
@ -2038,10 +2151,12 @@ static int rx_to_ig_allow (packet_t pp)
* At first I thought duplicate removal was broken but it turns out they
* are not exactly the same.
*
* The receive IGate spec says a packet should be cut at a CR.
* >>> The receive IGate spec says a packet should be cut at a CR. <<<
*
* In one case it is removed as expected In another case, it is replaced by a trailing
* space character. Maybe someone thought non printable characters should be
* replaced by spaces???
* replaced by spaces??? (I have since been told someone thought it would be a good
* idea to replace unprintable characters with spaces. How's that working out for MIC-E position???)
*
* At first I was tempted to remove any trailing spaces to make up for the other
* IGate adding it. Two wrongs don't make a right. Trailing spaces are not that