Refactor patty_kiss_tnc_recv()

Refactor patty_kiss_tnc_recv() to be much more compact and correct;
ensure the KISS command byte is absolutely required, and that
consecutive frame end markers are handled without error
This commit is contained in:
XANTRONIX Development 2020-05-27 18:17:00 -04:00 committed by XANTRONIX Industrial
parent 06fe62ed7b
commit ede262397b
2 changed files with 111 additions and 175 deletions

View file

@ -35,20 +35,15 @@ int patty_kiss_tnc_fd_unix(patty_kiss_tnc *tnc);
void patty_kiss_tnc_close(patty_kiss_tnc *tnc); void patty_kiss_tnc_close(patty_kiss_tnc *tnc);
void patty_kiss_tnc_drop(patty_kiss_tnc *tnc);
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc); size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc);
void patty_kiss_tnc_flush(patty_kiss_tnc *tnc, size_t len);
ssize_t patty_kiss_tnc_buffer(patty_kiss_tnc *tnc);
ssize_t patty_kiss_tnc_decode(patty_kiss_tnc *tnc, void *frame, size_t *len, int *port);
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc, ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
void **frame, int *port); void **frame,
int *port);
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc, ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *frame, size_t len, int port); const void *frame,
size_t len,
int port);
#endif /* _PATTY_KISS_H */ #endif /* _PATTY_KISS_H */

View file

