patty/src/server.c

1453 lines
34 KiB
C
Raw Normal View History

2020-06-16 18:48:29 -04:00
#define _GNU_SOURCE
2020-06-16 01:59:47 -04:00
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
2020-06-25 01:04:04 -04:00
#include <sys/types.h>
#include <sys/stat.h>
2020-06-16 01:59:47 -04:00
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <patty/ax25.h>
#include <patty/hash.h>
2020-06-16 01:59:47 -04:00
typedef int (*patty_ax25_server_call)(patty_ax25_server *, int);
2020-06-16 18:48:29 -04:00
struct _patty_ax25_server {
char path[PATTY_AX25_SOCK_PATH_SIZE];
int fd, /* fd of UNIX domain socket */
fd_max;
struct timeval timeout;
2020-07-03 00:13:09 -04:00
fd_set fds_watch, /* fds to monitor with select() */
2020-07-06 23:58:25 -04:00
fds_r; /* fds select()ed for reading */
2020-06-16 18:48:29 -04:00
patty_list *ifaces;
2020-06-25 22:20:39 -04:00
patty_ax25_route_table *routes;
2020-06-16 18:48:29 -04:00
patty_dict *socks_by_fd,
*socks_pending_accept,
*socks_pending_connect,
*socks_established;
2020-06-16 18:48:29 -04:00
patty_dict *clients,
*clients_by_sock;
2020-06-16 18:48:29 -04:00
};
patty_ax25_server *patty_ax25_server_new(const char *path) {
2020-06-24 23:56:31 -04:00
patty_ax25_server *server;
if ((server = malloc(sizeof(*server))) == NULL) {
goto error_malloc_server;
}
2020-06-16 01:59:47 -04:00
memset(server, '\0', sizeof(*server));
strncpy(server->path, path, PATTY_AX25_SOCK_PATH_SIZE);
2020-06-16 01:59:47 -04:00
if ((server->ifaces = patty_list_new()) == NULL) {
goto error_list_new_ifaces;
}
2020-06-25 22:20:39 -04:00
if ((server->routes = patty_ax25_route_table_new()) == NULL) {
goto error_route_table_new;
}
if ((server->socks_by_fd = patty_dict_new()) == NULL) {
goto error_dict_new_socks_by_fd;
2020-06-16 01:59:47 -04:00
}
if ((server->socks_pending_accept = patty_dict_new()) == NULL) {
goto error_dict_new_socks_pending_accept;
}
if ((server->socks_pending_connect = patty_dict_new()) == NULL) {
goto error_dict_new_socks_pending_connect;
}
if ((server->socks_established = patty_dict_new()) == NULL) {
goto error_dict_new_socks_established;
2020-06-16 18:48:29 -04:00
}
if ((server->clients = patty_dict_new()) == NULL) {
goto error_dict_new_clients;
}
if ((server->clients_by_sock = patty_dict_new()) == NULL) {
goto error_dict_new_clients_by_sock;
}
2020-06-24 23:56:31 -04:00
return server;
2020-06-16 01:59:47 -04:00
error_dict_new_clients_by_sock:
patty_dict_destroy(server->clients);
error_dict_new_clients:
patty_dict_destroy(server->socks_established);
error_dict_new_socks_established:
patty_dict_destroy(server->socks_pending_connect);
error_dict_new_socks_pending_connect:
patty_dict_destroy(server->socks_pending_accept);
error_dict_new_socks_pending_accept:
patty_dict_destroy(server->socks_by_fd);
2020-06-16 01:59:47 -04:00
error_dict_new_socks_by_fd:
2020-06-25 22:20:39 -04:00
patty_ax25_route_table_destroy(server->routes);
error_route_table_new:
2020-06-16 01:59:47 -04:00
patty_list_destroy(server->ifaces);
error_list_new_ifaces:
2020-06-24 23:56:31 -04:00
free(server);
error_malloc_server:
return NULL;
2020-06-16 01:59:47 -04:00
}
static int destroy_if(patty_ax25_if *iface, void *ctx) {
patty_ax25_if_destroy(iface);
return 0;
}
void patty_ax25_server_destroy(patty_ax25_server *server) {
patty_dict_destroy(server->clients_by_sock);
patty_dict_destroy(server->clients);
patty_dict_destroy(server->socks_established);
patty_dict_destroy(server->socks_pending_connect);
patty_dict_destroy(server->socks_pending_accept);
patty_dict_destroy(server->socks_by_fd);
patty_ax25_server_each_if(server, destroy_if, NULL);
patty_ax25_route_table_destroy(server->routes);
patty_list_destroy(server->ifaces);
free(server);
}
static inline void watch_fd(patty_ax25_server *server, int fd) {
FD_SET(fd, &server->fds_watch);
if (server->fd_max <= fd) {
server->fd_max = fd + 1;
}
}
static inline void clear_fd(patty_ax25_server *server, int fd) {
int i;
FD_CLR(fd, &server->fds_watch);
for (i=0; i<8*sizeof(&server->fds_watch); i++) {
if (FD_ISSET(i, &server->fds_watch)) {
server->fd_max = i;
}
}
server->fd_max++;
}
static patty_ax25_sock *sock_by_fd(patty_dict *dict,
int fd) {
return patty_dict_get(dict, (uint32_t)fd);
}
2020-06-16 01:59:47 -04:00
static patty_ax25_sock *sock_by_addr(patty_dict *dict,
patty_ax25_addr *addr) {
uint32_t hash;
2020-06-16 01:59:47 -04:00
2020-06-25 20:37:12 -04:00
patty_hash_init(&hash);
patty_ax25_addr_hash(&hash, addr);
patty_hash_end(&hash);
2020-06-16 01:59:47 -04:00
return patty_dict_get(dict, hash);
2020-06-16 01:59:47 -04:00
}
static patty_ax25_sock *sock_by_addrpair(patty_dict *dict,
patty_ax25_addr *local,
patty_ax25_addr *remote) {
uint32_t hash;
2020-07-03 19:03:24 -04:00
2020-06-25 20:37:12 -04:00
patty_hash_init(&hash);
patty_ax25_addr_hash(&hash, local);
patty_ax25_addr_hash(&hash, remote);
patty_hash_end(&hash);
2020-06-16 01:59:47 -04:00
return patty_dict_get(dict, hash);
2020-06-16 01:59:47 -04:00
}
static int sock_save_by_fd(patty_dict *dict, patty_ax25_sock *sock) {
if (patty_dict_set(dict,
(uint32_t)sock->fd,
sock) == NULL) {
goto error_dict_set;
}
return sock->fd;
error_dict_set:
return -1;
}
static inline int client_by_sock(patty_ax25_server *server,
patty_ax25_sock *sock) {
void *value;
if ((value = patty_dict_get(server->clients_by_sock,
(uint32_t)sock->fd)) == NULL) {
goto error_dict_get;
}
return (int)((int64_t)value);
error_dict_get:
return -1;
}
static inline int client_save_by_sock(patty_ax25_server *server,
int client,
patty_ax25_sock *sock) {
if (patty_dict_set(server->clients_by_sock,
(uint32_t)sock->fd,
NULL + client) == NULL) {
goto error_dict_set;
}
return 0;
error_dict_set:
return -1;
}
static inline int client_delete_by_sock(patty_ax25_server *server,
patty_ax25_sock *sock) {
return patty_dict_delete(server->clients_by_sock, (uint32_t)sock->fd);
}
static inline uint32_t hash_addr(patty_ax25_addr *addr) {
uint32_t hash;
2020-06-25 20:37:12 -04:00
patty_hash_init(&hash);
patty_ax25_addr_hash(&hash, addr);
patty_hash_end(&hash);
return hash;
}
static inline uint32_t hash_addrpair(patty_ax25_addr *local,
patty_ax25_addr *remote) {
uint32_t hash;
patty_hash_init(&hash);
patty_ax25_addr_hash(&hash, local);
patty_ax25_addr_hash(&hash, remote);
patty_hash_end(&hash);
return hash;
}
static int sock_save_by_addr(patty_dict *dict,
patty_ax25_sock *sock,
patty_ax25_addr *addr) {
uint32_t hash = hash_addr(addr);
if (patty_dict_set(dict,
hash,
sock) == NULL) {
goto error_dict_set;
}
2020-07-03 19:03:24 -04:00
return 0;
error_dict_set:
return -1;
}
static int sock_save_by_addrpair(patty_dict *dict,
patty_ax25_sock *sock,
patty_ax25_addr *local,
patty_ax25_addr *remote) {
uint32_t hash = hash_addrpair(local, remote);
if (patty_dict_set(dict,
hash,
sock) == NULL) {
goto error_dict_set;
}
2020-07-03 19:03:24 -04:00
return 0;
error_dict_set:
return -1;
}
2020-06-28 14:00:50 -04:00
static int sock_delete_by_addr(patty_dict *dict,
patty_ax25_addr *addr) {
uint32_t hash = hash_addr(addr);
2020-06-28 14:00:50 -04:00
return patty_dict_delete(dict, hash);
2020-06-28 14:00:50 -04:00
}
2020-06-27 23:54:09 -04:00
static int sock_delete_by_addrpair(patty_dict *dict,
patty_ax25_addr *local,
patty_ax25_addr *remote) {
uint32_t hash = hash_addrpair(local, remote);
2020-06-27 23:54:09 -04:00
return patty_dict_delete(dict, hash);
2020-06-27 23:54:09 -04:00
}
static int sock_close(patty_ax25_server *server,
patty_ax25_sock *sock) {
if (sock->status == PATTY_AX25_SOCK_ESTABLISHED) {
if (patty_ax25_sock_send_disc(sock, PATTY_AX25_FRAME_POLL) < 0) {
goto error_sock_send_disc;
}
2020-06-25 20:37:12 -04:00
if (sock_delete_by_addrpair(server->socks_established,
&sock->local,
&sock->remote) < 0) {
goto error_sock_delete_by_addrpair_established;
}
2020-06-25 20:37:12 -04:00
}
if (sock->status == PATTY_AX25_SOCK_PENDING_CONNECT) {
if (sock_delete_by_addrpair(server->socks_pending_connect,
&sock->local,
&sock->remote) < 0) {
goto error_sock_delete_by_addrpair_pending_connect;
}
}
2020-06-25 20:37:12 -04:00
if (sock->status == PATTY_AX25_SOCK_PENDING_ACCEPT) {
if (sock_delete_by_addr(server->socks_pending_accept,
&sock->local) < 0) {
goto error_sock_delete_by_addr_pending_accept;
}
2020-06-25 20:37:12 -04:00
}
if (patty_dict_delete(server->socks_by_fd, (uint32_t)sock->fd) < 0) {
goto error_dict_delete_by_fd_socks;
}
(void)client_delete_by_sock(server, sock);
2020-06-25 20:37:12 -04:00
clear_fd(server, sock->fd);
patty_ax25_sock_destroy(sock);
return 0;
error_dict_delete_by_fd_socks:
error_sock_delete_by_addr_pending_accept:
error_sock_delete_by_addrpair_pending_connect:
error_sock_delete_by_addrpair_established:
error_sock_send_disc:
return -1;
2020-06-25 20:37:12 -04:00
}
2020-06-16 18:48:29 -04:00
int patty_ax25_server_add_if(patty_ax25_server *server,
patty_ax25_if *iface) {
int fd;
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
2020-06-25 20:37:12 -04:00
watch_fd(server, fd);
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
if (patty_list_append(server->ifaces, iface) == NULL) {
goto error_list_append;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
return 0;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
error_list_append:
2020-06-16 01:59:47 -04:00
return -1;
}
2020-06-16 18:48:29 -04:00
int patty_ax25_server_delete_if(patty_ax25_server *server,
patty_ax25_if *iface) {
patty_list_item *item = server->ifaces->first;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
int fd, i = 0;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
while (item) {
if (item->value == iface) {
if (patty_list_splice(server->ifaces, i) == NULL) {
goto error_list_splice;
}
return 0;
}
item = item->next;
i++;
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
if ((fd = patty_kiss_tnc_fd(iface->tnc)) >= 0) {
2020-06-25 20:37:12 -04:00
watch_fd(server, fd);
2020-06-16 18:48:29 -04:00
if (patty_dict_delete(server->socks_by_fd, (uint32_t)fd) < 0) {
2020-06-16 18:48:29 -04:00
goto error_dict_delete;
}
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
return 0;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
error_dict_delete:
error_list_splice:
return -1;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
patty_ax25_if *patty_ax25_server_get_if(patty_ax25_server *server,
const char *name) {
patty_list_item *item = server->ifaces->first;
2020-06-16 01:59:47 -04:00
while (item) {
patty_ax25_if *iface = item->value;
2020-06-16 18:48:29 -04:00
if (strncmp(iface->name, name, sizeof(iface->name)) == 0) {
return iface;
}
2020-06-16 01:59:47 -04:00
item = item->next;
}
2020-06-16 18:48:29 -04:00
return NULL;
}
int patty_ax25_server_each_if(patty_ax25_server *server,
int (*callback)(patty_ax25_if *, void *),
void *ctx) {
patty_list_item *item = server->ifaces->first;
2020-06-16 18:48:29 -04:00
while (item) {
patty_ax25_if *iface = item->value;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
if (callback(iface, ctx) < 0) {
goto error_callback;
}
2020-06-16 01:59:47 -04:00
item = item->next;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
return 0;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
error_callback:
2020-06-16 01:59:47 -04:00
return -1;
}
int patty_ax25_server_add_route(patty_ax25_server *server,
patty_ax25_route *route) {
return patty_ax25_route_table_add(server->routes, route);
}
2020-06-16 01:59:47 -04:00
static int server_socket(patty_ax25_server *server,
2020-06-16 18:48:29 -04:00
int client) {
patty_ax25_call_socket_request request;
patty_ax25_call_socket_response response;
2020-06-16 01:59:47 -04:00
patty_ax25_sock *sock;
2020-06-25 20:37:12 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_read;
2020-06-16 01:59:47 -04:00
}
if ((sock = patty_ax25_sock_new(request.proto, request.type)) == NULL) {
2020-06-25 20:37:12 -04:00
goto error_sock_new;
2020-06-16 01:59:47 -04:00
}
if (sock_save_by_fd(server->socks_by_fd, sock) < 0) {
2020-06-16 18:48:29 -04:00
response.ret = -1;
response.eno = errno;
goto error_sock_save_by_fd;
2020-06-16 18:48:29 -04:00
}
if (client_save_by_sock(server, client, sock) < 0) {
goto error_client_save_by_sock;
2020-06-30 23:20:12 -04:00
}
response.ret = sock->fd;
response.eno = 0;
memcpy(response.path, sock->path, sizeof(response.path));
return write(client, &response, sizeof(response));
2020-06-16 18:48:29 -04:00
error_client_save_by_sock:
2020-06-30 23:20:12 -04:00
error_sock_save_by_fd:
2020-06-25 20:37:12 -04:00
patty_ax25_sock_destroy(sock);
error_sock_new:
error_read:
2020-06-16 01:59:47 -04:00
return -1;
}
static int server_setsockopt(patty_ax25_server *server,
int client) {
patty_ax25_call_setsockopt_request request;
patty_ax25_call_setsockopt_response response;
patty_ax25_sock *sock;
if (read(client, &request, sizeof(request)) < 0) {
goto error_read;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
}
switch (request.opt) {
case PATTY_AX25_SOCK_IF: {
patty_ax25_call_setsockopt_if data;
patty_ax25_if *iface;
if (sock->type != PATTY_AX25_SOCK_RAW) {
response.ret = -1;
response.eno = EINVAL;
goto error_invalid_type;
}
if (read(client, &data, request.len) < 0) {
goto error_read;
}
if ((iface = patty_ax25_server_get_if(server, data.name)) == NULL) {
response.ret = -1;
response.eno = ENODEV;
goto error_get_if;
}
patty_ax25_sock_bind_if(sock, iface);
if (sock_save_by_fd(server->socks_established, sock) < 0) {
goto error_sock_save_by_fd;
}
watch_fd(server, sock->fd);
break;
}
default:
response.ret = -1;
response.eno = EINVAL;
goto error_invalid_opt;
}
response.ret = 0;
response.eno = 0;
error_sock_by_fd:
error_get_if:
error_invalid_type:
error_invalid_opt:
return write(client, &response, sizeof(response));
error_sock_save_by_fd:
error_read:
return -1;
}
2020-06-16 01:59:47 -04:00
static int server_bind(patty_ax25_server *server,
2020-06-16 18:48:29 -04:00
int client) {
patty_ax25_call_bind_request request;
patty_ax25_call_bind_response response;
2020-06-16 01:59:47 -04:00
patty_ax25_sock *sock;
2020-06-16 18:48:29 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
2020-06-16 18:48:29 -04:00
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
2020-06-16 01:59:47 -04:00
}
switch (sock->status) {
case PATTY_AX25_SOCK_LISTENING:
case PATTY_AX25_SOCK_ESTABLISHED:
response.ret = -1;
response.eno = EINVAL;
2020-06-16 01:59:47 -04:00
goto error_invalid_status;
2020-06-16 01:59:47 -04:00
default:
break;
2020-06-16 18:48:29 -04:00
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
memcpy(&sock->local, &request.peer, sizeof(request.peer));
response.ret = 0;
response.eno = 0;
2020-06-16 18:48:29 -04:00
error_invalid_status:
error_sock_by_fd:
return write(client, &response, sizeof(response));
2020-06-16 18:48:29 -04:00
error_io:
2020-06-16 01:59:47 -04:00
return -1;
}
static int server_listen(patty_ax25_server *server,
2020-06-16 18:48:29 -04:00
int client) {
patty_ax25_call_listen_request request;
patty_ax25_call_listen_response response;
2020-06-16 01:59:47 -04:00
patty_ax25_sock *sock;
2020-06-16 18:48:29 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
2020-06-16 18:48:29 -04:00
response.ret = -1;
response.eno = EBADF;
2020-06-16 01:59:47 -04:00
goto error_sock_by_fd;
2020-06-16 01:59:47 -04:00
}
if (sock->local.callsign[0] == '\0') {
2020-06-16 18:48:29 -04:00
response.eno = EINVAL;
2020-06-16 01:59:47 -04:00
goto error_invalid_fd;
}
2020-06-16 18:48:29 -04:00
sock->status = PATTY_AX25_SOCK_LISTENING;
response.ret = 0;
response.eno = 0;
2020-06-16 01:59:47 -04:00
error_invalid_fd:
error_sock_by_fd:
return write(client, &response, sizeof(response));
2020-06-16 18:48:29 -04:00
error_io:
2020-06-16 01:59:47 -04:00
return -1;
}
2020-06-16 18:48:29 -04:00
static int server_accept(patty_ax25_server *server,
int client) {
patty_ax25_call_accept_request request;
patty_ax25_call_accept_response response;
2020-06-30 23:20:12 -04:00
patty_ax25_sock *sock;
2020-06-16 18:48:29 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
2020-06-16 18:48:29 -04:00
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
2020-06-16 18:48:29 -04:00
}
if (sock_save_by_addr(server->socks_pending_accept,
2020-06-30 23:20:12 -04:00
sock,
&sock->local) < 0) {
goto error_save_by_addr;
}
return 0;
error_sock_by_fd:
return write(client, &response, sizeof(response));
2020-06-16 18:48:29 -04:00
error_save_by_addr:
2020-06-16 18:48:29 -04:00
error_io:
2020-06-16 01:59:47 -04:00
return -1;
}
2020-06-18 18:50:24 -04:00
static int server_connect(patty_ax25_server *server,
int client) {
patty_ax25_call_connect_request request;
patty_ax25_call_connect_response response;
patty_ax25_sock *sock;
patty_ax25_route *route;
patty_ax25_if *iface;
2020-06-18 18:50:24 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
2020-06-18 18:50:24 -04:00
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
}
switch (sock->status) {
case PATTY_AX25_SOCK_LISTENING:
response.ret = -1;
response.eno = EINVAL;
goto error_invalid_status;
break;
case PATTY_AX25_SOCK_ESTABLISHED:
response.ret = -1;
response.eno = EISCONN;
2020-06-18 18:50:24 -04:00
goto error_invalid_status;
break;
default:
break;
2020-06-18 18:50:24 -04:00
}
if (sock->local.callsign[0]) {
/*
* If there is an address already bound to this socket, locate the
* appropriate route.
*/
route = patty_ax25_route_table_find(server->routes, &sock->local);
} else {
/*
* Otherwise, locate the default route.
*/
route = patty_ax25_route_table_default(server->routes);
}
/*
* If no route could be found, then assume the network is down or not
* configured.
*/
if (route == NULL) {
response.ret = -1;
response.eno = ENETDOWN;
goto error_network_down;
}
iface = sock->iface = route->iface;
/*
* If there is no local address bound to this sock, then bind the
* address of the default route interface.
*/
if (sock->local.callsign[0] == '\0') {
memcpy(&sock->local, &iface->addr, sizeof(sock->local));
}
/*
* Bind the requested remote address to the socket.
*/
2020-06-18 18:50:24 -04:00
memcpy(&sock->remote, &request.peer, sizeof(request.peer));
2020-06-25 20:37:12 -04:00
sock->status = PATTY_AX25_SOCK_PENDING_CONNECT;
if (sock_save_by_addrpair(server->socks_pending_connect,
2020-06-18 18:50:24 -04:00
sock,
&sock->local,
&sock->remote) < 0) {
goto error_sock_save_by_addrpair;
}
if (client_save_by_sock(server, client, sock) < 0) {
goto error_client_save_by_sock;
2020-06-30 23:20:12 -04:00
}
/*
* TODO: Send SABM(E) frame; set timer, and await response from peer.
* We will need to know what client to send a response to when either the
* timeout has been exceeded or the peer has sent a UA frame to
* acknowledge and establish the connection.
*/
if (patty_ax25_sock_send_sabm(sock, PATTY_AX25_FRAME_POLL) < 0) {
response.ret = -1;
response.eno = EIO;
goto error_sock_send_sabm;
}
2020-06-30 23:20:12 -04:00
return 0;
error_sock_send_sabm:
error_network_down:
error_invalid_status:
2020-06-18 18:50:24 -04:00
error_sock_by_fd:
return write(client, &response, sizeof(response));
2020-06-18 18:50:24 -04:00
error_client_save_by_sock:
2020-06-18 18:50:24 -04:00
error_sock_save_by_addrpair:
error_io:
return -1;
}
static int server_close(patty_ax25_server *server,
int client) {
patty_ax25_call_close_request request;
patty_ax25_call_close_response response;
patty_ax25_sock *sock;
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.fd)) == NULL) {
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
}
if (sock_close(server, sock) < 0) {
response.ret = -1;
response.eno = EBADF;
goto error_sock_delete;
}
response.ret = 0;
response.eno = 0;
error_sock_delete:
error_sock_by_fd:
return write(client, &response, sizeof(response));
error_io:
return -1;
}
static patty_ax25_server_call server_calls[PATTY_AX25_CALL_COUNT] = {
NULL,
server_socket,
server_setsockopt,
server_bind,
server_listen,
server_accept,
server_connect,
server_close,
NULL,
NULL
};
static int listen_unix(patty_ax25_server *server, const char *path) {
struct sockaddr_un addr;
2020-06-25 01:04:04 -04:00
struct stat st;
if (server->fd) {
errno = EBUSY;
goto error_listening;
}
2020-06-25 01:04:04 -04:00
if (stat(path, &st) >= 0) {
unlink(path);
}
if ((server->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
goto error_socket;
}
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path));
if (bind(server->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
goto error_bind;
}
if (listen(server->fd, 0) < 0) {
goto error_listen;
}
2020-06-25 20:37:12 -04:00
watch_fd(server, server->fd);
return 0;
error_listen:
error_bind:
close(server->fd);
error_socket:
error_listening:
return -1;
}
static int accept_client(patty_ax25_server *server) {
int fd;
struct sockaddr addr;
2020-06-25 20:37:12 -04:00
socklen_t addrlen = sizeof(addr);
memset(&addr, '\0', addrlen);
if (!FD_ISSET(server->fd, &server->fds_r)) {
goto done;
}
if ((fd = accept(server->fd, &addr, &addrlen)) < 0) {
goto error_accept;
}
if (patty_dict_set(server->clients, (uint32_t)fd, NULL + fd) == NULL) {
goto error_dict_set;
}
2020-06-30 23:20:12 -04:00
watch_fd(server, fd);
done:
return 0;
error_dict_set:
close(fd);
error_accept:
return -1;
}
static int handle_client(uint32_t key,
void *value,
void *ctx) {
patty_ax25_server *server = ctx;
int client = (int)key;
ssize_t readlen;
enum patty_ax25_call call;
if (!FD_ISSET(client, &server->fds_r)) {
goto done;
}
2020-06-25 20:37:12 -04:00
if ((readlen = read(client, &call, sizeof(call))) < 0) {
goto error_io;
} else if (readlen == 0) {
2020-06-30 23:20:12 -04:00
clear_fd(server, client);
if (patty_dict_delete(server->clients, key) < 0) {
goto error_dict_delete;
}
if (close(client) < 0) {
goto error_io;
}
goto done;
}
if (call <= PATTY_AX25_CALL_NONE || call >= PATTY_AX25_CALL_COUNT) {
goto error_io;
}
if (server_calls[call] == NULL) {
errno = ENOSYS;
goto error_not_implemented;
}
2020-07-03 19:03:24 -04:00
return server_calls[call](server, client);
done:
return 0;
error_not_implemented:
error_dict_delete:
error_io:
return -1;
}
static int handle_clients(patty_ax25_server *server) {
return patty_dict_each(server->clients, handle_client, server);
}
2020-06-28 14:00:50 -04:00
static void save_reply_addr(patty_ax25_sock *sock,
patty_ax25_frame *frame) {
2020-06-27 23:54:09 -04:00
unsigned int i,
2020-06-28 14:00:50 -04:00
hops = frame->hops > PATTY_AX25_MAX_HOPS?
2020-06-28 21:33:41 -04:00
PATTY_AX25_MAX_HOPS: sock->hops;
2020-06-30 23:20:12 -04:00
memcpy(&sock->remote, &frame->src, sizeof(patty_ax25_addr));
memcpy(&sock->local, &frame->dest, sizeof(patty_ax25_addr));
2020-06-27 23:54:09 -04:00
2020-06-28 14:00:50 -04:00
for (i=0; i<hops; i++) {
memcpy(&sock->repeaters[i],
&frame->repeaters[hops-1-i],
sizeof(patty_ax25_addr));
}
2020-06-28 14:00:50 -04:00
sock->hops = hops;
}
2020-06-29 00:10:48 -04:00
static int reply_to(patty_ax25_if *iface,
patty_ax25_frame *frame,
patty_ax25_frame *reply) {
2020-06-28 14:00:50 -04:00
ssize_t len;
if ((len = patty_ax25_frame_encode_reply_to(frame,
2020-06-29 00:10:48 -04:00
reply,
2020-06-28 14:00:50 -04:00
PATTY_AX25_FRAME_NORMAL,
2020-06-29 00:10:48 -04:00
iface->tx_buf,
2020-06-28 14:00:50 -04:00
iface->tx_bufsz)) < 0) {
goto error_toobig;
}
return patty_ax25_if_send(iface, iface->tx_buf, len);
error_toobig:
return -1;
}
2020-06-29 00:10:48 -04:00
static int reply_dm(patty_ax25_if *iface,
patty_ax25_frame *frame,
int flag) {
2020-06-29 00:10:48 -04:00
patty_ax25_frame reply = {
.control = PATTY_AX25_FRAME_DM | (flag << 4),
2020-06-29 00:10:48 -04:00
.proto = PATTY_AX25_PROTO_NONE,
.info = NULL,
.infolen = 0
};
return reply_to(iface, frame, &reply);
2020-06-28 14:00:50 -04:00
}
2020-06-29 00:10:48 -04:00
static int reply_ua(patty_ax25_if *iface,
patty_ax25_frame *frame,
int flag) {
2020-06-29 00:10:48 -04:00
patty_ax25_frame reply = {
.control = PATTY_AX25_FRAME_UA | (flag << 4),
2020-06-29 00:10:48 -04:00
.proto = PATTY_AX25_PROTO_NONE,
.info = NULL,
.infolen = 0
};
2020-06-30 23:20:12 -04:00
char callsign[7];
uint8_t ssid;
patty_ax25_ntop(&frame->dest, callsign, &ssid, sizeof(callsign));
return reply_to(iface, frame, &reply);
2020-06-30 23:20:12 -04:00
}
static int reply_frmr(patty_ax25_if *iface,
patty_ax25_frame *frame,
int flag) {
patty_ax25_frame reply = {
.control = PATTY_AX25_FRAME_FRMR | (flag << 4),
.proto = PATTY_AX25_PROTO_NONE,
.info = NULL,
.infolen = 0
};
return reply_to(iface, frame, &reply);
2020-06-27 23:54:09 -04:00
}
static int handle_sabm(patty_ax25_server *server,
patty_ax25_if *iface,
patty_ax25_frame *frame) {
2020-06-30 23:20:12 -04:00
int client;
patty_ax25_sock *local, *remote;
patty_ax25_call_accept_response response;
2020-06-30 23:20:12 -04:00
if ((local = sock_by_addr(server->socks_pending_accept,
&frame->dest)) == NULL) {
return reply_dm(iface, frame, PATTY_AX25_FRAME_FINAL);
}
if ((client = client_by_sock(server, local)) < 0) {
goto error_client_by_sock;
2020-06-30 23:20:12 -04:00
}
if ((remote = patty_ax25_sock_new(local->proto, local->type)) == NULL) {
2020-06-30 23:20:12 -04:00
goto error_sock_new;
}
remote->status = PATTY_AX25_SOCK_ESTABLISHED;
remote->mode = PATTY_AX25_SOCK_SABM;
remote->iface = iface;
save_reply_addr(remote, frame);
if (sock_save_by_fd(server->socks_by_fd, remote) < 0) {
goto error_sock_save_by_fd;
}
if (sock_save_by_addrpair(server->socks_established,
remote,
&remote->local,
&remote->remote) < 0) {
goto error_sock_save_by_addrpair;
}
if (sock_delete_by_addr(server->socks_pending_accept,
&local->local) < 0) {
goto error_sock_delete_by_addr;
}
if (reply_ua(iface, frame, PATTY_AX25_FRAME_FINAL) < 0) {
2020-06-30 23:20:12 -04:00
goto error_reply_ua;
}
memset(&response, '\0', sizeof(response));
response.ret = 0;
response.eno = 0;
memcpy(&response.peer, &frame->src, sizeof(patty_ax25_addr));
memcpy(&response.path, &remote->path, sizeof(response.path));
if (write(client, &response, sizeof(response)) < 0) {
goto error_write;
}
watch_fd(server, remote->fd);
2020-06-30 23:20:12 -04:00
return 0;
error_write:
error_reply_ua:
error_sock_delete_by_addr:
error_sock_save_by_addrpair:
error_sock_save_by_fd:
patty_ax25_sock_destroy(remote);
error_sock_new:
error_client_by_sock:
2020-06-30 23:20:12 -04:00
return -1;
}
static int handle_ua(patty_ax25_server *server,
patty_ax25_if *iface,
patty_ax25_frame *frame) {
int client;
patty_ax25_sock *sock;
patty_ax25_call_connect_response response;
if ((sock = sock_by_addrpair(server->socks_pending_connect,
&frame->dest,
&frame->src)) == NULL) {
return reply_frmr(iface, frame, PATTY_AX25_FRAME_FINAL);
2020-06-30 23:20:12 -04:00
}
if ((client = client_by_sock(server, sock)) < 0) {
goto error_client_by_sock;
2020-06-30 23:20:12 -04:00
}
sock->status = PATTY_AX25_SOCK_ESTABLISHED;
sock->mode = PATTY_AX25_SOCK_SABM;
if (sock_save_by_addrpair(server->socks_established,
2020-06-30 23:20:12 -04:00
sock,
&sock->local,
&sock->remote) < 0) {
goto error_save_by_addrpair;
}
2020-06-30 23:20:12 -04:00
if (sock_delete_by_addrpair(server->socks_pending_connect,
&sock->local,
&sock->remote) < 0) {
goto error_delete_by_addrpair;
}
2020-06-30 23:20:12 -04:00
response.ret = 0;
response.eno = 0;
2020-06-30 23:20:12 -04:00
if (write(client, &response, sizeof(response)) < 0) {
goto error_write;
}
watch_fd(server, sock->fd);
2020-06-30 23:20:12 -04:00
return 0;
error_write:
patty_ax25_sock_destroy(sock);
error_delete_by_addrpair:
error_save_by_addrpair:
error_client_by_sock:
2020-06-30 23:20:12 -04:00
return -1;
}
static int handle_dm(patty_ax25_server *server,
patty_ax25_if *iface,
patty_ax25_frame *frame) {
int client;
patty_ax25_sock *sock;
patty_ax25_call_connect_response response;
if ((sock = sock_by_addrpair(server->socks_established,
&frame->dest,
&frame->src)) != NULL) {
return sock_close(server, sock);
}
2020-06-30 23:20:12 -04:00
if ((sock = sock_by_addrpair(server->socks_pending_connect,
&frame->dest,
&frame->src)) == NULL) {
return 0;
}
if ((client = client_by_sock(server, sock)) < 0) {
goto error_client_by_sock;
2020-06-30 23:20:12 -04:00
}
if (sock_delete_by_addrpair(server->socks_pending_connect,
&frame->dest,
&frame->src) < 0) {
goto error_delete_by_addrpair;
}
response.ret = -1;
response.eno = ECONNREFUSED;
patty_ax25_sock_reset(sock);
if (write(client, &response, sizeof(response)) < 0) {
goto error_write;
}
return 0;
error_write:
error_delete_by_addrpair:
error_client_by_sock:
2020-06-30 23:20:12 -04:00
return -1;
}
static int handle_i(patty_ax25_server *server,
patty_ax25_if *iface,
patty_ax25_sock *sock,
patty_ax25_frame *frame) {
if (sock == NULL || sock->status != PATTY_AX25_SOCK_ESTABLISHED) {
return reply_dm(iface, frame, PATTY_AX25_FRAME_FINAL);
2020-06-30 23:20:12 -04:00
}
/*
* TODO: Validate RX and TX sequence numbers
*/
if (sock->seq_recv == frame->ns) {
patty_ax25_sock_seq_recv_incr(sock);
} else {
return patty_ax25_sock_send_rej(sock);
}
2020-07-04 16:48:26 -04:00
return write(sock->fd, frame->info, frame->infolen);
}
static int handle_disc(patty_ax25_server *server,
patty_ax25_if *iface,
patty_ax25_frame *frame) {
patty_ax25_sock *sock;
if ((sock = sock_by_addrpair(server->socks_established,
&frame->dest,
&frame->src)) == NULL) {
return reply_frmr(iface, frame, PATTY_AX25_FRAME_FINAL);
}
if (sock_close(server, sock) < 0) {
goto error_sock_delete;
}
return reply_dm(iface, frame, PATTY_AX25_FRAME_FINAL);
error_sock_delete:
return -1;
}
2020-06-28 14:00:50 -04:00
static int handle_frame(patty_ax25_server *server,
patty_ax25_if *iface,
void *buf,
size_t len) {
patty_ax25_frame frame;
enum patty_ax25_frame_format format = PATTY_AX25_FRAME_NORMAL;
ssize_t decoded;
patty_ax25_sock *sock;
if ((decoded = patty_ax25_frame_decode_address(&frame, buf, len)) < 0) {
2020-06-29 00:29:05 -04:00
iface->stats.dropped++;
2020-06-27 23:54:09 -04:00
goto error_io;
2020-06-25 20:37:12 -04:00
}
2020-06-26 23:03:00 -04:00
if ((sock = sock_by_addrpair(server->socks_established,
&frame.dest,
&frame.src)) != NULL) {
if (sock->mode == PATTY_AX25_SOCK_SABME) {
format = PATTY_AX25_FRAME_EXTENDED;
}
2020-06-27 23:54:09 -04:00
}
if (patty_ax25_frame_decode_control(&frame, format, buf, decoded, len) < 0) {
iface->stats.dropped++;
goto error_io;
}
switch (frame.type) {
case PATTY_AX25_FRAME_I: return handle_i(server, iface, sock, &frame);
case PATTY_AX25_FRAME_SABM: return handle_sabm(server, iface, &frame);
case PATTY_AX25_FRAME_UA: return handle_ua(server, iface, &frame);
case PATTY_AX25_FRAME_DM: return handle_dm(server, iface, &frame);
case PATTY_AX25_FRAME_DISC: return handle_disc(server, iface, &frame);
case PATTY_AX25_FRAME_FRMR: return 0;
default:
break;
}
return reply_frmr(iface, &frame, PATTY_AX25_FRAME_FINAL);
error_io:
return -1;
}
static int handle_iface(patty_ax25_server *server, patty_ax25_if *iface) {
int fd = patty_kiss_tnc_fd(iface->tnc);
void *buf;
ssize_t readlen;
if (!FD_ISSET(fd, &server->fds_r)) {
goto done;
}
if ((readlen = patty_ax25_if_recv(iface, &buf)) < 0) {
goto error_io;
} else if (readlen == 0) {
2020-06-25 20:37:12 -04:00
clear_fd(server, fd);
goto done;
}
2020-06-28 14:00:50 -04:00
if (handle_frame(server, iface, buf, readlen) < 0) {
goto error_io;
}
done:
return 0;
error_io:
return -1;
}
static int handle_ifaces(patty_ax25_server *server) {
patty_list_item *item = server->ifaces->first;
while (item) {
patty_ax25_if *iface = item->value;
if (handle_iface(server, iface) < 0) {
goto error_io;
}
item = item->next;
}
return 0;
error_io:
return -1;
}
static int handle_sock(uint32_t key,
void *value,
void *ctx) {
patty_ax25_server *server = ctx;
patty_ax25_sock *sock = value;
ssize_t len;
if (!FD_ISSET(sock->fd, &server->fds_r)) {
goto done;
}
2020-06-30 23:20:12 -04:00
if ((len = read(sock->fd, sock->buf, sock->bufsz)) < 0) {
if (errno == EIO) {
(void)sock_close(server, sock);
goto done;
}
goto error_io;
} else if (len == 0) {
sock_close(server, sock);
goto done;
}
if (patty_ax25_sock_write(sock, sock->buf, len) < 0) {
2020-06-30 23:20:12 -04:00
goto error_sock_write;
}
done:
return 0;
2020-06-30 23:20:12 -04:00
error_sock_write:
error_io:
return -1;
}
static int handle_socks(patty_ax25_server *server) {
return patty_dict_each(server->socks_established, handle_sock, server);
}
int patty_ax25_server_run(patty_ax25_server *server) {
if (listen_unix(server, server->path) < 0) {
goto error_listen_unix;
}
while (1) {
int nready;
2020-06-21 22:49:19 -04:00
memcpy(&server->fds_r, &server->fds_watch, sizeof(server->fds_r));
if ((nready = select( server->fd_max,
&server->fds_r,
NULL,
NULL,
2020-06-25 01:54:39 -04:00
NULL)) < 0) {
goto error_io;
}
if (handle_clients(server) < 0) {
goto error_io;
}
if (handle_ifaces(server) < 0) {
goto error_io;
}
if (handle_socks(server) < 0) {
goto error_io;
}
2020-06-30 23:20:12 -04:00
if (accept_client(server) < 0) {
goto error_io;
}
}
close(server->fd);
return 0;
error_io:
close(server->fd);
error_listen_unix:
return -1;
}