mirror of https://github.com/wb2osz/direwolf.git
1723 lines
43 KiB
C
1723 lines
43 KiB
C
// TODO: Shouldn't this be using dw_printf???
|
|
|
|
|
|
//
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
//
|
|
// Copyright (C) 2011,2013 John Langner, WB2OSZ
|
|
//
|
|
// 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, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_pad
|
|
*
|
|
* Purpose: Packet assembler and disasembler.
|
|
*
|
|
* We can obtain AX.25 packets from different sources:
|
|
*
|
|
* (a) from an HDLC frame.
|
|
* (b) from text representation.
|
|
* (c) built up piece by piece.
|
|
*
|
|
* We also want to use a packet in different ways:
|
|
*
|
|
* (a) transmit as an HDLC frame.
|
|
* (b) print in human-readable text.
|
|
* (c) take it apart piece by piece.
|
|
*
|
|
* Looking at the more general case, we might want to modify
|
|
* an existing packet. For instance an APRS repeater might
|
|
* want to change "WIDE2-2" to "WIDE2-1" and retransmit it.
|
|
*
|
|
*
|
|
* Description:
|
|
*
|
|
*
|
|
* A UI frame starts with 2-10 addressses (14-70 octets):
|
|
*
|
|
* * Destination Address
|
|
* * Source Address
|
|
* * 0-8 Digipeater Addresses (Could there ever be more as a result of
|
|
* digipeaters inserting their own call
|
|
* and decrementing the remaining count in
|
|
* WIDEn-n, TRACEn-n, etc.?
|
|
* NO. The limit is 8 when transmitting AX.25 over the radio.
|
|
* However, communication with an IGate server
|
|
* could have a longer VIA path but that is in text form.)
|
|
*
|
|
* Each address is composed of:
|
|
*
|
|
* * 6 upper case letters or digits, blank padded.
|
|
* These are shifted left one bit, leaving the the LSB always 0.
|
|
* * a 7th octet containing the SSID and flags.
|
|
* The LSB is always 0 except for the last octet of the address field.
|
|
*
|
|
* The final octet of the Destination has the form:
|
|
*
|
|
* C R R SSID 0, where,
|
|
*
|
|
* C = command/response = 1
|
|
* R R = Reserved = 1 1
|
|
* SSID = substation ID
|
|
* 0 = zero
|
|
*
|
|
* The final octet of the Source has the form:
|
|
*
|
|
* C R R SSID 0, where,
|
|
*
|
|
* C = command/response = 1
|
|
* R R = Reserved = 1 1
|
|
* SSID = substation ID
|
|
* 0 = zero (or 1 if no repeaters)
|
|
*
|
|
* The final octet of each repeater has the form:
|
|
*
|
|
* H R R SSID 0, where,
|
|
*
|
|
* H = has-been-repeated = 0 initially.
|
|
* Set to 1 after this address has been used.
|
|
* R R = Reserved = 1 1
|
|
* SSID = substation ID
|
|
* 0 = zero (or 1 if last repeater in list)
|
|
*
|
|
* A digipeater would repeat this frame if it finds its address
|
|
* with the "H" bit set to 0 and all earlier repeater addresses
|
|
* have the "H" bit set to 1.
|
|
* The "H" bit would be set to 1 in the repeated frame.
|
|
*
|
|
* When monitoring, an asterisk is displayed after the last digipeater with
|
|
* the "H" bit set. No asterisk means the source is being heard directly.
|
|
*
|
|
* Example, if we can hear all stations involved,
|
|
*
|
|
* SRC>DST,RPT1,RPT2,RPT3: -- we heard SRC
|
|
* SRC>DST,RPT1*,RPT2,RPT3: -- we heard RPT1
|
|
* SRC>DST,RPT1,RPT2*,RPT3: -- we heard RPT2
|
|
* SRC>DST,RPT1,RPT2,RPT3*: -- we heard RPT3
|
|
*
|
|
*
|
|
* Next we have:
|
|
*
|
|
* * One byte Control Field - APRS uses 3 for UI frame
|
|
* * One byte Protocol ID - APRS uses 0xf0 for no layer 3
|
|
*
|
|
* Finally the Information Field of 1-256 bytes.
|
|
*
|
|
* And, of course, the 2 byte CRC.
|
|
*
|
|
*
|
|
* Constructors: ax25_init - Clear everything.
|
|
* ax25_from_text - Tear apart a text string
|
|
* ax25_from_frame - Tear apart an AX.25 frame.
|
|
* Must be called before any other function.
|
|
*
|
|
* Get methods: .... - Extract destination, source, or digipeater
|
|
* address from frame.
|
|
*
|
|
* Assumptions: CRC has already been verified to be correct.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
#define AX25_PAD_C /* this will affect behavior of ax25_pad.h */
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#ifndef _POSIX_C_SOURCE
|
|
|
|
#define _POSIX_C_SOURCE 1
|
|
#endif
|
|
|
|
#include "regex.h"
|
|
|
|
#if __WIN32__
|
|
char *strtok_r(char *str, const char *delim, char **saveptr);
|
|
#endif
|
|
|
|
#include "ax25_pad.h"
|
|
#include "textcolor.h"
|
|
#include "fcs_calc.h"
|
|
|
|
/*
|
|
* Accumulate statistics.
|
|
* If new_count gets more than a few larger than delete_count plus the size of
|
|
* the transmit queue we have a memory leak.
|
|
*/
|
|
|
|
static int new_count = 0;
|
|
static int delete_count = 0;
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_new
|
|
*
|
|
* Purpose: Allocate memory for a new packet object.
|
|
*
|
|
* Returns: Identifier for a new packet object.
|
|
* In the current implementation this happens to be a pointer.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
static packet_t ax25_new (void)
|
|
{
|
|
struct packet_s *this_p;
|
|
|
|
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("ax25_new(): before alloc, new=%d, delete=%d\n", new_count, delete_count);
|
|
#endif
|
|
|
|
new_count++;
|
|
|
|
/*
|
|
* check for memory leak.
|
|
*/
|
|
if (new_count > delete_count + 100) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count);
|
|
}
|
|
|
|
this_p = calloc(sizeof (struct packet_s), (size_t)1);
|
|
this_p->magic1 = MAGIC;
|
|
this_p->magic2 = MAGIC;
|
|
return (this_p);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_delete
|
|
*
|
|
* Purpose: Destroy a packet object, freeing up memory it was using.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_delete (packet_t this_p)
|
|
{
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("ax25_delete(): before free, new=%d, delete=%d\n", new_count, delete_count);
|
|
#endif
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
memset (this_p, 0, sizeof (struct packet_s));
|
|
delete_count++;
|
|
free (this_p);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_from_text
|
|
*
|
|
* 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.
|
|
* source>dest[,repeater1,repeater2,...]:information
|
|
*
|
|
* strict - True to enforce rules for packets sent over the air.
|
|
* False to be more lenient for packets from IGate server.
|
|
*
|
|
* Returns: Pointer to new packet object in the current implementation.
|
|
*
|
|
* Outputs: Use the "get" functions to retrieve information in different ways.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
packet_t ax25_from_text (char *monitor, int strict)
|
|
{
|
|
|
|
/*
|
|
* Tearing it apart is destructive so make our own copy first.
|
|
*/
|
|
char stuff[512];
|
|
|
|
char *pinfo;
|
|
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;
|
|
|
|
|
|
|
|
packet_t this_p = ax25_new ();
|
|
|
|
/* Is it possible to have a nul character (zero byte) in the */
|
|
/* information field of an AX.25 frame? */
|
|
|
|
strcpy (stuff, monitor);
|
|
|
|
/*
|
|
* 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;
|
|
strcpy (temp, stuff + match[0].rm_eo);
|
|
strcpy (stuff + match[0].rm_so + 1, temp);
|
|
}
|
|
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
|
|
|
|
/*
|
|
* Separate the addresses from the rest.
|
|
*/
|
|
pinfo = strchr (stuff, ':');
|
|
|
|
if (pinfo == NULL) {
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
*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';
|
|
}
|
|
|
|
strcpy ((char*)(this_p->the_rest + 2), pinfo);
|
|
this_p->the_rest_len = strlen(pinfo) + 2;
|
|
|
|
/*
|
|
* Now separate the addresses.
|
|
* Note that source and destination order is swappped.
|
|
*/
|
|
|
|
this_p->num_addr = 2;
|
|
|
|
/*
|
|
* Source address.
|
|
* Don't use traditional strtok because it is not thread safe.
|
|
*/
|
|
pa = strtok_r (stuff, ">", &saveptr);
|
|
if (pa == NULL) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to create packet from text. No source address\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( ! ax25_parse_addr (pa, strict, this_p->addrs[AX25_SOURCE], &ssid_temp, &heard_temp)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to create packet from text. Bad source address\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
this_p->ssid_etc[AX25_SOURCE] = SSID_H_MASK | SSID_RR_MASK;
|
|
ax25_set_ssid (this_p, AX25_SOURCE, ssid_temp);
|
|
|
|
/*
|
|
* Destination address.
|
|
*/
|
|
|
|
pa = strtok_r (NULL, ",", &saveptr);
|
|
if (pa == NULL) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to create packet from text. No destination address\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
if ( ! ax25_parse_addr (pa, strict, this_p->addrs[AX25_DESTINATION], &ssid_temp, &heard_temp)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to create packet from text. Bad destination address\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
this_p->ssid_etc[AX25_DESTINATION] = SSID_H_MASK | SSID_RR_MASK;
|
|
ax25_set_ssid (this_p, AX25_DESTINATION, ssid_temp);
|
|
|
|
/*
|
|
* VIA path.
|
|
*/
|
|
while (( pa = strtok_r (NULL, ",", &saveptr)) != NULL && this_p->num_addr < AX25_MAX_ADDRS ) {
|
|
|
|
//char *last;
|
|
int k;
|
|
|
|
k = this_p->num_addr;
|
|
|
|
this_p->num_addr++;
|
|
|
|
if ( ! ax25_parse_addr (pa, strict, this_p->addrs[k], &ssid_temp, &heard_temp)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to create packet from text. Bad digipeater address\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
this_p->ssid_etc[k] = SSID_RR_MASK;
|
|
ax25_set_ssid (this_p, k, ssid_temp);
|
|
|
|
// Does it have an "*" at the end?
|
|
// TODO: Complain if more than one "*".
|
|
// Could also check for all has been repeated bits are adjacent.
|
|
|
|
if (heard_temp) {
|
|
for ( ; k >= AX25_REPEATER_1; k--) {
|
|
ax25_set_h (this_p, k);
|
|
}
|
|
}
|
|
}
|
|
|
|
this_p->the_rest[0] = AX25_UI_FRAME;
|
|
this_p->the_rest[1] = AX25_NO_LAYER_3;
|
|
|
|
return (this_p);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_from_frame
|
|
*
|
|
* Purpose: Split apart an HDLC frame to components.
|
|
*
|
|
* Inputs: fbuf - Pointer to beginning of frame.
|
|
*
|
|
* flen - Length excluding the two FCS bytes.
|
|
*
|
|
* alevel - Audio level of received signal.
|
|
* Maximum range 0 - 100.
|
|
* -1 might be used when not applicable.
|
|
*
|
|
* Returns: Pointer to new packet object or NULL if error.
|
|
*
|
|
* Outputs: Use the "get" functions to retrieve information in different ways.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
packet_t ax25_from_frame (unsigned char *fbuf, int flen, int alevel)
|
|
{
|
|
unsigned char *pf;
|
|
//int found_last;
|
|
packet_t this_p;
|
|
|
|
int a;
|
|
int addr_bytes;
|
|
|
|
/*
|
|
* First make sure we have an acceptable length:
|
|
*
|
|
* We are not concerned with the FCS (CRC) because someone else checked it.
|
|
*
|
|
* Is is possible to have zero length for info? No.
|
|
*/
|
|
|
|
if (flen < AX25_MIN_PACKET_LEN || flen > AX25_MAX_PACKET_LEN)
|
|
{
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Frame length %d not in allowable range of %d to %d.\n", flen, AX25_MIN_PACKET_LEN, AX25_MAX_PACKET_LEN);
|
|
return (NULL);
|
|
}
|
|
|
|
this_p = ax25_new ();
|
|
|
|
/*
|
|
* Extract the addresses.
|
|
* The last one has '1' in the LSB of the last byte.
|
|
*/
|
|
|
|
#if 1
|
|
|
|
/*
|
|
* 0.9 - Try new strategy that will allow KISS mode
|
|
* to handle non AX.25 frame.
|
|
*/
|
|
|
|
this_p->num_addr = 0; /* Number of addresses extracted. */
|
|
|
|
addr_bytes = 0;
|
|
for (a = 0; a < flen && addr_bytes == 0; a++) {
|
|
if (fbuf[a] & 0x01) {
|
|
addr_bytes = a + 1;
|
|
}
|
|
}
|
|
|
|
if (addr_bytes % 7 == 0) {
|
|
int addrs = addr_bytes / 7;
|
|
if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) {
|
|
this_p->num_addr = addrs;
|
|
|
|
for (a = 0; a < addrs; a++){
|
|
unsigned char *pin;
|
|
char *pout;
|
|
int j;
|
|
char ch;
|
|
|
|
pin = fbuf + a * 7;
|
|
pout = & this_p->addrs[a][0];
|
|
|
|
for (j=0; j<6; j++)
|
|
{
|
|
ch = *pin++ >> 1;
|
|
if (ch != ' ')
|
|
{
|
|
*pout++ = ch;
|
|
}
|
|
}
|
|
*pout = '\0';
|
|
|
|
this_p->ssid_etc[a] = *pin & ~ SSID_LAST_MASK;
|
|
}
|
|
}
|
|
}
|
|
|
|
pf = fbuf + this_p->num_addr * 7;
|
|
|
|
#else
|
|
|
|
pf = fbuf; /* Transmitted form from here. */
|
|
|
|
this_p->num_addr = 0; /* Number of addresses extracted. */
|
|
found_last = 0;
|
|
|
|
while (this_p->num_addr < AX25_MAX_ADDRS && ! found_last) {
|
|
|
|
unsigned char *pin;
|
|
char *pout;
|
|
int j;
|
|
char ch;
|
|
|
|
pin = pf;
|
|
pout = & this_p->addrs[this_p->num_addr][0];
|
|
|
|
for (j=0; j<6; j++)
|
|
{
|
|
ch = *pin++ >> 1;
|
|
if (ch != ' ')
|
|
{
|
|
*pout++ = ch;
|
|
}
|
|
}
|
|
*pout = '\0';
|
|
|
|
this_p->ssid_etc[this_p->num_addr] = pf[6] & ~ SSID_LAST_MASK;
|
|
|
|
this_p->num_addr++;
|
|
|
|
if (pf[6] & SSID_LAST_MASK) { /* Is this the last one? */
|
|
found_last = 1;
|
|
}
|
|
else {
|
|
pf += 7; /* Get ready for next one. */
|
|
}
|
|
}
|
|
|
|
if (this_p->num_addr < 2) {
|
|
int k;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Frame format error detected in ax25_from_frame, %s, line %d.\n", __FILE__, __LINE__);
|
|
dw_printf ("Did not find a minimum of two addresses at beginning of AX.25 frame.\n");
|
|
for (k=0; k<14; k++) {
|
|
dw_printf (" %02x", fbuf[k]);
|
|
}
|
|
dw_printf ("\n");
|
|
/* Should we keep going or delete the packet? */
|
|
}
|
|
|
|
/*
|
|
* pf still points to the last address (repeater or source).
|
|
*
|
|
* Verify that it has the last address bit set.
|
|
*/
|
|
if ((pf[6] & SSID_LAST_MASK) == 0) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Last address in header does not have LSB set.\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
pf += 7;
|
|
|
|
#endif
|
|
|
|
if (this_p->num_addr * 7 > flen - 1) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Frame too short to include control field.\n");
|
|
ax25_delete (this_p);
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* pf should now point to control field.
|
|
* Previously we separated out control, PID, and Info here.
|
|
*
|
|
* Now (version 0.8) we save control, PID, and info together.
|
|
* This makes it easier to act as a dumb KISS TNC
|
|
* for AX.25-based protocols other than APRS.
|
|
*/
|
|
this_p->the_rest_len = flen - (pf - fbuf);
|
|
|
|
assert (this_p->the_rest_len >= 1);
|
|
|
|
memcpy (this_p->the_rest, pf, (size_t)this_p->the_rest_len);
|
|
this_p->the_rest[this_p->the_rest_len] = '\0';
|
|
|
|
return (this_p);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_dup
|
|
*
|
|
* Purpose: Make a copy of given packet object.
|
|
*
|
|
* Inputs: copy_from - Existing packet object.
|
|
*
|
|
* Returns: Pointer to new packet object or NULL if error.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
packet_t ax25_dup (packet_t copy_from)
|
|
{
|
|
|
|
packet_t this_p;
|
|
|
|
|
|
this_p = ax25_new ();
|
|
|
|
memcpy (this_p, copy_from, sizeof (struct packet_s));
|
|
|
|
return (this_p);
|
|
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_parse_addr
|
|
*
|
|
* Purpose: Parse address with optional ssid.
|
|
*
|
|
* Inputs: in_addr - Input such as "WB2OSZ-15*"
|
|
*
|
|
* strict - TRUE for strict checking (6 characters, no lower case,
|
|
* SSID must be in range of 0 to 15).
|
|
* Strict is appropriate for packets sent
|
|
* over the radio. Communication with IGate
|
|
* allows lower case (e.g. "qAR") and two
|
|
* alphanumeric characters for the SSID.
|
|
* We also get messages like this from a server.
|
|
* KB1POR>APU25N,TCPIP*,qAC,T2NUENGLD:...
|
|
*
|
|
* Outputs: out_addr - Address without any SSID.
|
|
* Must be at least AX25_MAX_ADDR_LEN bytes.
|
|
*
|
|
* out_ssid - Numeric value of SSID.
|
|
*
|
|
* out_heard - True if "*" found.
|
|
*
|
|
* Returns: True (1) if OK, false (0) if any error.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
|
|
int ax25_parse_addr (char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard)
|
|
{
|
|
char *p;
|
|
char sstr[4];
|
|
int i, j, k;
|
|
int maxlen;
|
|
|
|
strcpy (out_addr, "");
|
|
*out_ssid = 0;
|
|
*out_heard = 0;
|
|
|
|
maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN-1);
|
|
p = in_addr;
|
|
i = 0;
|
|
for (p = in_addr; isalnum(*p); p++) {
|
|
if (i >= maxlen) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Address is too long. \"%s\" has more than %d characters.\n", in_addr, maxlen);
|
|
return 0;
|
|
}
|
|
out_addr[i++] = *p;
|
|
out_addr[i] = '\0';
|
|
if (strict && islower(*p)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Address has lower case letters. \"%s\" must be all upper case.\n", in_addr);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
strcpy (sstr, "");
|
|
j = 0;
|
|
if (*p == '-') {
|
|
for (p++; isalnum(*p); p++) {
|
|
if (j >= 2) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("SSID is too long. SSID part of \"%s\" has more than 2 characters.\n", in_addr);
|
|
return 0;
|
|
}
|
|
sstr[j++] = *p;
|
|
sstr[j] = '\0';
|
|
if (strict && ! isdigit(*p)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("SSID must be digits. \"%s\" has letters in SSID.\n", in_addr);
|
|
return 0;
|
|
}
|
|
}
|
|
k = atoi(sstr);
|
|
if (k < 0 || k > 15) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("SSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", in_addr);
|
|
return 0;
|
|
}
|
|
*out_ssid = k;
|
|
}
|
|
|
|
if (*p == '*') {
|
|
*out_heard = 1;
|
|
p++;
|
|
}
|
|
|
|
if (*p != '\0') {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Invalid character \"%c\" found in address \"%s\".\n", *p, in_addr);
|
|
return 0;
|
|
}
|
|
|
|
return (1);
|
|
|
|
} /* end ax25_parse_addr */
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_unwrap_third_party
|
|
*
|
|
* Purpose: Unwrap a third party messge from the header.
|
|
*
|
|
* Inputs: copy_from - Existing packet object.
|
|
*
|
|
* Returns: Pointer to new packet object or NULL if error.
|
|
*
|
|
* Example: Input: A>B,C:}D>E,F:info
|
|
* Output: D>E,F:info
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
packet_t ax25_unwrap_third_party (packet_t from_pp)
|
|
{
|
|
unsigned char *info_p;
|
|
packet_t result_pp;
|
|
|
|
if (ax25_get_dti(from_pp) != '}') {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Internal error: ax25_unwrap_third_party: wrong data type.\n");
|
|
return (NULL);
|
|
}
|
|
|
|
(void) ax25_get_info (from_pp, &info_p);
|
|
|
|
result_pp = ax25_from_text((char *)info_p + 1, 0);
|
|
|
|
return (result_pp);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_set_addr
|
|
*
|
|
* Purpose: Add or change an address.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
* ad - Address with optional dash and substation id.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* TODO: ax25_from_text could use this.
|
|
*
|
|
* Returns: None.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_set_addr (packet_t this_p, int n, char *ad)
|
|
{
|
|
int ssid_temp, heard_temp;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= 0 && n < AX25_MAX_ADDRS);
|
|
assert (strlen(ad) < AX25_MAX_ADDR_LEN);
|
|
|
|
if (n+1 > this_p->num_addr) {
|
|
this_p->num_addr = n+1;
|
|
this_p->ssid_etc[n] = SSID_RR_MASK;
|
|
}
|
|
|
|
ax25_parse_addr (ad, 0, this_p->addrs[n], &ssid_temp, &heard_temp);
|
|
ax25_set_ssid (this_p, n, ssid_temp);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_insert_addr
|
|
*
|
|
* Purpose: Insert address at specified position, shifting others up one
|
|
* position.
|
|
* This is used when a digipeater wants to insert its own call
|
|
* for tracing purposes.
|
|
* For example:
|
|
* W1ABC>TEST,WIDE3-3
|
|
* Would become:
|
|
* W1ABC>TEST,WB2OSZ-1*,WIDE3-2
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
*
|
|
* ad - Address with optional dash and substation id.
|
|
*
|
|
* Bugs: Little validity or bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: None.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_insert_addr (packet_t this_p, int n, char *ad)
|
|
{
|
|
int k;
|
|
int ssid_temp, heard_temp;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= AX25_REPEATER_1 && n < AX25_MAX_ADDRS);
|
|
assert (strlen(ad) < AX25_MAX_ADDR_LEN);
|
|
|
|
/* Don't do it if we already have the maximum number. */
|
|
/* Should probably return success/fail code but currently the caller doesn't care. */
|
|
|
|
if ( this_p->num_addr >= AX25_MAX_ADDRS) {
|
|
return;
|
|
}
|
|
|
|
/* Shift the current occupant and others up. */
|
|
|
|
for (k=this_p->num_addr; k>n; k--) {
|
|
strcpy (this_p->addrs[k], this_p->addrs[k-1]);
|
|
this_p->ssid_etc[k] = this_p->ssid_etc[k-1];
|
|
}
|
|
|
|
this_p->num_addr++;
|
|
|
|
ax25_parse_addr (ad, 0, this_p->addrs[n], &ssid_temp, &heard_temp);
|
|
this_p->ssid_etc[n] = SSID_RR_MASK;
|
|
ax25_set_ssid (this_p, n, ssid_temp);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_remove_addr
|
|
*
|
|
* Purpose: Remove address at specified position, shifting others down one position.
|
|
* This is used when we want to remove something from the digipeater list.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_REPEATER1, AX25_REPEATER2, etc.
|
|
*
|
|
* Bugs: Little validity or bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: None.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_remove_addr (packet_t this_p, int n)
|
|
{
|
|
int k;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= AX25_REPEATER_1 && n < AX25_MAX_ADDRS);
|
|
|
|
/* Shift those beyond to fill this position. */
|
|
|
|
this_p->num_addr--;
|
|
|
|
for (k = n; k < this_p->num_addr; k++) {
|
|
strcpy (this_p->addrs[k], this_p->addrs[k+1]);
|
|
this_p->ssid_etc[k] = this_p->ssid_etc[k+1];
|
|
}
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_num_addr
|
|
*
|
|
* Purpose: Return number of addresses in current packet.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: Number of addresses in the current packet.
|
|
* Should be in the range of 2 .. AX25_MAX_ADDRS.
|
|
*
|
|
* Version 0.9: Could be zero for a non AX.25 frame in KISS mode.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_num_addr (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
return (this_p->num_addr);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_num_repeaters
|
|
*
|
|
* Purpose: Return number of repeater addresses in current packet.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: Number of addresses in the current packet - 2.
|
|
* Should be in the range of 0 .. AX25_MAX_ADDRS - 2.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_num_repeaters (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
if (this_p->num_addr >= 2) {
|
|
return (this_p->num_addr - 2);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_addr_with_ssid
|
|
*
|
|
* Purpose: Return specified address with any SSID in current packet.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
*
|
|
* Outputs: station - String representation of the station, including the SSID.
|
|
* e.g. "WB2OSZ-15"
|
|
*
|
|
* Bugs: No bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: Character string in usual human readable format,
|
|
*
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_get_addr_with_ssid (packet_t this_p, int n, char *station)
|
|
{
|
|
int ssid;
|
|
char sstr[4];
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
/*
|
|
* This assert failure popped up once and it is not clear why.
|
|
* Let's print out more information about the situation so we
|
|
* might have a clue about the root cause.
|
|
* Try to keep going instead of dying at this point.
|
|
*/
|
|
//assert (n >= 0 && n < this_p->num_addr);
|
|
|
|
if (n < 0) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
|
dw_printf ("Address index, %d, is less than zero.\n", n);
|
|
strcpy (station, "??????");
|
|
return;
|
|
}
|
|
|
|
if (n >= this_p->num_addr) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Internal error detected in ax25_get_addr_with_ssid, %s, line %d.\n", __FILE__, __LINE__);
|
|
dw_printf ("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr);
|
|
strcpy (station, "??????");
|
|
return;
|
|
}
|
|
|
|
strcpy (station, this_p->addrs[n]);
|
|
|
|
ssid = ax25_get_ssid (this_p, n);
|
|
if (ssid != 0) {
|
|
sprintf (sstr, "-%d", ssid);
|
|
strcat (station, sstr);
|
|
}
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_ssid
|
|
*
|
|
* Purpose: Return SSID of specified address in current packet.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
*
|
|
* Warning: No bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: Substation id, as integer 0 .. 15.
|
|
*
|
|
* Bugs: Rewrite to keep call and SSID separate internally.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_ssid (packet_t this_p, int n)
|
|
{
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= 0 && n < this_p->num_addr);
|
|
|
|
return ((this_p->ssid_etc[n] & SSID_SSID_MASK) >> SSID_SSID_SHIFT);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_set_ssid
|
|
*
|
|
* Purpose: Set the SSID of specified address in current packet.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
*
|
|
* ssid - New SSID. Must be in range of 0 to 15.
|
|
*
|
|
* Warning: No bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Bugs: Rewrite to keep call and SSID separate internally.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_set_ssid (packet_t this_p, int n, int ssid)
|
|
{
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= 0 && n < this_p->num_addr);
|
|
|
|
this_p->ssid_etc[n] = (this_p->ssid_etc[n] & ~ SSID_SSID_MASK) |
|
|
((ssid << SSID_SSID_SHIFT) & SSID_SSID_MASK) ;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_h
|
|
*
|
|
* Purpose: Return "has been repeated" flag of specified address in current packet.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc.
|
|
*
|
|
* Bugs: No bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: True or false.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_h (packet_t this_p, int n)
|
|
{
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= 0 && n < this_p->num_addr);
|
|
|
|
return ((this_p->ssid_etc[n] & SSID_H_MASK) >> SSID_H_SHIFT);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_set_h
|
|
*
|
|
* Purpose: Set the "has been repeated" flag of specified address in current packet.
|
|
*
|
|
* Inputs: n - Index of address. Use the symbols
|
|
* Should be in range of AX25_REPEATER_1 .. AX25_REPEATER_8.
|
|
*
|
|
* Bugs: No bounds checking is performed. Be careful.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: None
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_set_h (packet_t this_p, int n)
|
|
{
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
assert (n >= 0 && n < this_p->num_addr);
|
|
|
|
this_p->ssid_etc[n] |= SSID_H_MASK;
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_heard
|
|
*
|
|
* Purpose: Return index of the station that we heard.
|
|
*
|
|
* Inputs: none
|
|
*
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: If any of the digipeaters have the has-been-repeated bit set,
|
|
* return the index of the last one. Otherwise return index for source.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_heard(packet_t this_p)
|
|
{
|
|
int i;
|
|
int result;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
result = AX25_SOURCE;
|
|
|
|
for (i = AX25_REPEATER_1; i < ax25_get_num_addr(this_p); i++) {
|
|
|
|
if (ax25_get_h(this_p,i)) {
|
|
result = i;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_first_not_repeated
|
|
*
|
|
* Purpose: Return index of the first repeater that does NOT have the
|
|
* "has been repeated" flag set or -1 if none.
|
|
*
|
|
* Inputs: none
|
|
*
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: In range of X25_REPEATER_1 .. X25_REPEATER_8 or -1 if none.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_first_not_repeated(packet_t this_p)
|
|
{
|
|
int i;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
for (i = AX25_REPEATER_1; i < ax25_get_num_addr(this_p); i++) {
|
|
|
|
if ( ! ax25_get_h(this_p,i)) {
|
|
return (i);
|
|
}
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_info
|
|
*
|
|
* Purpose: Obtain Information part of current packet.
|
|
*
|
|
* Inputs: None.
|
|
*
|
|
* Outputs: paddr - Starting address is returned here.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: Number of octets in the Information part.
|
|
* Should be in the range of AX25_MIN_INFO_LEN .. AX25_MAX_INFO_LEN.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_info (packet_t this_p, unsigned char **paddr)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
if (this_p->num_addr >= 2) {
|
|
*paddr = this_p->the_rest + ax25_get_info_offset(this_p);
|
|
return (ax25_get_num_info(this_p));
|
|
}
|
|
|
|
/* Not AX.25. Whole packet is info. */
|
|
|
|
*paddr = this_p->the_rest;
|
|
return (this_p->the_rest_len);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_dti
|
|
*
|
|
* Purpose: Get Data Type Identifier from Information part.
|
|
*
|
|
* Inputs: None.
|
|
*
|
|
* Assumption: ax25_from_text or ax25_from_frame was called first.
|
|
*
|
|
* Returns: First byte from the information part.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int ax25_get_dti (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
if (this_p->num_addr >= 2) {
|
|
return (this_p->the_rest[ax25_get_info_offset(this_p)]);
|
|
}
|
|
return (' ');
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_set_nextp
|
|
*
|
|
* Purpose: Set next packet object in queue.
|
|
*
|
|
* Inputs: this_p - Current packet object.
|
|
*
|
|
* next_p - pointer to next one
|
|
*
|
|
* Description: This is used to build a linked list for a queue.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void ax25_set_nextp (packet_t this_p, packet_t next_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
this_p->nextp = next_p;
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_get_nextp
|
|
*
|
|
* Purpose: Obtain next packet object in queue.
|
|
*
|
|
* Inputs: Packet object.
|
|
*
|
|
* Returns: Following object in queue or NULL.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
packet_t ax25_get_nextp (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
return (this_p->nextp);
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_format_addrs
|
|
*
|
|
* Purpose: Format all the addresses suitable for printing.
|
|
*
|
|
* The AX.25 spec refers to this as "Source Path Header" - "TNC-2" Format
|
|
*
|
|
* Inputs: Current packet.
|
|
*
|
|
* Outputs: result - All addresses combined into a single string of the form:
|
|
*
|
|
* "Source > Destination [ , repeater ... ] :"
|
|
*
|
|
* An asterisk is displayed after the last digipeater
|
|
* with the "H" bit set. e.g. If we hear RPT2,
|
|
*
|
|
* SRC>DST,RPT1,RPT2*,RPT3:
|
|
*
|
|
* No asterisk means the source is being heard directly.
|
|
* Needs to be 101 characters to avoid overflowing.
|
|
* (Up to 100 characters + \0)
|
|
*
|
|
* Errors: No error checking so caller needs to be careful.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
void ax25_format_addrs (packet_t this_p, char *result)
|
|
{
|
|
int i;
|
|
int heard;
|
|
char stemp[AX25_MAX_ADDR_LEN];
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
*result = '\0';
|
|
|
|
/* New in 0.9. */
|
|
/* Don't get upset if no addresses. */
|
|
/* This will allow packets that do not comply to AX.25 format. */
|
|
|
|
if (this_p->num_addr == 0) {
|
|
return;
|
|
}
|
|
|
|
ax25_get_addr_with_ssid (this_p, AX25_SOURCE, stemp);
|
|
strcat (result, stemp);
|
|
strcat (result, ">");
|
|
|
|
ax25_get_addr_with_ssid (this_p, AX25_DESTINATION, stemp);
|
|
strcat (result, stemp);
|
|
|
|
heard = ax25_get_heard(this_p);
|
|
|
|
for (i=(int)AX25_REPEATER_1; i<this_p->num_addr; i++) {
|
|
ax25_get_addr_with_ssid (this_p, i, stemp);
|
|
strcat (result, ",");
|
|
strcat (result, stemp);
|
|
if (i == heard) {
|
|
strcat (result, "*");
|
|
}
|
|
}
|
|
|
|
strcat (result, ":");
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_pack
|
|
*
|
|
* Purpose: Put all the pieces into format ready for transmission.
|
|
*
|
|
* Inputs: this_p - pointer to packet object.
|
|
*
|
|
* Outputs: result - Frame buffer, AX25_MAX_PACKET_LEN bytes.
|
|
* Should also have two extra for FCS to be
|
|
* added later.
|
|
*
|
|
* Returns: Number of octets in the frame buffer.
|
|
* Does NOT include the extra 2 for FCS.
|
|
*
|
|
* Errors: Returns -1.
|
|
*
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
int ax25_pack (packet_t this_p, unsigned char result[AX25_MAX_PACKET_LEN])
|
|
{
|
|
int j, k;
|
|
unsigned char *pout;
|
|
int len;
|
|
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
pout = result;
|
|
|
|
for (j=0; j<this_p->num_addr; j++) {
|
|
|
|
char *s;
|
|
|
|
memset (pout, ' ' << 1, (size_t)6);
|
|
|
|
s = this_p->addrs[j];
|
|
for (k=0; *s != '\0'; k++, s++) {
|
|
pout[k] = *s << 1;
|
|
}
|
|
|
|
if (j == this_p->num_addr - 1) {
|
|
pout[6] = this_p->ssid_etc[j] | SSID_LAST_MASK;
|
|
}
|
|
else {
|
|
pout[6] = this_p->ssid_etc[j] & ~ SSID_LAST_MASK;
|
|
}
|
|
pout += 7;
|
|
}
|
|
|
|
memcpy (pout, this_p->the_rest, (size_t)this_p->the_rest_len);
|
|
pout += this_p->the_rest_len;
|
|
|
|
len = pout - result;
|
|
|
|
assert (len <= AX25_MAX_PACKET_LEN);
|
|
|
|
return (len);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_is_aprs
|
|
*
|
|
* Purpose: Is this packet APRS format?
|
|
*
|
|
* Inputs: this_p - pointer to packet object.
|
|
*
|
|
* Returns: True if this frame has the proper control
|
|
* octets for an APRS packet.
|
|
* control 3 for UI frame
|
|
* protocol id 0xf0 for no layer 3
|
|
*
|
|
*
|
|
* Description: Dire Wolf should be able to act as a KISS TNC for
|
|
* any type of AX.25 activity. However, there are other
|
|
* places where we want to process only APRS.
|
|
* (e.g. digipeating and IGate.)
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
|
|
int ax25_is_aprs (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
return (this_p->num_addr >= 2 &&
|
|
ax25_get_control(this_p) == AX25_UI_FRAME &&
|
|
ax25_get_pid(this_p) == AX25_NO_LAYER_3);
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_get_control
|
|
*
|
|
* Purpose: Get Control field from packet.
|
|
*
|
|
* Inputs: this_p - pointer to packet object.
|
|
*
|
|
* Returns: APRS uses AX25_UI_FRAME.
|
|
* This could also be used in other situations.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
|
|
int ax25_get_control (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
if (this_p->num_addr >= 2) {
|
|
return (this_p->the_rest[ax25_get_control_offset(this_p)]);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_get_pid
|
|
*
|
|
* Purpose: Get protocol ID from packet.
|
|
*
|
|
* Inputs: this_p - pointer to packet object.
|
|
*
|
|
* Returns: APRS uses 0xf0 for no layer 3.
|
|
* This could also be used in other situations.
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
|
|
int ax25_get_pid (packet_t this_p)
|
|
{
|
|
assert (this_p->magic1 == MAGIC);
|
|
assert (this_p->magic2 == MAGIC);
|
|
|
|
if (this_p->num_addr >= 2) {
|
|
return (this_p->the_rest[ax25_get_pid_offset(this_p)]);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_dedupe_crc
|
|
*
|
|
* Purpose: Calculate a checksum for the packet source, destination, and
|
|
* information but NOT the digipeaters.
|
|
* This is used for duplicate detection in the digipeater
|
|
* and IGate algorithms.
|
|
*
|
|
* Input: pp - Pointer to packet object.
|
|
*
|
|
* Returns: Value which will be the same for a duplicate but very unlikely
|
|
* to match a non-duplicate packet.
|
|
*
|
|
* Description: For detecting duplicates, we need to look
|
|
* + source station
|
|
* + destination
|
|
* + information field
|
|
* but NOT the changing list of digipeaters.
|
|
*
|
|
* Typically, only a checksum is kept to reduce memory
|
|
* requirements and amount of compution for comparisons.
|
|
* There is a very very small probability that two unrelated
|
|
* packets will result in the same checksum, and the
|
|
* undesired dropping of the packet.
|
|
*
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
unsigned short ax25_dedupe_crc (packet_t pp)
|
|
{
|
|
unsigned short crc;
|
|
char src[AX25_MAX_ADDR_LEN];
|
|
char dest[AX25_MAX_ADDR_LEN];
|
|
unsigned char *pinfo;
|
|
int info_len;
|
|
|
|
ax25_get_addr_with_ssid(pp, AX25_SOURCE, src);
|
|
ax25_get_addr_with_ssid(pp, AX25_DESTINATION, dest);
|
|
info_len = ax25_get_info (pp, &pinfo);
|
|
|
|
crc = 0xffff;
|
|
crc = crc16((unsigned char *)src, strlen(src), crc);
|
|
crc = crc16((unsigned char *)dest, strlen(dest), crc);
|
|
crc = crc16(pinfo, info_len, crc);
|
|
|
|
return (crc);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*
|
|
* Name: ax25_m_m_crc
|
|
*
|
|
* Purpose: Calculate a checksum for the packet.
|
|
* This is used for the multimodem duplicate detection.
|
|
*
|
|
* Input: pp - Pointer to packet object.
|
|
*
|
|
* Returns: Value which will be the same for a duplicate but very unlikely
|
|
* to match a non-duplicate packet.
|
|
*
|
|
* Description: For detecting duplicates, we need to look the entire packet.
|
|
*
|
|
* Typically, only a checksum is kept to reduce memory
|
|
* requirements and amount of compution for comparisons.
|
|
* There is a very very small probability that two unrelated
|
|
* packets will result in the same checksum, and the
|
|
* undesired dropping of the packet.
|
|
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
unsigned short ax25_m_m_crc (packet_t pp)
|
|
{
|
|
unsigned short crc;
|
|
unsigned char fbuf[AX25_MAX_PACKET_LEN];
|
|
int flen;
|
|
|
|
flen = ax25_pack (pp, fbuf);
|
|
|
|
crc = 0xffff;
|
|
crc = crc16(fbuf, flen, crc);
|
|
|
|
return (crc);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Function: ax25_safe_print
|
|
*
|
|
* Purpose: Print given string, changing non printable characters to
|
|
* hexadecimal notation. Note that character values
|
|
* <DEL>, 28, 29, 30, and 31 can appear in MIC-E message.
|
|
*
|
|
* Inputs: pstr - Pointer to string.
|
|
*
|
|
* len - Maximum length if not -1.
|
|
*
|
|
* ascii_only - Restrict output to only ASCII.
|
|
* Normally we allow UTF-8.
|
|
*
|
|
* Stops after non-zero len characters or at nul.
|
|
*
|
|
* Returns: none
|
|
*
|
|
* Description: Print a string in a "safe" manner.
|
|
* Anything that is not a printable character
|
|
* will be converted to a hexadecimal representation.
|
|
* For example, a Line Feed character will appear as <0x0a>
|
|
* rather than dropping down to the next line on the screen.
|
|
*
|
|
* ax25_from_text can accept this format.
|
|
*
|
|
*
|
|
* Example: W1MED-1>T2QP0S,N1OHZ,N8VIM*,WIDE1-1:'cQBl <0x1c>-/]<0x0d>
|
|
* ------ ------
|
|
*
|
|
* Questions: What should we do about UTF-8? Should that be displayed
|
|
* as hexadecimal for troubleshooting? Maybe an option so the
|
|
* packet raw data is in hexadecimal but an extracted
|
|
* comment displays UTF-8? Or a command line option for only ASCII?
|
|
*
|
|
*------------------------------------------------------------------*/
|
|
|
|
#define MAXSAFE 500
|
|
|
|
void ax25_safe_print (char *pstr, int len, int ascii_only)
|
|
{
|
|
int ch;
|
|
char safe_str[MAXSAFE*6+1];
|
|
int safe_len;
|
|
|
|
safe_len = 0;
|
|
safe_str[safe_len] = '\0';
|
|
|
|
|
|
if (len < 0)
|
|
len = strlen(pstr);
|
|
|
|
if (len > MAXSAFE)
|
|
len = MAXSAFE;
|
|
|
|
while (len > 0 && *pstr != '\0')
|
|
{
|
|
ch = *((unsigned char *)pstr);
|
|
|
|
if (ch < ' ' || ch == 0x7f || ch == 0xfe || ch == 0xff ||
|
|
(ascii_only && ch >= 0x80) ) {
|
|
|
|
/* Control codes and delete. */
|
|
/* UTF-8 does not use fe and ff except in a possible */
|
|
/* "Byte Order Mark" (BOM) at the beginning. */
|
|
|
|
sprintf (safe_str + safe_len, "<0x%02x>", ch);
|
|
safe_len += 6;
|
|
}
|
|
else {
|
|
/* Let everything else thru so we can handle UTF-8 */
|
|
/* Maybe we should have an option to display 0x80 */
|
|
/* and above as hexadecimal. */
|
|
|
|
safe_str[safe_len++] = ch;
|
|
safe_str[safe_len] = '\0';
|
|
}
|
|
|
|
pstr++;
|
|
len--;
|
|
}
|
|
|
|
dw_printf ("%s", safe_str);
|
|
|
|
} /* end ax25_safe_print */
|
|
|
|
/* end ax25_pad.c */
|
|
|
|
|