2015-07-14 22:27:42 -05:00
|
|
|
#include <stdlib.h>
|
2015-07-16 00:31:27 +00:00
|
|
|
#include <string.h>
|
2020-05-26 23:41:39 -04:00
|
|
|
#include <inttypes.h>
|
2015-07-14 22:27:42 -05:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2015-07-14 16:45:03 +00:00
|
|
|
#include <sys/types.h>
|
2015-07-14 22:27:42 -05:00
|
|
|
#include <sys/stat.h>
|
2015-07-14 16:45:03 +00:00
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2015-07-12 02:16:26 -05:00
|
|
|
#include <patty/kiss.h>
|
|
|
|
|
|
|
|
enum kiss_flags {
|
|
|
|
KISS_NONE = 0x00,
|
|
|
|
KISS_FRAME = 0x01,
|
|
|
|
KISS_ESCAPE = 0x02
|
|
|
|
};
|
|
|
|
|
2015-07-14 22:27:42 -05:00
|
|
|
struct _patty_kiss_tnc {
|
2015-07-20 23:21:45 -05:00
|
|
|
const char * device;
|
|
|
|
|
2020-05-26 23:52:06 -04:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
void *frame,
|
|
|
|
*buf;
|
|
|
|
|
|
|
|
size_t bufsz,
|
|
|
|
buflen,
|
|
|
|
dropped;
|
2015-07-14 22:27:42 -05:00
|
|
|
};
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
patty_kiss_tnc *patty_kiss_tnc_open(const char *device, size_t bufsz) {
|
2015-07-14 22:27:42 -05:00
|
|
|
patty_kiss_tnc *tnc;
|
|
|
|
|
|
|
|
if ((tnc = malloc(sizeof(*tnc))) == NULL) {
|
|
|
|
goto error_malloc_tnc;
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if ((tnc->buf = malloc(bufsz)) == NULL) {
|
2015-07-14 22:27:42 -05:00
|
|
|
goto error_malloc_buf;
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if ((tnc->frame = malloc(bufsz)) == NULL) {
|
|
|
|
goto error_malloc_frame;
|
|
|
|
}
|
|
|
|
|
2015-07-14 22:27:42 -05:00
|
|
|
if ((tnc->fd = open(device, O_RDWR)) < 0) {
|
|
|
|
goto error_open;
|
|
|
|
}
|
|
|
|
|
2015-07-20 23:21:45 -05:00
|
|
|
tnc->device = device;
|
2015-07-15 21:59:56 -05:00
|
|
|
tnc->bufsz = bufsz;
|
|
|
|
tnc->buflen = 0;
|
2020-05-26 23:52:06 -04:00
|
|
|
tnc->dropped = 0;
|
2015-07-14 22:27:42 -05:00
|
|
|
|
|
|
|
return tnc;
|
|
|
|
|
|
|
|
error_open:
|
2015-07-16 00:31:27 +00:00
|
|
|
free(tnc->frame);
|
|
|
|
|
|
|
|
error_malloc_frame:
|
2015-07-14 22:27:42 -05:00
|
|
|
free(tnc->buf);
|
|
|
|
|
|
|
|
error_malloc_buf:
|
|
|
|
free(tnc);
|
|
|
|
|
|
|
|
error_malloc_tnc:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-29 22:28:44 -05:00
|
|
|
int patty_kiss_tnc_fd_unix(patty_kiss_tnc *tnc) {
|
|
|
|
return tnc->fd;
|
|
|
|
}
|
|
|
|
|
2015-07-14 22:27:42 -05:00
|
|
|
void patty_kiss_tnc_close(patty_kiss_tnc *tnc) {
|
|
|
|
close(tnc->fd);
|
2015-07-15 20:29:50 -05:00
|
|
|
free(tnc->frame);
|
|
|
|
free(tnc->buf);
|
|
|
|
free(tnc);
|
2015-07-14 22:27:42 -05:00
|
|
|
}
|
|
|
|
|
2015-09-17 22:24:57 -05:00
|
|
|
ssize_t patty_kiss_tnc_buffer(patty_kiss_tnc *tnc) {
|
2015-07-15 21:45:46 -05:00
|
|
|
size_t fillsz = tnc->bufsz - tnc->buflen;
|
|
|
|
ssize_t readlen = 0;
|
2015-07-12 02:16:26 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
/*
|
|
|
|
* If the buffer needs to be filled, then fill it.
|
|
|
|
*/
|
|
|
|
if (fillsz) {
|
2020-05-26 23:41:39 -04:00
|
|
|
void *dest = ((uint8_t *)tnc->buf) + tnc->buflen;
|
2015-07-16 00:31:27 +00:00
|
|
|
|
|
|
|
if ((readlen = read(tnc->fd, dest, fillsz)) < 0) {
|
|
|
|
errno = EIO;
|
|
|
|
|
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
tnc->buflen += readlen;
|
2015-07-12 12:00:35 -05:00
|
|
|
}
|
|
|
|
|
2015-07-15 21:45:46 -05:00
|
|
|
return readlen;
|
2015-07-15 20:32:24 -05:00
|
|
|
|
|
|
|
error_io:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-09-17 22:38:51 -05:00
|
|
|
ssize_t patty_kiss_tnc_decode(patty_kiss_tnc *tnc, void *frame, size_t *len, int *port) {
|
2015-07-15 20:32:24 -05:00
|
|
|
size_t i, b;
|
2020-05-26 23:44:10 -04:00
|
|
|
enum kiss_flags flags = KISS_NONE;
|
2015-07-15 20:32:24 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
/*
|
|
|
|
* The default port is always zero, unless a command byte issued from the
|
|
|
|
* TNC in the first byte of the frame body.
|
|
|
|
*/
|
2015-07-12 02:28:31 -05:00
|
|
|
*port = 0;
|
2015-07-12 02:16:26 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
/*
|
|
|
|
* Ah, yes, the icky state machine wherein we unescape things off the wire
|
|
|
|
* and whatnot.
|
|
|
|
*/
|
|
|
|
for (i=0, b=0; i<tnc->buflen; i++) {
|
2020-05-26 23:41:39 -04:00
|
|
|
uint8_t c = ((uint8_t *)tnc->buf)[i];
|
2015-07-16 00:31:27 +00:00
|
|
|
|
2015-07-17 00:45:14 +00:00
|
|
|
/*
|
|
|
|
* If the first byte is not a frame end, then that's a bad thing.
|
|
|
|
*/
|
|
|
|
if (i == 0 && c != PATTY_KISS_FEND) {
|
|
|
|
errno = EIO;
|
|
|
|
|
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (i == 1 && (c & 0x0f) == 0) {
|
|
|
|
*port = (c & 0xf0) >> 4;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-12 02:16:26 -05:00
|
|
|
|
2015-07-14 16:45:03 +00:00
|
|
|
if ((flags & KISS_FRAME) == 0) {
|
2015-07-12 02:16:26 -05:00
|
|
|
if (c == PATTY_KISS_FEND) {
|
|
|
|
flags |= KISS_FRAME;
|
2015-07-16 00:31:27 +00:00
|
|
|
|
|
|
|
continue;
|
2015-07-12 02:16:26 -05:00
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
errno = EIO;
|
|
|
|
|
|
|
|
goto error_io;
|
2015-07-12 02:16:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & KISS_ESCAPE) {
|
|
|
|
if (c == PATTY_KISS_TFEND) {
|
2020-05-26 23:41:39 -04:00
|
|
|
((uint8_t *)frame)[b++] = PATTY_KISS_FEND;
|
2015-07-12 02:16:26 -05:00
|
|
|
} else if (c == PATTY_KISS_TFESC) {
|
2020-05-26 23:41:39 -04:00
|
|
|
((uint8_t *)frame)[b++] = PATTY_KISS_FESC;
|
2015-07-12 02:18:08 -05:00
|
|
|
} else {
|
2015-07-12 12:00:35 -05:00
|
|
|
errno = EIO;
|
|
|
|
|
2015-07-12 02:18:08 -05:00
|
|
|
goto error_io;
|
2015-07-12 02:16:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
flags &= ~KISS_ESCAPE;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & KISS_FRAME) {
|
|
|
|
if (c == PATTY_KISS_FESC) {
|
|
|
|
flags |= KISS_ESCAPE;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else if (c == PATTY_KISS_FEND) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-26 23:41:39 -04:00
|
|
|
((uint8_t *)frame)[b++] = c;
|
2015-07-16 00:31:27 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-12 02:28:31 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
/*
|
|
|
|
* Return empty-handed if we do not have a fully book-ended frame.
|
|
|
|
*/
|
|
|
|
if ((flags & KISS_FRAME) == 0) {
|
2015-07-15 21:16:39 -05:00
|
|
|
*len = 0;
|
|
|
|
*port = 0;
|
2015-07-12 02:28:31 -05:00
|
|
|
|
2015-07-16 00:31:40 +00:00
|
|
|
return 0;
|
2015-07-12 02:16:26 -05:00
|
|
|
}
|
|
|
|
|
2015-07-15 21:16:39 -05:00
|
|
|
*len = b;
|
2015-07-16 00:31:27 +00:00
|
|
|
|
2015-07-15 21:16:39 -05:00
|
|
|
return i;
|
|
|
|
|
|
|
|
error_io:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-09-17 22:42:28 -05:00
|
|
|
void patty_kiss_tnc_drop(patty_kiss_tnc *tnc) {
|
2015-07-15 21:59:08 -05:00
|
|
|
memset(tnc->buf, '\0', tnc->bufsz);
|
|
|
|
memset(tnc->frame, '\0', tnc->bufsz);
|
|
|
|
|
|
|
|
tnc->buflen = 0;
|
2015-07-15 21:59:56 -05:00
|
|
|
tnc->dropped++;
|
2015-07-15 21:59:08 -05:00
|
|
|
}
|
|
|
|
|
2020-05-26 23:52:06 -04:00
|
|
|
size_t patty_kiss_tnc_dropped(patty_kiss_tnc *tnc) {
|
2015-07-15 21:59:56 -05:00
|
|
|
return tnc->dropped;
|
2015-07-15 21:59:08 -05:00
|
|
|
}
|
|
|
|
|
2015-09-17 22:42:28 -05:00
|
|
|
void patty_kiss_tnc_flush(patty_kiss_tnc *tnc, size_t len) {
|
2015-07-15 21:16:39 -05:00
|
|
|
/*
|
|
|
|
* Move everything from the buffer not processed up to this point, to the
|
|
|
|
* beginning of the buffer.
|
|
|
|
*/
|
2020-05-26 23:41:39 -04:00
|
|
|
memmove(tnc->buf, ((uint8_t *)tnc->buf) + len, tnc->buflen - len);
|
2015-07-15 21:16:39 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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) {
|
2015-07-15 21:59:08 -05:00
|
|
|
ssize_t buffered, decoded;
|
2015-07-15 21:16:39 -05:00
|
|
|
size_t framelen;
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
/*
|
2015-07-15 21:16:39 -05:00
|
|
|
* Initialize the frame to be returned to the caller.
|
2015-07-16 00:31:27 +00:00
|
|
|
*/
|
2015-07-15 21:16:39 -05:00
|
|
|
memset(tnc->frame, '\0', tnc->bufsz);
|
|
|
|
|
2015-07-15 21:59:08 -05:00
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* Fill the buffer with something to parse.
|
|
|
|
*/
|
2015-09-17 22:24:57 -05:00
|
|
|
if ((buffered = patty_kiss_tnc_buffer(tnc)) < 0) {
|
2015-07-15 21:59:08 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to decode the TNC buffer into the frame.
|
|
|
|
*/
|
2015-09-17 22:38:51 -05:00
|
|
|
if ((decoded = patty_kiss_tnc_decode(tnc, tnc->frame, &framelen, port)) < 0) {
|
2015-07-15 21:59:08 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we can no longer buffer nor decode any data, then drop the whole
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
if (decoded == 0 && buffered == 0) {
|
2015-09-17 22:42:28 -05:00
|
|
|
patty_kiss_tnc_drop(tnc);
|
2015-07-15 21:59:08 -05:00
|
|
|
}
|
|
|
|
} while (decoded == 0);
|
2015-07-15 21:16:39 -05:00
|
|
|
|
2015-07-15 21:19:34 -05:00
|
|
|
/*
|
2015-07-15 21:59:08 -05:00
|
|
|
* Flush the buffer up to the point of the next frame start.
|
2015-07-15 21:19:34 -05:00
|
|
|
*/
|
2015-09-17 22:42:28 -05:00
|
|
|
patty_kiss_tnc_flush(tnc, decoded);
|
2015-07-15 21:16:39 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
*frame = tnc->frame;
|
|
|
|
|
2015-07-15 21:16:39 -05:00
|
|
|
return framelen;
|
2015-07-12 02:18:08 -05:00
|
|
|
|
|
|
|
error_io:
|
|
|
|
return -1;
|
2015-07-12 02:16:26 -05:00
|
|
|
}
|
2015-07-12 15:34:40 -05:00
|
|
|
|
2020-05-26 23:41:39 -04:00
|
|
|
static inline ssize_t write_byte(int fd, uint8_t c) {
|
2015-07-12 23:48:12 -05:00
|
|
|
return write(fd, &c, sizeof(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline ssize_t write_command(int fd, int command, int port) {
|
|
|
|
return write_byte(fd, ((port & 0x0f) << 4) | (command & 0x0f));
|
|
|
|
}
|
|
|
|
|
2020-05-26 23:41:39 -04:00
|
|
|
static uint8_t escape_fend[2] = { PATTY_KISS_FESC, PATTY_KISS_TFEND };
|
|
|
|
static uint8_t escape_fesc[2] = { PATTY_KISS_FESC, PATTY_KISS_TFESC };
|
2020-05-22 19:34:25 -04:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
ssize_t patty_kiss_tnc_send(patty_kiss_tnc *tnc, const void *buf, size_t len, int port) {
|
2015-07-12 23:48:12 -05:00
|
|
|
size_t i, start = 0, end = 0;
|
2015-07-12 15:34:40 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) {
|
2015-07-12 23:48:12 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
2015-07-12 15:34:40 -05:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (write_command(tnc->fd, PATTY_KISS_DATA, port) < 0) {
|
2015-07-12 15:34:40 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<len; i++) {
|
2020-05-26 23:41:39 -04:00
|
|
|
uint8_t c = ((uint8_t *)buf)[i];
|
|
|
|
uint8_t *escape = NULL;
|
2015-07-12 15:34:40 -05:00
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case PATTY_KISS_FEND: {
|
2015-07-12 23:48:12 -05:00
|
|
|
escape = escape_fend; break;
|
2015-07-12 15:34:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
case PATTY_KISS_FESC: {
|
2015-07-12 23:48:12 -05:00
|
|
|
escape = escape_fesc; break;
|
2015-07-12 15:34:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
2015-07-12 23:48:12 -05:00
|
|
|
end = i;
|
2015-07-14 16:45:03 +00:00
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (write(tnc->fd, &c, 1) < 0) {
|
2015-07-14 16:45:03 +00:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2015-07-12 15:34:40 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-12 23:48:12 -05:00
|
|
|
if (escape) {
|
2020-05-26 23:41:39 -04:00
|
|
|
if (write(tnc->fd, ((uint8_t *)buf) + start, end - start) < 0) {
|
2015-07-12 23:48:12 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (write(tnc->fd, escape, 2) < 0) {
|
2015-07-12 23:48:12 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
escape = NULL;
|
|
|
|
start = i + 1;
|
|
|
|
end = start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end - start) {
|
2020-05-26 23:41:39 -04:00
|
|
|
if (write(tnc->fd, ((uint8_t *)buf) + start, end - start) < 0) {
|
2015-07-12 15:34:40 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:31:27 +00:00
|
|
|
if (write_byte(tnc->fd, PATTY_KISS_FEND) < 0) {
|
2015-07-12 23:48:12 -05:00
|
|
|
goto error_io;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
2015-07-12 15:34:40 -05:00
|
|
|
|
|
|
|
error_io:
|
|
|
|
return -1;
|
|
|
|
}
|