2015-07-17 00:45:14 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <patty/ax25.h>
|
|
|
|
|
2020-05-23 15:10:42 -04:00
|
|
|
static ssize_t decode_station(patty_ax25_address *address,
|
|
|
|
void *data,
|
|
|
|
off_t offset) {
|
2015-07-17 00:45:14 +00:00
|
|
|
int i, space = 0;
|
2015-07-17 20:14:48 -05:00
|
|
|
uint8_t ssid = ((uint8_t *)data + offset)[6];
|
2015-07-17 00:45:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* First, unpack each callsign octet, ensuring all are within the 7 bit
|
|
|
|
* ASCII space and do not have the extended bit set to 1.
|
|
|
|
*/
|
2015-07-28 02:16:56 +00:00
|
|
|
for (i=0; i<PATTY_AX25_ADDRESS_CALLSIGN_LEN; i++) {
|
2015-07-17 20:14:48 -05:00
|
|
|
uint8_t b = ((uint8_t *)data + offset)[i];
|
2015-07-17 22:35:07 +00:00
|
|
|
uint8_t c = b >> 1;
|
2015-07-17 00:45:14 +00:00
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
if (!PATTY_AX25_ADDRESS_CHAR_VALID(c) || PATTY_AX25_ADDRESS_OCTET_LAST(b)) {
|
2015-07-17 00:45:14 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == ' ') {
|
|
|
|
/*
|
|
|
|
* Take note if we have reached the end of the call sign.
|
|
|
|
*/
|
|
|
|
if (space == 0) {
|
|
|
|
space = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
address->callsign[i] = '\0';
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we have already reached the end of the callsign, and a
|
|
|
|
* non-space character is encountered, then the address is
|
|
|
|
* erroneous.
|
|
|
|
*/
|
|
|
|
if (space) {
|
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
address->callsign[i] = c;
|
2015-07-17 00:45:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now, unpack the data from the SSID field.
|
|
|
|
*/
|
2015-07-18 02:17:35 -05:00
|
|
|
address->c = PATTY_AX25_ADDRESS_SSID_C(ssid);
|
|
|
|
address->ssid = PATTY_AX25_ADDRESS_SSID_NUMBER(ssid);
|
|
|
|
address->last = PATTY_AX25_ADDRESS_OCTET_LAST(ssid);
|
2015-07-17 00:45:14 +00:00
|
|
|
|
2015-07-28 02:16:56 +00:00
|
|
|
return PATTY_AX25_ADDRESS_SIZE;
|
2015-07-17 00:45:14 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-23 15:10:42 -04:00
|
|
|
static ssize_t decode_hops(patty_ax25_frame *frame,
|
|
|
|
void *data,
|
|
|
|
off_t start) {
|
2015-07-17 22:35:07 +00:00
|
|
|
ssize_t decoded;
|
|
|
|
off_t offset = start;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to decode the AX.25-specified maximum number of hops in the current
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
for (i=0; i<PATTY_AX25_MAX_HOPS; i++) {
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_station(&frame->repeaters[i], data, offset)) < 0) {
|
2015-07-17 22:35:07 +00:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
|
|
|
frame->hops++;
|
|
|
|
|
|
|
|
if (frame->repeaters[i].last) {
|
|
|
|
frame->repeated = frame->repeaters[i].repeated;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the last hop does not have the address extension bit set, then
|
|
|
|
* that's a big problem.
|
|
|
|
*/
|
|
|
|
if (frame->hops && !frame->repeaters[frame->hops-1].last) {
|
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset - start;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-23 15:10:42 -04:00
|
|
|
static ssize_t decode_address(patty_ax25_frame *frame,
|
|
|
|
void *data,
|
|
|
|
off_t start) {
|
2015-07-18 02:22:25 -05:00
|
|
|
off_t offset = start;
|
|
|
|
ssize_t decoded;
|
|
|
|
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_station(&frame->dest, data, offset)) < 0) {
|
2015-07-18 02:22:25 -05:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It would be considered erroneous if the destination address did have the
|
|
|
|
* extension bit set to 1.
|
|
|
|
*/
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_station(&frame->src, data, offset)) < 0) {
|
2015-07-18 02:22:25 -05:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
2015-07-18 02:41:42 -05:00
|
|
|
/*
|
|
|
|
* Determine whether the current frame is a Command or Response, and which
|
|
|
|
* version of AX.25 this frame adheres to.
|
|
|
|
*/
|
|
|
|
if (PATTY_AX25_ADDRESS_SSID_C(frame->src.ssid)) {
|
|
|
|
frame->cr = PATTY_AX25_FRAME_RESPONSE;
|
|
|
|
frame->version = PATTY_AX25_ADDRESS_SSID_C(frame->dest.ssid)?
|
|
|
|
PATTY_AX25_OLD: PATTY_AX25_2_0;
|
|
|
|
} else {
|
|
|
|
frame->cr = PATTY_AX25_FRAME_COMMAND;
|
|
|
|
frame->version = PATTY_AX25_ADDRESS_SSID_C(frame->src.ssid)?
|
|
|
|
PATTY_AX25_OLD: PATTY_AX25_2_0;
|
|
|
|
}
|
|
|
|
|
2015-07-18 02:22:25 -05:00
|
|
|
/*
|
|
|
|
* If the source address is not the final address in the frame, begin
|
|
|
|
* decoding repeater addresses.
|
|
|
|
*/
|
|
|
|
if (!frame->src.last) {
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_hops(frame, data, offset)) < 0) {
|
2015-07-18 02:22:25 -05:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset - start;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-23 15:10:42 -04:00
|
|
|
static ssize_t decode_payload(patty_ax25_frame *frame,
|
|
|
|
void *data,
|
|
|
|
off_t offset) {
|
2015-07-17 20:14:48 -05:00
|
|
|
uint8_t control = ((uint8_t *)data + offset)[0];
|
2015-07-17 22:35:07 +00:00
|
|
|
size_t decoded = 0;
|
|
|
|
|
2015-07-17 20:14:48 -05:00
|
|
|
int info = 0;
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
frame->control = control;
|
|
|
|
|
|
|
|
decoded++;
|
|
|
|
|
|
|
|
if (PATTY_AX25_CONTROL_INFO(control)) {
|
2015-07-17 20:14:48 -05:00
|
|
|
frame->type = PATTY_AX25_FRAME_INFO;
|
2015-07-17 22:35:07 +00:00
|
|
|
|
2015-07-17 20:14:48 -05:00
|
|
|
info = 1;
|
2015-07-17 22:35:07 +00:00
|
|
|
} else if (PATTY_AX25_CONTROL_SUPER(control)) {
|
|
|
|
frame->type = PATTY_AX25_FRAME_SUPER;
|
|
|
|
} else if (PATTY_AX25_CONTROL_UNNUMBERED(control)) {
|
|
|
|
frame->type = PATTY_AX25_FRAME_UNNUMBERED;
|
|
|
|
|
|
|
|
if (PATTY_AX25_CONTROL_UNNUMBERED_INFO(control)) {
|
2015-07-17 20:14:48 -05:00
|
|
|
info = 1;
|
2015-07-17 22:35:07 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
frame->type = PATTY_AX25_FRAME_UNKNOWN;
|
|
|
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2015-07-17 20:14:48 -05:00
|
|
|
/*
|
|
|
|
* If we've received either an Info or Unnumbered Info frame, then decode
|
|
|
|
* the Protocol Identifier field, and calculate the location and size of
|
|
|
|
* the payload as well.
|
|
|
|
*/
|
|
|
|
if (info) {
|
|
|
|
decoded++;
|
|
|
|
|
|
|
|
frame->proto = ((uint8_t *)data + offset)[1];
|
|
|
|
frame->payload = (void *)((uint8_t *)data + offset + decoded);
|
|
|
|
frame->payloadsz = frame->size - offset - decoded;
|
|
|
|
|
|
|
|
decoded += frame->payloadsz;
|
|
|
|
}
|
2015-07-17 22:35:07 +00:00
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-22 21:40:37 -04:00
|
|
|
int patty_ax25_frame_decode(patty_ax25_frame *frame, void *data, size_t size) {
|
2015-07-17 00:45:14 +00:00
|
|
|
ssize_t decoded;
|
2020-05-23 13:21:34 -04:00
|
|
|
off_t offset = 0;
|
2015-07-17 00:45:14 +00:00
|
|
|
|
|
|
|
memset(frame, '\0', sizeof(*frame));
|
|
|
|
|
2015-07-17 20:14:48 -05:00
|
|
|
frame->size = size;
|
|
|
|
|
2015-07-17 00:45:14 +00:00
|
|
|
/*
|
2015-07-18 02:22:25 -05:00
|
|
|
* First, decode the variable-length Address field.
|
2015-07-17 00:45:14 +00:00
|
|
|
*/
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_address(frame, data, offset)) < 0) {
|
2020-05-23 13:21:34 -04:00
|
|
|
errno = EIO;
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
goto error_decode;
|
2015-07-17 00:45:14 +00:00
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
/*
|
2020-05-23 13:21:34 -04:00
|
|
|
* Next, decode the remaining Control Field, optional Protocol Identifier
|
2015-07-17 20:21:00 -05:00
|
|
|
* field, and payload that may follow.
|
2015-07-17 22:35:07 +00:00
|
|
|
*/
|
2020-05-23 15:10:42 -04:00
|
|
|
if ((decoded = decode_payload(frame, data, offset)) < 0) {
|
2020-05-23 13:21:34 -04:00
|
|
|
errno = EIO;
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
goto error_decode;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
2015-07-17 00:45:14 +00:00
|
|
|
}
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
return offset;
|
2015-07-17 00:45:14 +00:00
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
error_decode:
|
2015-07-17 00:45:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2020-05-23 16:03:55 -04:00
|
|
|
|
|
|
|
int patty_ax25_frame_payload(patty_ax25_frame *frame, void **data, size_t *len) {
|
|
|
|
if (frame == NULL || frame->payload == NULL || data == NULL || len == NULL) {
|
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error_invalid_args;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = frame->payload;
|
|
|
|
*len = frame->payloadsz;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error_invalid_args:
|
|
|
|
return -1;
|
|
|
|
}
|