You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

310 lines
6.8 KiB

/*
Copyright Jeroen Vreeken (jeroen@vreeken.net), 2016
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <eth_ar/fprs.h>
#include <poll.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <resolv.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#ifdef __linux__
#include <linux/sockios.h>
#endif
#include <dml/dml.h>
#include "dml_config.h"
#include "fprs_aprsis.h"
static int fd_is = -1;
static GIOChannel *io_is = NULL;
static int to_is = 0;
static int from_is = 0;
static char *call = NULL;
static char *is_host = NULL;
static int is_port;
static gboolean aprs_is_cb(GIOChannel *source, GIOCondition condition, gpointer arg);
static bool filter_type_message = false;
static void (*message_cb)(struct fprs_frame *) = NULL;
static int tcp_connect(char *host, int port)
{
struct addrinfo *result;
struct addrinfo *entry;
struct addrinfo hints = { 0 };
int error;
int sock = -1;
char port_str[10];
int tcp_connect_timeout = 1;
sprintf(port_str, "%d", port);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(host, port_str, &hints, &result);
if (error) {
fprintf(stderr, "getaddrinfo: %s (%s:%s)\n", gai_strerror(error), host, port_str);
res_init();
return -1;
}
for (entry = result; entry; entry = entry->ai_next) {
if (entry->ai_family == AF_INET6) {
bool ipv6 = atoi(dml_config_value("ipv6", NULL, "1"));
if (!ipv6) {
continue;
}
}
int flags;
sock = socket(entry->ai_family, entry->ai_socktype,
entry->ai_protocol);
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
if (sock >= 0) {
fd_set fdset_tx, fdset_err;
struct timeval tv;
tv.tv_sec = tcp_connect_timeout;
tv.tv_usec = 0;
if (connect(sock, entry->ai_addr, entry->ai_addrlen)) {
int ret;
do {
errno = 0;
FD_ZERO(&fdset_tx);
FD_ZERO(&fdset_err);
FD_SET(sock, &fdset_tx);
FD_SET(sock, &fdset_err);
tv.tv_sec = tcp_connect_timeout;
tv.tv_usec = 0;
ret = select(sock+1, NULL, &fdset_tx, NULL,
&tv);
} while (
ret < 0
&&
(errno == EAGAIN || errno == EINTR ||
errno == EINPROGRESS));
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (sock, SOL_SOCKET, SO_ERROR,
&error, &len );
if (!retval && error) {
close(sock);
sock = -1;
}
}
if (sock >= 0) {
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags & ~O_NONBLOCK);
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
&(int){1}, sizeof(int));
#ifdef __linux__
/* number of probes which may fail */
setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
&(int){5}, sizeof(int));
/* Idle time before starting with probes */
setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,
&(int){10}, sizeof(int));
/* interval between probes */
setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,
&(int){2}, sizeof(int));
#endif
break;
}
}
}
freeaddrinfo(result);
return sock;
}
static int aprsis_open(void)
{
printf("Opening connection to APRS-IS\n");
fd_is = tcp_connect(is_host, is_port);
if (fd_is < 0)
return -1;
char loginline[256];
size_t loginline_len = sizeof(loginline) - 1;
fprs2aprs_login(loginline, &loginline_len, call);
if (write(fd_is, loginline, strlen(loginline)) <= 0)
goto err_write;
if (filter_type_message) {
char *filter = "#filter t/m\r\n";
if (write(fd_is, filter, strlen(filter)) <= 0)
goto err_write;
}
io_is = g_io_channel_unix_new(fd_is);
printf("io_is = %p\n", io_is);
to_is = 0;
from_is = 0;
g_io_channel_set_encoding(io_is, NULL, NULL);
g_io_add_watch(io_is, G_IO_IN, aprs_is_cb, fprs_aprsis_init);
return 0;
err_write:
close(fd_is);
fd_is = -1;
printf("Writing to APRS-IS failed\n");
return -1;
}
static void aprs_is_error(void)
{
close(fd_is);
fd_is = -1;
g_source_remove_by_user_data(fprs_aprsis_init);
g_io_channel_unref(io_is);
io_is = NULL;
printf("Lost connection to APRS-IS (to is: %d, from is: %d)\n", to_is, from_is);
/* Fist attempt to reconnect: */
aprsis_open();
}
static gboolean aprs_is_cb(GIOChannel *source, GIOCondition condition, gpointer arg)
{
static char buffer[1000];
static size_t pos;
ssize_t r;
r = read(fd_is, buffer + pos, 1000 - pos);
// printf("------------------------------------- %zd ----- %zd\n", pos, r);
if (r > 0) {
int i;
char *line = buffer;
for (i = 0; i < r + pos; i++) {
if (buffer[i] == '\r' ||
buffer[i] == '\n') {
buffer[i] = 0;
if (strlen(line)) {
// printf("%s\n", line);
struct fprs_frame *frame = aprs2fprs(buffer);
if (frame) {
if (message_cb) {
from_is++;
message_cb(frame);
}
fprs_frame_destroy(frame);
}
}
line = buffer + i + 1;
}
}
pos = i - (line - buffer);
if (line != buffer + i) {
memmove(buffer, line, i - pos);
}
} else {
if (r == 0) {
printf("Read from aprsis failed\n");
aprs_is_error();
}
return FALSE;
}
return TRUE;
}
int fprs_aprsis_frame(struct fprs_frame *frame, uint8_t *from)
{
if (fd_is < 0) {
aprsis_open();
if (fd_is < 0)
return -1;
}
char aprs[256] = { 0 };
size_t aprs_size = 255;
if (fprs2aprs(aprs, &aprs_size, frame, from, call)) {
printf("Could not convert to APRS-IS frame\n");
return -1;
}
printf("%s", aprs);
if (write(fd_is, aprs, strlen(aprs)) <= 0) {
printf("Write to aprsis failed\n");
aprs_is_error();
return -1;
} else {
to_is++;
}
return 0;
}
int fprs_aprsis_init(char *host, int port, char *mycall, bool req_msg, void (*msg_cb)(struct fprs_frame *))
{
if (fd_is >= 0) {
close(fd_is);
fd_is = -1;
g_io_channel_unref(io_is);
io_is = NULL;
g_source_remove_by_user_data(fprs_aprsis_init);
}
free(is_host);
free(call);
is_host = strdup(host);
is_port = port;
call = strdup(mycall);
filter_type_message = req_msg;
message_cb = msg_cb;
int i;
for (i = 0; i < strlen(call); i++) {
call[i] = toupper(call[i]);
}
return aprsis_open();
}