2020-09-14 00:24:59 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
|
|
|
#include <patty/ax25/aprs_is.h>
|
|
|
|
|
|
|
|
enum state {
|
|
|
|
APRS_IS_HEADER,
|
|
|
|
APRS_IS_COMMENT,
|
|
|
|
APRS_IS_BODY,
|
|
|
|
APRS_IS_COMPLETE
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _patty_ax25_aprs_is {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
char rx_buf[PATTY_AX25_APRS_IS_PACKET_MAX],
|
|
|
|
tx_buf[PATTY_AX25_APRS_IS_FRAME_MAX],
|
|
|
|
station[PATTY_AX25_ADDRSTRLEN+1],
|
|
|
|
info[PATTY_AX25_APRS_IS_PAYLOAD_MAX];
|
|
|
|
|
|
|
|
patty_ax25_frame frame;
|
|
|
|
|
|
|
|
size_t rx_bufsz,
|
|
|
|
tx_bufsz,
|
|
|
|
infosz;
|
|
|
|
|
|
|
|
enum state state;
|
|
|
|
|
|
|
|
size_t offset_i,
|
|
|
|
offset_station,
|
|
|
|
offset_info;
|
|
|
|
|
|
|
|
ssize_t readlen,
|
|
|
|
encoded;
|
|
|
|
};
|
|
|
|
|
|
|
|
static ssize_t send_line(patty_ax25_aprs_is *aprs, char *buf, size_t len) {
|
|
|
|
if (len > PATTY_AX25_APRS_IS_PACKET_MAX - 2) {
|
|
|
|
len = PATTY_AX25_APRS_IS_PACKET_MAX - 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[len] = '\r';
|
|
|
|
buf[len+1] = '\n';
|
|
|
|
|
|
|
|
return write(aprs->fd, buf, len+2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_va(patty_ax25_aprs_is *aprs, const char *fmt, va_list args) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (vsnprintf(aprs->tx_buf, PATTY_AX25_APRS_IS_PACKET_MAX, fmt, args) < 0) {
|
|
|
|
goto error_vsnprintf;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = strnlen(aprs->tx_buf, PATTY_AX25_APRS_IS_PACKET_MAX);
|
|
|
|
|
|
|
|
if (send_line(aprs, aprs->tx_buf, len) < 0) {
|
|
|
|
goto error_send_line;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error_send_line:
|
|
|
|
error_vsnprintf:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_fmt(patty_ax25_aprs_is *aprs, const char *fmt, ...) {
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
|
|
|
if (send_va(aprs, fmt, args) < 0) {
|
|
|
|
goto error_send_va;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error_send_va:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-09-18 17:57:34 -05:00
|
|
|
static int aprs_is_connect(patty_ax25_aprs_is *aprs,
|
|
|
|
patty_ax25_aprs_is_info *info) {
|
2020-09-14 00:24:59 -04:00
|
|
|
struct addrinfo *ai0,
|
|
|
|
*ai;
|
|
|
|
|
|
|
|
if (getaddrinfo(info->host, info->port, NULL, &ai0) < 0) {
|
|
|
|
goto error_getaddrinfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ai=ai0; ai; ai=ai->ai_next) {
|
|
|
|
if ((aprs->fd = socket(ai->ai_family,
|
|
|
|
ai->ai_socktype,
|
|
|
|
ai->ai_protocol)) < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connect(aprs->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
|
|
|
close(aprs->fd);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai0);
|
|
|
|
|
|
|
|
if (send_fmt(aprs,
|
|
|
|
"user %s pass %s vers %s %s filter %s",
|
|
|
|
info->user,
|
|
|
|
info->pass,
|
|
|
|
info->appname,
|
|
|
|
info->version,
|
|
|
|
info->filter) < 0) {
|
|
|
|
goto error_send_fmt;
|
|
|
|
}
|
|
|
|
|
2020-09-18 17:57:34 -05:00
|
|
|
return 0;
|
2020-09-14 00:24:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
error_send_fmt:
|
|
|
|
close(aprs->fd);
|
2020-09-18 17:57:34 -05:00
|
|
|
|
|
|
|
error_getaddrinfo:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
patty_ax25_aprs_is *patty_ax25_aprs_is_new(patty_ax25_aprs_is_info *info) {
|
|
|
|
patty_ax25_aprs_is *aprs;
|
|
|
|
|
|
|
|
if ((aprs = malloc(sizeof(*aprs))) == NULL) {
|
|
|
|
goto error_malloc_aprs;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(aprs, '\0', sizeof(*aprs));
|
|
|
|
|
|
|
|
aprs->rx_bufsz = PATTY_AX25_APRS_IS_PACKET_MAX;
|
|
|
|
aprs->tx_bufsz = PATTY_AX25_APRS_IS_FRAME_MAX;
|
|
|
|
aprs->infosz = PATTY_AX25_APRS_IS_PAYLOAD_MAX;
|
|
|
|
aprs->state = APRS_IS_HEADER;
|
|
|
|
|
|
|
|
if (aprs_is_connect(aprs, info) < 0) {
|
|
|
|
goto error_connect;
|
|
|
|
}
|
|
|
|
|
|
|
|
return aprs;
|
|
|
|
|
|
|
|
error_connect:
|
|
|
|
close(aprs->fd);
|
|
|
|
|
2020-09-14 00:24:59 -04:00
|
|
|
free(aprs);
|
|
|
|
|
|
|
|
error_malloc_aprs:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void patty_ax25_aprs_is_destroy(patty_ax25_aprs_is *aprs) {
|
|
|
|
(void)close(aprs->fd);
|
|
|
|
|
|
|
|
free(aprs);
|
|
|
|
}
|
|
|
|
|
|
|
|
int patty_ax25_aprs_is_fd(patty_ax25_aprs_is *aprs) {
|
|
|
|
return aprs->fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t patty_ax25_aprs_is_pending(patty_ax25_aprs_is *aprs) {
|
|
|
|
return aprs->offset_i < aprs->readlen? 1: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t patty_ax25_aprs_is_fill(patty_ax25_aprs_is *aprs) {
|
|
|
|
if ((aprs->readlen = read(aprs->fd, aprs->rx_buf, aprs->rx_bufsz)) < 0) {
|
|
|
|
goto error_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
aprs->offset_i = 0;
|
|
|
|
|
|
|
|
return aprs->readlen;
|
|
|
|
|
|
|
|
error_read:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t patty_ax25_aprs_is_drain(patty_ax25_aprs_is *aprs,
|
|
|
|
void *buf,
|
|
|
|
size_t len) {
|
|
|
|
ssize_t offset_start = aprs->offset_i;
|
|
|
|
|
|
|
|
while (aprs->offset_i < aprs->readlen) {
|
|
|
|
char c = aprs->rx_buf[aprs->offset_i++];
|
|
|
|
|
|
|
|
switch (aprs->state) {
|
|
|
|
case APRS_IS_HEADER: {
|
|
|
|
patty_ax25_addr *addr = NULL;
|
|
|
|
|
|
|
|
if (c == '#') {
|
|
|
|
aprs->state = APRS_IS_COMMENT;
|
|
|
|
} else if (c == '>') {
|
|
|
|
addr = &aprs->frame.src;
|
|
|
|
} else if (c == ',' || c == ':') {
|
|
|
|
if (aprs->frame.dest.callsign[0]) {
|
|
|
|
if (aprs->frame.hops == PATTY_AX25_MAX_HOPS) {
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = &aprs->frame.repeaters[aprs->frame.hops++];
|
|
|
|
} else {
|
|
|
|
addr = &aprs->frame.dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c == ':') {
|
|
|
|
aprs->state = APRS_IS_BODY;
|
|
|
|
}
|
|
|
|
} else if (PATTY_AX25_ADDR_CHAR_VALID(c)) {
|
|
|
|
if (aprs->offset_station == PATTY_AX25_ADDRSTRLEN) {
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
aprs->station[aprs->offset_station++] = c;
|
|
|
|
} else {
|
|
|
|
errno = EINVAL;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr) {
|
|
|
|
aprs->station[aprs->offset_station] = '\0';
|
|
|
|
|
|
|
|
if (patty_ax25_pton(aprs->station, addr) < 0) {
|
|
|
|
(void)patty_ax25_aprs_is_flush(aprs);
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
aprs->offset_station = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case APRS_IS_COMMENT:
|
|
|
|
if (c == '\r') {
|
|
|
|
break;
|
|
|
|
} else if (c == '\n') {
|
|
|
|
aprs->state = APRS_IS_HEADER;
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case APRS_IS_BODY:
|
|
|
|
if (c == '\r') {
|
|
|
|
break;
|
|
|
|
} else if (c == '\n') {
|
|
|
|
aprs->state = APRS_IS_COMPLETE;
|
|
|
|
|
|
|
|
aprs->frame.control = PATTY_AX25_FRAME_UI;
|
|
|
|
aprs->frame.cr = PATTY_AX25_FRAME_COMMAND;
|
|
|
|
aprs->frame.proto = PATTY_AX25_PROTO_NONE;
|
|
|
|
aprs->frame.info = aprs->info;
|
|
|
|
aprs->frame.infolen = aprs->offset_info;
|
|
|
|
|
|
|
|
if ((aprs->encoded = patty_ax25_frame_encode(&aprs->frame,
|
|
|
|
buf,
|
|
|
|
len)) < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
if (aprs->offset_info == aprs->infosz) {
|
|
|
|
errno = EOVERFLOW;
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
aprs->info[aprs->offset_info++] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case APRS_IS_COMPLETE:
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
return aprs->offset_i - offset_start;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int patty_ax25_aprs_is_ready(patty_ax25_aprs_is *aprs) {
|
|
|
|
return aprs->state == APRS_IS_COMPLETE? 1: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t patty_ax25_aprs_is_flush(patty_ax25_aprs_is *aprs) {
|
|
|
|
ssize_t ret = aprs->encoded;
|
|
|
|
|
|
|
|
aprs->state = APRS_IS_HEADER;
|
|
|
|
aprs->offset_i = aprs->readlen;
|
|
|
|
aprs->offset_station = 0;
|
|
|
|
aprs->offset_info = 0;
|
|
|
|
aprs->encoded = 0;
|
|
|
|
|
|
|
|
memset(&aprs->frame, '\0', sizeof(aprs->frame));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int patty_ax25_aprs_is_printf(patty_ax25_aprs_is *aprs, const char *fmt, ...) {
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
|
|
|
if (send_va(aprs, fmt, args) < 0) {
|
|
|
|
goto error_send_va;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error_send_va:
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t patty_ax25_aprs_is_send(patty_ax25_aprs_is *aprs,
|
|
|
|
const void *buf,
|
|
|
|
size_t len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
patty_ax25_if_driver *patty_ax25_aprs_is_driver() {
|
|
|
|
static patty_ax25_if_driver driver = {
|
|
|
|
.create = (patty_ax25_if_driver_create *)patty_ax25_aprs_is_new,
|
|
|
|
.destroy = (patty_ax25_if_driver_destroy *)patty_ax25_aprs_is_destroy,
|
|
|
|
.fd = (patty_ax25_if_driver_fd *)patty_ax25_aprs_is_fd,
|
|
|
|
.pending = (patty_ax25_if_driver_pending *)patty_ax25_aprs_is_pending,
|
|
|
|
.fill = (patty_ax25_if_driver_fill *)patty_ax25_aprs_is_fill,
|
|
|
|
.drain = (patty_ax25_if_driver_drain *)patty_ax25_aprs_is_drain,
|
|
|
|
.ready = (patty_ax25_if_driver_ready *)patty_ax25_aprs_is_ready,
|
|
|
|
.flush = (patty_ax25_if_driver_flush *)patty_ax25_aprs_is_flush,
|
|
|
|
.send = (patty_ax25_if_driver_send *)patty_ax25_aprs_is_send
|
|
|
|
};
|
|
|
|
|
|
|
|
return &driver;
|
|
|
|
};
|