#include #include #include #include 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; } for (i=0; i> 1; if (!PATTY_AX25_ADDR_CHAR_VALID(c) || PATTY_AX25_ADDR_OCTET_LAST(b)) { errno = EIO; goto error; } if (c == ' ' && !space) { space = 1; } else if (c != ' ' && space) { errno = EIO; goto error; } } 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; patty_ax25_addr *addr = NULL; int i; /* * Try to count the AX.25-specified maximum number of hops in the current * frame. */ for (i=0; irepeaters[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; ssize_t decoded; if ((decoded = decode_station(&frame->dest, buf, offset, len)) < 0) { goto error; } else { offset += decoded; } if ((decoded = decode_station(&frame->src, buf, offset, len)) < 0) { 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) { goto error; } else { offset += decoded; } } return offset; 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; } 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; } 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 { 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; offset = len; default: break; } errno = 0; return offset - start; error: return -1; } static ssize_t encode_address(void *buf, patty_ax25_addr *dest, patty_ax25_addr *src, patty_ax25_addr *repeaters, unsigned int hops, size_t len) { size_t offset = 0; unsigned int i; if (hops > PATTY_AX25_MAX_HOPS) { hops = PATTY_AX25_MAX_HOPS; } if (len < (2 + hops) * sizeof(patty_ax25_addr)) { goto error_toobig; } memcpy((uint8_t *)dest + offset, &dest, sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)dest)[offset-1] &= ~1; memcpy((uint8_t *)dest + offset, &src, sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)dest)[offset-1] &= ~1; for (i=0; ihops > 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; irepeaters[i].callsign[0] == '\0') { break; } memcpy((uint8_t *)dest + offset, &frame->repeaters[hops-1-i], sizeof(patty_ax25_addr)); offset += sizeof(patty_ax25_addr); ((uint8_t *)dest)[offset-1] &= ~1; } ((uint8_t *)dest)[offset-1] |= 1; return offset; error_toobig: return -1; } ssize_t patty_ax25_frame_encode(patty_ax25_frame *frame, enum patty_ax25_frame_format format, void *buf, size_t len) { size_t offset; if ((offset = encode_address(buf, &frame->dest, &frame->src, frame->repeaters, frame->hops, len)) < 0) { goto error_toobig; } switch (format) { case PATTY_AX25_FRAME_NORMAL: ((uint8_t *)buf)[offset++] = frame->control; break; case PATTY_AX25_FRAME_EXTENDED: ((uint8_t *)buf)[offset++] = (frame->control & 0xff00) >> 8; ((uint8_t *)buf)[offset++] = frame->control & 0xf00ff; break; } if (PATTY_AX25_FRAME_CONTROL_I(frame->control) || PATTY_AX25_FRAME_CONTROL_UI(frame->control)) { if (1 + offset + frame->infolen > len) { goto error_toobig; } ((uint8_t *)buf)[offset++] = frame->proto; memcpy((uint8_t *)buf + offset, frame->info, frame->infolen); offset += frame->infolen; } return offset; error_toobig: return -1; } ssize_t patty_ax25_frame_encode_reply_to(patty_ax25_frame *frame, patty_ax25_frame *reply, enum patty_ax25_frame_format format, void *buf, size_t len) { size_t offset; if ((offset = encode_reply_address(frame, buf, len)) < 0) { goto error_toobig; } switch (format) { case PATTY_AX25_FRAME_NORMAL: ((uint8_t *)buf)[offset++] = reply->control; break; case PATTY_AX25_FRAME_EXTENDED: ((uint8_t *)buf)[offset++] = (reply->control & 0xff00) >> 8; ((uint8_t *)buf)[offset++] = reply->control & 0xf00ff; break; } if (PATTY_AX25_FRAME_CONTROL_I(reply->control)) { if (len < 1 + offset + reply->infolen) { goto error_toobig; } ((uint8_t *)buf)[offset++] = reply->proto; memcpy((uint8_t *)buf + offset, reply->info, reply->infolen); offset += reply->infolen; } return offset; error_toobig: return -1; } 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)? PATTY_AX25_2_0: PATTY_AX25_OLD; } enum patty_ax25_frame_type patty_ax25_frame_type(patty_ax25_frame *frame) { return frame->type; } enum patty_ax25_frame_cr patty_ax25_frame_cr(patty_ax25_frame *frame) { return PATTY_AX25_ADDR_SSID_C(frame->src.ssid)? 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) { errno = EINVAL; goto error_invalid_args; } *info = frame->info; return frame->infolen; error_invalid_args: return -1; }