@ -11,9 +11,10 @@
#include <patty/kiss.h> #include <patty/kiss.h>
enum kiss_flags { enum kiss_flags {
KISS_NONE = 0x00, KISS_NONE = 0,
KISS_FRAME = 0x01, KISS_FRAME = 1 << 0,
KISS_ESCAPE = 0x02 KISS_COMMAND = 1 << 1,
KISS_ESCAPE = 1 << 2
}; };
struct _patty_kiss_tnc { struct _patty_kiss_tnc {
@ -21,8 +22,8 @@ struct _patty_kiss_tnc {
int fd; int fd;
void *frame, void *buf,
*buf; *frame;
size_t bufsz, size_t bufsz,
buflen, buflen,
@ -79,132 +80,35 @@ void patty_kiss_tnc_close(patty_kiss_tnc *tnc) {
free(tnc); free(tnc);
} }
ssize_t patty_kiss_tnc_buffer(patty_kiss_tnc *tnc) { static ssize_t tnc_buffer(patty_kiss_tnc *tnc) {
size_t fillsz = tnc->bufsz - tnc->buflen; ssize_t readlen;
ssize_t readlen = 0;
/* if (tnc->buflen == tnc->bufsz) {
* If the buffer needs to be filled, then fill it. goto full;
*/ }
if (fillsz) {
void *dest = ((uint8_t *)tnc->buf) + tnc->buflen;
if ((readlen = read(tnc->fd, dest, fillsz)) < 0) { if ((readlen = read(tnc->fd, tnc->buf, tnc->bufsz - tnc->buflen)) < 0) {
errno = EIO; goto error_read;
goto error_io;
} }
tnc->buflen += readlen; tnc->buflen += readlen;
}
return readlen; return readlen;
error_io: full:
return -1;
}
ssize_t patty_kiss_tnc_decode(patty_kiss_tnc *tnc, void *frame, size_t *len, int *port) {
size_t i, b;
enum kiss_flags flags = KISS_NONE;
/*
* The default port is always zero, unless a command byte issued from the
* TNC in the first byte of the frame body.
*/
*port = 0;
/*
* Ah, yes, the icky state machine wherein we unescape things off the wire
* and whatnot.
*/
for (i=0, b=0; i<tnc->buflen; i++) {
uint8_t c = ((uint8_t *)tnc->buf)[i];
if (i == 0) {
if (c != PATTY_KISS_FEND) {
/*
* If the first byte is not a frame end, then that's a bad thing.
*/
errno = EIO;
goto error_io;
}
} else if (i == 1) {
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
errno = EIO;
goto error_io;
}
*port = PATTY_KISS_COMMAND_PORT(c);
continue;
}
if ((flags & KISS_FRAME) == 0) {
if (c == PATTY_KISS_FEND) {
flags |= KISS_FRAME;
continue;
}
errno = EIO;
goto error_io;
}
if (flags & KISS_ESCAPE) {
if (c == PATTY_KISS_TFEND) {
((uint8_t *)frame)[b++] = PATTY_KISS_FEND;
} else if (c == PATTY_KISS_TFESC) {
((uint8_t *)frame)[b++] = PATTY_KISS_FESC;
} else {
errno = EIO;
goto error_io;
}
flags &= ~KISS_ESCAPE;
continue;
}
if (flags & KISS_FRAME) {
if (c == PATTY_KISS_FESC) {
flags |= KISS_ESCAPE;
continue;
} else if (c == PATTY_KISS_FEND) {
break;
}
((uint8_t *)frame)[b++] = c;
}
}
/*
* Return empty-handed if we do not have a fully book-ended frame.
*/
if ((flags & KISS_FRAME) == 0) {
*len = 0;
*port = 0;
return 0; return 0;
}
*len = b; error_read:
return i;
error_io:
return -1; return -1;
} }
void patty_kiss_tnc_drop(patty_kiss_tnc *tnc) { static void tnc_flush(patty_kiss_tnc *tnc, size_t len) {
memset(tnc->buf, '\0', tnc->bufsz); memmove(tnc->buf, ((uint8_t *)tnc->buf) + len, tnc->buflen - len);
memset(tnc->frame, '\0', tnc->bufsz);
tnc->buflen -= len;
}
static void tnc_drop(patty_kiss_tnc *tnc) {
tnc->buflen = 0; tnc->buflen = 0;
tnc->dropped++; tnc->dropped++;
} }
@ -213,63 +117,97 @@ size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
return tnc->dropped; return tnc->dropped;
} }
void patty_kiss_tnc_flush(patty_kiss_tnc *tnc, size_t len) { ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc,
/* void **frame,
* Move everything from the buffer not processed up to this point, to the int *port) {
* beginning of the buffer. size_t i = 0,
*/ b = 0;
memmove(tnc->buf, ((uint8_t *)tnc->buf) + len, tnc->buflen - len);
/* enum kiss_flags flags = KISS_NONE;
* Then, decrement the buffer length by the number of bytes already
* processed.
*/
tnc->buflen -= len;
}
ssize_t patty_kiss_tnc_recv(patty_kiss_tnc *tnc, void **frame, int *port) { if (tnc_buffer(tnc) < 0) {
ssize_t buffered, decoded;
size_t framelen;
/*
* Initialize the frame to be returned to the caller.
*/
memset(tnc->frame, '\0', tnc->bufsz);
do {
/*
* Fill the buffer with something to parse.
*/
if ((buffered = patty_kiss_tnc_buffer(tnc)) < 0) {
goto error_io; goto error_io;
} }
/* while (i < tnc->buflen) {
* Try to decode the TNC buffer into the frame. uint8_t c = ((uint8_t *)tnc->buf)[i++];
*/
if ((decoded = patty_kiss_tnc_decode(tnc, tnc->frame, &framelen, port)) < 0) { if (!(flags & KISS_FRAME)) {
patty_kiss_tnc_drop(tnc); if (c == PATTY_KISS_FEND) {
flags |= KISS_FRAME;
continue;
} else {
errno = EIO;
goto error_io;
}
} else {
if (c == PATTY_KISS_FEND) {
if (b > 0) {
flags &= ~KISS_FRAME;
goto done;
}
continue;
}
}
if (!(flags & KISS_COMMAND)) {
if (PATTY_KISS_COMMAND(c) != PATTY_KISS_DATA) {
errno = EIO;
goto error_io; goto error_io;
} }
/* *port = PATTY_KISS_COMMAND_PORT(c);
* If we can no longer buffer nor decode any data, then drop the whole
* frame.
*/
if (decoded == 0 && buffered == 0) {
patty_kiss_tnc_drop(tnc);
}
} while (decoded == 0);
/* flags |= KISS_COMMAND;
* Flush the buffer up to the point of the next frame start.
*/ continue;
patty_kiss_tnc_flush(tnc, decoded); }
if (!(flags & KISS_ESCAPE)) {
if (c == PATTY_KISS_FESC) {
flags |= KISS_ESCAPE;
continue;
}
} else {
switch (c) {
case PATTY_KISS_TFEND:
((uint8_t *)tnc->frame)[b++] = PATTY_KISS_FEND;
flags &= ~KISS_ESCAPE;
continue;
case PATTY_KISS_TFESC:
((uint8_t *)tnc->frame)[b++] = PATTY_KISS_FESC;
flags &= ~KISS_ESCAPE;
continue;
default:
errno = EIO;
goto error_io;
}
}
((uint8_t *)tnc->frame)[b++] = c;
}
done:
if (flags & KISS_FRAME) {
tnc_drop(tnc);
*frame = NULL;
} else {
tnc_flush(tnc, i);
*frame = tnc->frame; *frame = tnc->frame;
}
return framelen; return (ssize_t)b;
error_io: error_io:
return -1; return -1;
@ -286,7 +224,10 @@ static inline ssize_t write_command(int fd, int command, int port) {
static uint8_t escape_fend[2] = { PATTY_KISS_FESC, PATTY_KISS_TFEND }; static uint8_t escape_fend[2] = { PATTY_KISS_FESC, PATTY_KISS_TFEND };
static uint8_t escape_fesc[2] = { PATTY_KISS_FESC, PATTY_KISS_TFESC }; static uint8_t escape_fesc[2] = { PATTY_KISS_FESC, PATTY_KISS_TFESC };
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc, const void *buf, size_t len, int port) { ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc,
const void *buf,
size_t len,
int port) {
size_t i, start = 0, end = 0; size_t i, start = 0, end = 0;
if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) { if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) {