patty/src/frame.c

468 lines
12 KiB
C
Raw Normal View History

#include <string.h>
#include <inttypes.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,
size_t offset,
size_t len) {
int i, space = 0;
if (len < offset + sizeof(patty_ax25_addr)) {
errno = EIO;
goto error;
}
2020-06-07 02:46:12 -04:00
for (i=0; i<PATTY_AX25_CALLSIGN_LEN; i++) {
uint8_t b = ((uint8_t *)data + offset)[i];
uint8_t c = b >> 1;
if (!PATTY_AX25_ADDR_CHAR_VALID(c) || PATTY_AX25_ADDR_OCTET_LAST(b)) {
errno = EIO;
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) {
errno = EIO;
2020-06-07 02:46:12 -04:00
goto error;
}
}
2020-06-07 03:10:05 -04:00
memcpy(addr, ((uint8_t *)data) + offset, sizeof(*addr));
return PATTY_AX25_ADDR_SIZE;
error:
return -1;
}
static ssize_t decode_hops(patty_ax25_frame *frame,
void *data,
size_t offset,
size_t len) {
ssize_t start = offset;
2020-06-07 02:46:12 -04:00
patty_ax25_addr *addr = NULL;
int i;
/*
2020-06-07 02:46:12 -04:00
* Try to count the AX.25-specified maximum number of hops in the current
* frame.
*/
for (i=0; i<PATTY_AX25_MAX_HOPS; i++) {
ssize_t decoded;
2020-06-07 02:46:12 -04:00
addr = (patty_ax25_addr *)((uint8_t *)data + offset);
if ((decoded = decode_station(&frame->repeaters[i], data, offset, len)) < 0) {
goto error;
} else {
offset += decoded;
}
frame->hops++;
if (PATTY_AX25_ADDR_OCTET_LAST(addr->ssid)) {
break;
}
}
/*
* If the last hop does not have the address extension bit set, then
* that's a big problem.
*/
if (addr && !PATTY_AX25_ADDR_OCTET_LAST(addr->ssid)) {
errno = EIO;
goto error;
}
return offset - start;
error:
return -1;
}
ssize_t patty_ax25_frame_decode_address(patty_ax25_frame *frame,
void *buf,
size_t len) {
size_t offset = 0;
2015-07-18 02:22:25 -05:00
ssize_t decoded;
if ((decoded = decode_station(&frame->dest, buf, offset, len)) < 0) {
2015-07-18 02:22:25 -05:00
goto error;
} else {
offset += decoded;
}
if ((decoded = decode_station(&frame->src, buf, offset, len)) < 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.
*/
frame->hops = 0;
if (!PATTY_AX25_ADDR_OCTET_LAST(frame->src.ssid)) {
if ((decoded = decode_hops(frame, buf, offset, len)) < 0) {
2015-07-18 02:22:25 -05:00
goto error;
} else {
offset += decoded;
}
}
return offset;
2015-07-18 02:22:25 -05:00
error:
return -1;
}
static inline uint8_t decode_nr(uint16_t control,
enum patty_ax25_frame_format format) {
switch (format) {
case PATTY_AX25_FRAME_NORMAL: return (control & 0x00e0) >> 5;
case PATTY_AX25_FRAME_EXTENDED: return (control & 0x7e00) >> 9;
}
return 0;
}
static inline uint8_t decode_ns(uint16_t control,
enum patty_ax25_frame_format format) {
switch (format) {
case PATTY_AX25_FRAME_NORMAL: return (control & 0x000e) >> 1;
case PATTY_AX25_FRAME_EXTENDED: return (control & 0x007e) >> 1;
}
return 0;
}
2020-06-10 22:46:30 -04:00
static inline uint8_t decode_pf(uint16_t control,
enum patty_ax25_frame_format format) {
switch (format) {
case PATTY_AX25_FRAME_NORMAL: return (control & 0x0010) >> 4;
case PATTY_AX25_FRAME_EXTENDED: return (control & 0x0100) >> 8;
}
2020-06-10 22:46:30 -04:00
return 0;
}
ssize_t patty_ax25_frame_decode_control(patty_ax25_frame *frame,
enum patty_ax25_frame_format format,
void *data,
size_t offset,
size_t len) {
uint8_t *buf = data;
size_t start = offset;
frame->control = 0;
frame->nr = 0;
frame->ns = 0;
frame->pf = 0;
frame->type = PATTY_AX25_FRAME_UNKNOWN;
frame->proto = PATTY_AX25_PROTO_UNKNOWN;
frame->info = NULL;
frame->infolen = 0;
switch (format) {
case PATTY_AX25_FRAME_NORMAL:
frame->control = (uint16_t)(buf[offset++]);
break;
case PATTY_AX25_FRAME_EXTENDED:
frame->control = (uint16_t)(buf[offset++] << 8);
frame->control |= (uint16_t)(buf[offset++]);
if (PATTY_AX25_FRAME_CONTROL_U(frame->control)) {
errno = EIO;
goto error;
}
}
if (PATTY_AX25_FRAME_CONTROL_I(frame->control)) {
frame->type = PATTY_AX25_FRAME_I;
frame->nr = decode_nr(frame->control, format);
frame->ns = decode_ns(frame->control, format);
frame->pf = decode_pf(frame->control, format);
} else if (PATTY_AX25_FRAME_CONTROL_S(frame->control)) {
uint16_t c = frame->control & PATTY_AX25_FRAME_S_MASK;
switch (c) {
case PATTY_AX25_FRAME_RR:
case PATTY_AX25_FRAME_RNR:
case PATTY_AX25_FRAME_REJ:
case PATTY_AX25_FRAME_SREJ:
frame->type = c;
frame->nr = decode_nr(frame->control, format);
frame->pf = decode_pf(frame->control, format);
default:
break;
}
} else if (PATTY_AX25_FRAME_CONTROL_U(frame->control)) {
uint16_t c = frame->control & PATTY_AX25_FRAME_U_MASK;
switch (c) {
case PATTY_AX25_FRAME_SABME:
case PATTY_AX25_FRAME_SABM:
case PATTY_AX25_FRAME_DISC:
case PATTY_AX25_FRAME_DM:
case PATTY_AX25_FRAME_UA:
case PATTY_AX25_FRAME_FRMR:
case PATTY_AX25_FRAME_UI:
case PATTY_AX25_FRAME_XID:
case PATTY_AX25_FRAME_TEST:
frame->type = c;
frame->pf = decode_pf(frame->control, format);
default:
break;
}
} else {
2020-05-23 13:21:34 -04:00
errno = EIO;
goto error;
}
switch (frame->type) {
case PATTY_AX25_FRAME_I:
case PATTY_AX25_FRAME_UI:
frame->proto = buf[offset++];
frame->info = buf + offset;
frame->infolen = len - offset;
2020-05-23 13:21:34 -04:00
offset = len;
default:
break;
}
errno = 0;
return offset - start;
error:
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,
enum patty_ax25_frame_format format,
2020-06-28 23:54:22 -04:00
void *buf,
2020-06-27 23:54:09 -04:00
size_t len) {
2020-06-28 23:27:40 -04:00
size_t offset;
2020-06-27 23:54:09 -04:00
2020-06-28 23:54:22 -04:00
if ((offset = encode_address(buf,
&frame->dest,
&frame->src,
frame->repeaters,
frame->hops,
len)) < 0) {
goto error_toobig;
}
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_FRAME_CONTROL_I(frame->control) || PATTY_AX25_FRAME_CONTROL_UI(frame->control)) {
2020-06-27 23:54:09 -04:00
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,
2020-06-29 00:10:48 -04:00
patty_ax25_frame *reply,
2020-06-28 14:00:50 -04:00
enum patty_ax25_frame_format format,
2020-06-29 00:10:48 -04:00
void *buf,
2020-06-28 14:00:50 -04:00
size_t len) {
2020-06-29 00:10:48 -04:00
size_t offset;
2020-06-28 14:00:50 -04:00
2020-06-29 00:10:48 -04:00
if ((offset = encode_reply_address(frame, buf, len)) < 0) {
goto error_toobig;
}
2020-06-28 14:00:50 -04:00
switch (format) {
case PATTY_AX25_FRAME_NORMAL:
2020-06-29 00:10:48 -04:00
((uint8_t *)buf)[offset++] = reply->control;
2020-06-28 14:00:50 -04:00
break;
case PATTY_AX25_FRAME_EXTENDED:
2020-06-29 00:10:48 -04:00
((uint8_t *)buf)[offset++] = (reply->control & 0xff00) >> 8;
((uint8_t *)buf)[offset++] = reply->control & 0xf00ff;
2020-06-28 14:00:50 -04:00
break;
}
if (PATTY_AX25_FRAME_CONTROL_I(reply->control)) {
2020-06-29 00:10:48 -04:00
if (len < 1 + offset + reply->infolen) {
2020-06-28 14:00:50 -04:00
goto error_toobig;
}
2020-06-29 00:10:48 -04:00
((uint8_t *)buf)[offset++] = reply->proto;
2020-06-28 14:00:50 -04:00
2020-06-29 00:10:48 -04:00
memcpy((uint8_t *)buf + offset, reply->info, reply->infolen);
2020-06-28 14:00:50 -04:00
2020-06-29 00:10:48 -04:00
offset += reply->infolen;
2020-06-28 14:00:50 -04:00
}
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_ADDR_SSID_C(frame->src.ssid) ==
PATTY_AX25_ADDR_SSID_C(frame->dest.ssid)?
2020-06-27 23:54:09 -04:00
PATTY_AX25_2_0: PATTY_AX25_OLD;
}
enum patty_ax25_frame_type patty_ax25_frame_type(patty_ax25_frame *frame) {
return frame->type;
2020-06-27 23:54:09 -04:00
}
enum patty_ax25_frame_cr patty_ax25_frame_cr(patty_ax25_frame *frame) {
return PATTY_AX25_ADDR_SSID_C(frame->src.ssid)?
2020-06-27 23:54:09 -04:00
PATTY_AX25_FRAME_RESPONSE:
PATTY_AX25_FRAME_COMMAND;
}
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;
}
*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;
}