// 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 . // /*------------------------------------------------------------------ * * 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 #include #include #include #include #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; inum_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; jnum_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 * , 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 */