2015-07-17 00:45:14 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <patty/ax25.h>
|
|
|
|
|
2020-06-07 03:10:05 -04:00
|
|
|
static ssize_t decode_station(patty_ax25_addr *addr,
|
|
|
|
void *data,
|
|
|
|
off_t offset) {
|
2015-07-17 00:45:14 +00:00
|
|
|
int i, space = 0;
|
|
|
|
|
2020-06-07 02:46:12 -04:00
|
|
|
for (i=0; i<PATTY_AX25_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;
|
|
|
|
}
|
|
|
|
|
2020-06-07 02:46:12 -04:00
|
|
|
if (c == ' ' && !space) {
|
2020-05-26 22:08:25 -04:00
|
|
|
space = 1;
|
2020-06-07 03:07:18 -04:00
|
|
|
} else if (c != ' ' && space) {
|
2020-06-07 02:46:12 -04:00
|
|
|
errno = EINVAL;
|
2015-07-17 00:45:14 +00:00
|
|
|
|
2020-06-07 02:46:12 -04:00
|
|
|
goto error;
|
2015-07-17 00:45:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 03:10:05 -04:00
|
|
|
memcpy(addr, ((uint8_t *)data) + offset, sizeof(*addr));
|
|
|
|
|
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;
|
|
|
|
|
2020-06-07 02:46:12 -04:00
|
|
|
patty_ax25_addr *addr = NULL;
|
|
|
|
|
2015-07-17 22:35:07 +00:00
|
|
|
/*
|
2020-06-07 02:46:12 -04:00
|
|
|
* Try to count the AX.25-specified maximum number of hops in the current
|
2015-07-17 22:35:07 +00:00
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
for (i=0; i<PATTY_AX25_MAX_HOPS; i++) {
|
2020-06-07 02:46:12 -04:00
|
|
|
addr = (patty_ax25_addr *)((uint8_t *)data + offset);
|
|
|
|
|
2020-06-07 03:10:05 -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++;
|
|
|
|
|
2020-06-07 02:46:12 -04:00
|
|
|
if (PATTY_AX25_ADDRESS_OCTET_LAST(addr->ssid)) {
|
2015-07-17 22:35:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the last hop does not have the address extension bit set, then
|
|
|
|
* that's a big problem.
|
|
|
|
*/
|
2020-06-07 03:07:18 -04:00
|
|
|
if (addr && !PATTY_AX25_ADDRESS_OCTET_LAST(addr->ssid)) {
|
2015-07-17 22:35:07 +00:00
|
|
|
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-06-07 03:10:05 -04:00
|
|
|
if ((decoded = decode_station(&frame->dest, data, offset)) < 0) {
|
2015-07-18 02:22:25 -05:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
2020-06-07 03:10:05 -04:00
|
|
|
if ((decoded = decode_station(&frame->src, data, offset)) < 0) {
|
2015-07-18 02:22:25 -05:00
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
offset += decoded;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the source address is not the final address in the frame, begin
|
|
|
|
* decoding repeater addresses.
|
|
|
|
*/
|
2020-06-07 02:46:12 -04:00
|
|
|
if (!PATTY_AX25_ADDRESS_OCTET_LAST(frame->src.ssid)) {
|
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-30 23:54:11 -04:00
|
|
|
static ssize_t decode_info(patty_ax25_frame *frame,
|
2020-06-28 22:40:56 -04:00
|
|
|
void *data,
|
|
|
|
off_t offset,
|
|
|
|
size_t size) {
|
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
|
|
|
if (PATTY_AX25_CONTROL_INFO(control)) {
|
2015-07-17 20:14:48 -05:00
|
|
|
info = 1;
|
2015-07-17 22:35:07 +00:00
|
|
|
} else if (PATTY_AX25_CONTROL_UNNUMBERED(control)) {
|
|
|
|
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
|
|
|
}
|
2020-06-28 22:40:56 -04:00
|
|
|
} else if (!PATTY_AX25_CONTROL_SUPER(control)) {
|
2015-07-17 22:35:07 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2020-06-10 22:46:30 -04:00
|
|
|
frame->control = control;
|
|
|
|
|
|
|
|
decoded++;
|
|
|
|
|
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++;
|
|
|
|
|
2020-06-27 23:54:09 -04:00
|
|
|
frame->proto = ((uint8_t *)data + offset)[1];
|
|
|
|
frame->info = (void *)((uint8_t *)data + offset + decoded);
|
|
|
|
frame->infolen = size - offset - decoded;
|
2015-07-17 20:14:48 -05:00
|
|
|
|
2020-06-27 23:54:09 -04:00
|
|
|
decoded += frame->infolen;
|
2015-07-17 20:14:48 -05:00
|
|
|
}
|
2015-07-17 22:35:07 +00:00
|
|
|
|
|
|
|
return decoded;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-30 23:54:11 -04:00
|
|
|
ssize_t patty_ax25_frame_decode(patty_ax25_frame *frame,
|
2020-06-27 23:54:09 -04:00
|
|
|
enum patty_ax25_frame_format format,
|
|
|
|
void *src,
|
2020-05-30 23:54:11 -04:00
|
|
|
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-18 02:22:25 -05:00
|
|
|
* First, decode the variable-length Address field.
|
2015-07-17 00:45:14 +00:00
|
|
|
*/
|
2020-06-27 23:54:09 -04:00
|
|
|
if ((decoded = decode_address(frame, src, 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
|
2020-05-30 23:54:11 -04:00
|
|
|
* field, and Info payload that may follow.
|
2015-07-17 22:35:07 +00:00
|
|
|
*/
|
2020-06-27 23:54:09 -04:00
|
|
|
if ((decoded = decode_info(frame, src, offset, size)) < 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
|
|
|
|
2020-06-28 18:11:49 -04:00
|
|
|
static ssize_t encode_address(void *buf,
|
|
|
|
patty_ax25_addr *dest,
|
|
|
|
patty_ax25_addr *src,
|
|
|
|
patty_ax25_addr *repeaters,
|
|
|
|
unsigned int hops,
|
2020-06-27 23:54:09 -04:00
|
|
|
size_t len) {
|
2020-06-28 18:11:49 -04:00
|
|
|
size_t offset = 0;
|
|
|
|
unsigned int i;
|
2020-06-27 23:54:09 -04:00
|
|
|
|
2020-06-28 18:11:49 -04:00
|
|
|
if (hops > PATTY_AX25_MAX_HOPS) {
|
|
|
|
hops = PATTY_AX25_MAX_HOPS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len < (2 + hops) * sizeof(patty_ax25_addr)) {
|
2020-06-27 23:54:09 -04:00
|
|
|
goto error_toobig;
|
|
|
|
}
|
|
|
|
|
2020-06-28 18:11:49 -04:00
|
|
|
memcpy((uint8_t *)dest + offset, &dest, sizeof(patty_ax25_addr));
|
2020-06-27 23:54:09 -04:00
|
|
|
offset += sizeof(patty_ax25_addr);
|
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
|
|
|
|
2020-06-28 18:11:49 -04:00
|
|
|
memcpy((uint8_t *)dest + offset, &src, sizeof(patty_ax25_addr));
|
2020-06-27 23:54:09 -04:00
|
|
|
offset += sizeof(patty_ax25_addr);
|
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
|
|
|
|
|
|
|
for (i=0; i<hops; i++) {
|
2020-06-28 18:11:49 -04:00
|
|
|
if (repeaters[i].callsign[0] == '\0') {
|
2020-06-27 23:54:09 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy((uint8_t *)dest + offset,
|
2020-06-28 18:11:49 -04:00
|
|
|
&repeaters[i],
|
2020-06-27 23:54:09 -04:00
|
|
|
sizeof(patty_ax25_addr));
|
|
|
|
|
|
|
|
offset += sizeof(patty_ax25_addr);
|
2020-06-28 18:11:49 -04:00
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
2020-06-27 23:54:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
((uint8_t *)dest)[offset-1] |= 1;
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
error_toobig:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-28 14:00:50 -04:00
|
|
|
static ssize_t encode_reply_address(patty_ax25_frame *frame,
|
|
|
|
void *dest,
|
|
|
|
size_t len) {
|
|
|
|
size_t i,
|
|
|
|
offset = 0,
|
|
|
|
hops = frame->hops > PATTY_AX25_MAX_HOPS?
|
|
|
|
PATTY_AX25_MAX_HOPS: frame->hops;
|
|
|
|
|
|
|
|
if ((2 + hops) * sizeof(patty_ax25_addr) > len) {
|
|
|
|
goto error_toobig;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy((uint8_t *)dest + offset, &frame->src, sizeof(patty_ax25_addr));
|
|
|
|
offset += sizeof(patty_ax25_addr);
|
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
|
|
|
|
|
|
|
memcpy((uint8_t *)dest + offset, &frame->dest, sizeof(patty_ax25_addr));
|
|
|
|
offset += sizeof(patty_ax25_addr);
|
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
|
|
|
|
|
|
|
for (i=0; i<hops; i++) {
|
|
|
|
if (frame->repeaters[i].callsign[0] == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy((uint8_t *)dest + offset,
|
|
|
|
&frame->repeaters[hops-1-i],
|
|
|
|
sizeof(patty_ax25_addr));
|
|
|
|
|
|
|
|
offset += sizeof(patty_ax25_addr);
|
2020-06-28 18:11:49 -04:00
|
|
|
((uint8_t *)dest)[offset-1] &= ~1;
|
2020-06-28 14:00:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
((uint8_t *)dest)[offset-1] |= 1;
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
error_toobig:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-27 23:54:09 -04:00
|
|
|
ssize_t patty_ax25_frame_encode(patty_ax25_frame *frame,
|
2020-06-28 14:00:50 -04:00
|
|
|
void *buf,
|
2020-06-27 23:54:09 -04:00
|
|
|
enum patty_ax25_frame_format format,
|
2020-06-28 18:11:49 -04:00
|
|
|
uint16_t control,
|
|
|
|
uint8_t proto,
|
|
|
|
void *info,
|
|
|
|
size_t infolen,
|
2020-06-27 23:54:09 -04:00
|
|
|
size_t len) {
|
|
|
|
size_t offset = 0;
|
|
|
|
|
2020-06-28 18:11:49 -04:00
|
|
|
offset = encode_address(buf,
|
|
|
|
&frame->dest,
|
|
|
|
&frame->src,
|
|
|
|
frame->repeaters,
|
|
|
|
frame->hops,
|
|
|
|
len);
|
2020-06-27 23:54:09 -04:00
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
case PATTY_AX25_FRAME_NORMAL:
|
2020-06-28 14:00:50 -04:00
|
|
|
((uint8_t *)buf)[offset++] = frame->control;
|
2020-06-27 23:54:09 -04:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTY_AX25_FRAME_EXTENDED:
|
2020-06-28 14:00:50 -04:00
|
|
|
((uint8_t *)buf)[offset++] = (frame->control & 0xff00) >> 8;
|
|
|
|
((uint8_t *)buf)[offset++] = frame->control & 0xf00ff;
|
2020-06-27 23:54:09 -04:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PATTY_AX25_CONTROL_INFO(frame->control)) {
|
|
|
|
if (1 + offset + frame->infolen > len) {
|
|
|
|
goto error_toobig;
|
|
|
|
}
|
|
|
|
|
2020-06-28 14:00:50 -04:00
|
|
|
((uint8_t *)buf)[offset++] = frame->proto;
|
2020-06-27 23:54:09 -04:00
|
|
|
|
2020-06-28 14:00:50 -04:00
|
|
|
memcpy((uint8_t *)buf + offset, frame->info, frame->infolen);
|
2020-06-27 23:54:09 -04:00
|
|
|
|
|
|
|
offset += frame->infolen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
error_toobig:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-28 14:00:50 -04:00
|
|
|
ssize_t patty_ax25_frame_encode_reply_to(patty_ax25_frame *frame,
|
|
|
|
void *buf,
|
|
|
|
enum patty_ax25_frame_format format,
|
|
|
|
uint16_t control,
|
|
|
|
uint8_t proto,
|
|
|
|
void *info,
|
|
|
|
size_t infolen,
|
|
|
|
size_t len) {
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
offset = encode_reply_address(frame, buf, len);
|
|
|
|
|
|
|
|
switch (format) {
|
|
|
|
case PATTY_AX25_FRAME_NORMAL:
|
|
|
|
((uint8_t *)buf)[offset++] = control;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PATTY_AX25_FRAME_EXTENDED:
|
|
|
|
((uint8_t *)buf)[offset++] = (control & 0xff00) >> 8;
|
|
|
|
((uint8_t *)buf)[offset++] = control & 0xf00ff;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PATTY_AX25_CONTROL_INFO(control)) {
|
|
|
|
if (1 + offset + infolen > len) {
|
|
|
|
goto error_toobig;
|
|
|
|
}
|
|
|
|
|
|
|
|
((uint8_t *)buf)[offset++] = proto;
|
|
|
|
|
|
|
|
memcpy((uint8_t *)buf + offset, info, infolen);
|
|
|
|
|
|
|
|
offset += infolen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
error_toobig:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-06-27 23:54:09 -04:00
|
|
|
enum patty_ax25_version patty_ax25_frame_version(patty_ax25_frame *frame) {
|
|
|
|
return PATTY_AX25_ADDRESS_SSID_C(frame->src.ssid) ==
|
|
|
|
PATTY_AX25_ADDRESS_SSID_C(frame->dest.ssid)?
|
|
|
|
PATTY_AX25_2_0: PATTY_AX25_OLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum patty_ax25_frame_type patty_ax25_frame_type(patty_ax25_frame *frame) {
|
|
|
|
if (PATTY_AX25_CONTROL_INFO(frame->control)) {
|
|
|
|
return PATTY_AX25_FRAME_INFO;
|
|
|
|
} else if (PATTY_AX25_CONTROL_SUPER(frame->control)) {
|
|
|
|
return PATTY_AX25_FRAME_SUPER;
|
|
|
|
} else if (PATTY_AX25_CONTROL_UNNUMBERED(frame->control)) {
|
|
|
|
return PATTY_AX25_FRAME_UNNUMBERED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PATTY_AX25_FRAME_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum patty_ax25_frame_cr patty_ax25_frame_cr(patty_ax25_frame *frame) {
|
|
|
|
return PATTY_AX25_ADDRESS_SSID_C(frame->src.ssid)?
|
|
|
|
PATTY_AX25_FRAME_RESPONSE:
|
|
|
|
PATTY_AX25_FRAME_COMMAND;
|
|
|
|
}
|
|
|
|
|
2020-05-30 23:54:11 -04:00
|
|
|
ssize_t patty_ax25_frame_info(patty_ax25_frame *frame,
|
|
|
|
void **info) {
|
|
|
|
if (frame == NULL || frame->info == NULL || info == NULL) {
|
2020-05-23 16:03:55 -04:00
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error_invalid_args;
|
|
|
|
}
|
|
|
|
|
2020-05-30 23:54:11 -04:00
|
|
|
*info = frame->info;
|
2020-05-23 16:03:55 -04:00
|
|
|
|
2020-06-27 23:54:09 -04:00
|
|
|
return frame->infolen;
|
2020-05-23 16:03:55 -04:00
|
|
|
|
|
|
|
error_invalid_args:
|
|
|
|
return -1;
|
|
|
|
}
|