patty/src/server.c

795 lines
18 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <patty/ax25.h>
2020-06-16 18:48:29 -04:00
struct _patty_ax25_server {
patty_list *ifaces;
2020-06-20 00:41:55 -04:00
patty_dict *clients,
*client_requests;
patty_dict *socks_by_fd,
*socks_pending_accept,
*socks_pending_connect,
*socks_established;
2020-06-16 18:48:29 -04:00
int fd_max;
fd_set fds_watch,
fds_r,
fds_w;
2020-06-20 00:41:55 -04:00
patty_list *responses_by_client;
2020-06-16 18:48:29 -04:00
};
2020-06-16 01:59:47 -04:00
int patty_ax25_server_init(patty_ax25_server *server) {
memset(server, '\0', sizeof(*server));
if ((server->ifaces = patty_list_new()) == NULL) {
goto error_list_new_ifaces;
}
2020-06-20 00:41:55 -04:00
if ((server->clients = patty_dict_new()) == NULL) {
goto error_dict_new_clients;
}
if ((server->client_requests = patty_dict_new()) == NULL) {
goto error_dict_new_client_requests;
}
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
}
2020-06-16 01:59:47 -04:00
return 0;
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-20 00:41:55 -04:00
patty_dict_destroy(server->client_requests);
error_dict_new_client_requests:
patty_dict_destroy(server->clients);
error_dict_new_clients:
2020-06-16 01:59:47 -04:00
patty_list_destroy(server->ifaces);
error_list_new_ifaces:
return -1;
}
static int destroy_if(patty_ax25_if *iface, void *ctx) {
patty_ax25_if_destroy(iface);
return 0;
}
void patty_ax25_server_stop(patty_ax25_server *server) {
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);
2020-06-20 00:41:55 -04:00
patty_dict_destroy(server->client_requests);
patty_dict_destroy(server->clients);
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
patty_ax25_server_each_if(server, destroy_if, NULL);
2020-06-16 01:59:47 -04:00
patty_list_destroy(server->ifaces);
}
2020-06-16 18:48:29 -04:00
static void sock_init(patty_ax25_sock *sock, enum patty_ax25_sock_type type) {
2020-06-16 01:59:47 -04:00
memset(sock, '\0', sizeof(*sock));
2020-06-16 18:48:29 -04:00
sock->status = PATTY_AX25_SOCK_CLOSED;
2020-06-16 01:59:47 -04:00
sock->mode = PATTY_AX25_SOCK_DM;
2020-06-16 18:48:29 -04:00
sock->type = type;
2020-06-16 01:59:47 -04:00
sock->n_maxlen = PATTY_AX25_FRAME_DEFAULT_MAXLEN;
sock->n_window = PATTY_AX25_FRAME_DEFAULT_WINDOW;
}
static inline void hash_init(uint32_t *hash) {
*hash = 0xffffffdf;
}
static inline void hash_byte(uint32_t *hash, uint8_t c) {
*hash += c;
*hash += (*hash << 10);
*hash ^= (*hash >> 6);
}
static inline void hash_addr(uint32_t *hash, patty_ax25_addr *addr) {
int i;
for (i=0; i<PATTY_AX25_CALLSIGN_LEN; i++) {
hash_byte(hash, addr->callsign[i] >> 1);
}
hash_byte(hash, PATTY_AX25_ADDRESS_SSID_NUMBER(addr->ssid));
}
static inline void hash_end(uint32_t *hash) {
*hash += (*hash << 3);
*hash ^= (*hash >> 11);
*hash += (*hash << 15);
}
static patty_ax25_sock *sock_by_fd(patty_dict *dict,
int fd) {
return patty_dict_get(dict,
NULL + fd,
sizeof(fd));
}
2020-06-16 01:59:47 -04:00
static patty_ax25_sock *sock_by_addr(patty_dict *dict,
patty_ax25_addr *addr) {
patty_dict_slot *slot;
2020-06-16 01:59:47 -04:00
uint32_t hash;
2020-06-16 01:59:47 -04:00
hash_init(&hash);
hash_addr(&hash, addr);
hash_end(&hash);
2020-06-16 01:59:47 -04:00
if ((slot = patty_dict_slot_find(dict, hash)) == NULL) {
errno = EEXIST;
2020-06-16 01:59:47 -04:00
goto error_dict_slot_find;
2020-06-16 01:59:47 -04:00
}
return (patty_ax25_sock *)slot->value;
2020-06-16 01:59:47 -04:00
error_dict_slot_find:
2020-06-16 01:59:47 -04:00
return NULL;
}
static patty_ax25_sock *sock_by_addrpair(patty_dict *dict,
patty_ax25_addr *local,
patty_ax25_addr *remote) {
2020-06-16 01:59:47 -04:00
patty_dict_slot *slot;
uint32_t hash;
hash_init(&hash);
hash_addr(&hash, local);
hash_addr(&hash, remote);
hash_end(&hash);
2020-06-16 01:59:47 -04:00
if ((slot = patty_dict_slot_find(dict, hash)) == NULL) {
errno = EEXIST;
2020-06-16 01:59:47 -04:00
goto error_dict_slot_find;
}
return (patty_ax25_sock *)slot->value;
error_dict_slot_find:
return NULL;
}
static int sock_save_by_fd(patty_dict *dict, patty_ax25_sock *sock) {
if (patty_dict_set(dict,
NULL + sock->fd,
sizeof(sock->fd),
sock) == NULL) {
goto error_dict_set;
}
return sock->fd;
error_dict_set:
return -1;
}
static int sock_save_by_addr(patty_dict *dict,
patty_ax25_sock *sock,
patty_ax25_addr *addr) {
uint32_t hash;
hash_init(&hash);
hash_addr(&hash, addr);
hash_end(&hash);
if (patty_dict_set_with_hash(dict,
addr,
sizeof(*addr),
sock,
hash) == NULL) {
goto error_dict_set_with_hash;
}
return 0;
error_dict_set_with_hash:
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_init(&hash);
hash_addr(&hash, local);
hash_addr(&hash, remote);
hash_end(&hash);
if (patty_dict_set_with_hash(dict,
sock,
sizeof(*sock),
sock,
hash) == NULL) {
goto error_dict_set_with_hash;
}
return 0;
error_dict_set_with_hash:
return -1;
}
static int sock_delete(patty_ax25_server *server,
patty_ax25_sock *sock) {
uint32_t hash_established,
hash_pending_connect,
hash_pending_accept;
hash_init(&hash_established);
hash_addr(&hash_established, &sock->local);
hash_addr(&hash_established, &sock->remote);
hash_end(&hash_established);
if (patty_dict_delete_with_hash(server->socks_established, hash_established) < 0) {
goto error_dict_delete_established;
}
hash_init(&hash_pending_connect);
hash_addr(&hash_pending_connect, &sock->local);
hash_end(&hash_pending_connect);
if (patty_dict_delete_with_hash(server->socks_pending_connect, hash_pending_connect) < 0) {
goto error_dict_delete_pending_connect;
}
hash_init(&hash_pending_accept);
hash_addr(&hash_pending_accept, &sock->local);
hash_end(&hash_pending_accept);
if (patty_dict_delete(server->socks_by_fd, NULL + sock->fd, sizeof(sock->fd)) < 0) {
goto error_dict_delete_by_fd;
}
free(sock);
return 0;
error_dict_delete_by_fd:
error_dict_delete_pending_connect:
error_dict_delete_established:
return -1;
}
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) {
FD_SET(fd, &server->fds_watch);
if (server->fd_max < fd + 1) {
server->fd_max = fd + 1;
}
2020-06-16 01:59:47 -04:00
}
if (patty_dict_set(server->socks_by_fd, NULL + fd, sizeof(fd), iface) == NULL) {
2020-06-16 18:48:29 -04:00
goto error_dict_set;
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:
error_dict_set:
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) {
FD_CLR(fd, &server->fds_watch);
if (server->fd_max == fd) {
server->fd_max--;
}
if (patty_dict_delete(server->socks_by_fd, NULL + fd, sizeof(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_iterator *iter;
patty_ax25_if *iface;
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
if ((iter = patty_list_start(server->ifaces)) == NULL) {
goto error_list_start;
}
while ((iface = patty_list_next(iter)) != NULL) {
if (strncmp(iface->name, name, sizeof(iface->name)) == 0) {
patty_list_finish(iter);
return iface;
}
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
patty_list_finish(iter);
error_list_start:
return NULL;
}
int patty_ax25_server_each_if(patty_ax25_server *server,
int (*callback)(patty_ax25_if *, void *),
void *ctx) {
patty_list_iterator *iter;
patty_ax25_if *iface;
if ((iter = patty_list_start(server->ifaces)) == NULL) {
goto error_list_start;
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
while ((iface = patty_list_next(iter)) != NULL) {
if (callback(iface, ctx) < 0) {
goto error_callback;
}
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
patty_list_finish(iter);
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:
patty_list_finish(iter);
error_list_start:
2020-06-16 01:59:47 -04:00
return -1;
}
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;
if ((sock = malloc(sizeof(*sock))) == NULL) {
goto error_malloc_sock;
}
2020-06-16 18:48:29 -04:00
sock_init(sock, request.type);
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
if ((sock->fd = open("/dev/null", O_RDWR)) < 0) {
goto error_io;
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
response.ret = sock->fd;
response.eno = 0;
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
}
error_sock_save_by_fd:
2020-06-16 18:48:29 -04:00
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
return 0;
error_io:
2020-06-16 01:59:47 -04:00
free(sock);
error_malloc_sock:
return -1;
}
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.socket)) == 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:
2020-06-16 18:48:29 -04:00
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
return 0;
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.socket)) == 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:
2020-06-16 18:48:29 -04:00
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
return 0;
error_io:
2020-06-16 01:59:47 -04:00
return -1;
}
2020-06-16 18:48:29 -04:00
static int server_accept_pty(patty_ax25_server *server,
patty_ax25_call_accept_response *response,
patty_ax25_sock *sock) {
if ((sock->fd = open("/dev/ptmx", O_RDWR)) < 0) {
goto error_open;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
if (grantpt(sock->fd) < 0) {
goto error_grantpt;
2020-06-16 01:59:47 -04:00
}
2020-06-16 18:48:29 -04:00
if (unlockpt(sock->fd) < 0) {
goto error_unlockpt;
}
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
if (ptsname_r(sock->fd, response->path, PATTY_AX25_SOCK_PATH_SIZE) < 0) {
goto error_ptsname_r;
2020-06-16 01:59:47 -04:00
}
sock->type |= PATTY_AX25_SOCK_PTY;
2020-06-16 18:48:29 -04:00
response->ret = sock->fd;
response->eno = 0;
2020-06-16 01:59:47 -04:00
return 0;
2020-06-16 18:48:29 -04:00
error_ptsname_r:
error_unlockpt:
error_grantpt:
close(sock->fd);
error_open:
2020-06-16 01:59:47 -04:00
return -1;
}
2020-06-16 18:48:29 -04:00
static int server_accept_unix(patty_ax25_server *server,
patty_ax25_call_accept_response *response,
patty_ax25_sock *sock) {
struct sockaddr_un addr;
if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
goto error_socket;
}
if (snprintf(response->path, PATTY_AX25_SOCK_PATH_SIZE, "/var/run/patty/%d.sock", sock->fd) < 0) {
goto error_snprintf;
}
memset(&addr, '\0', sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, response->path, sizeof(addr.sun_path));
if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
goto error_bind;
}
sock->type |= PATTY_AX25_SOCK_UNIX;
2020-06-16 18:48:29 -04:00
response->ret = sock->fd;
response->eno = 0;
return 0;
error_bind:
error_snprintf:
close(sock->fd);
error_socket:
return -1;
}
static int server_accept(patty_ax25_server *server,
int client) {
patty_ax25_call_accept_request request;
patty_ax25_call_accept_response response;
patty_ax25_sock *local,
*remote;
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((local = sock_by_fd(server->socks_by_fd, request.socket)) == 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 ((remote = malloc(sizeof(*remote))) == NULL) {
goto error_malloc_remote;
}
sock_init(remote, local->type);
memcpy(&remote->local, &local->local, sizeof(remote->local));
2020-06-16 18:48:29 -04:00
if (local->type & PATTY_AX25_SOCK_PTY) {
if (server_accept_pty(server, &response, remote) < 0) {
goto error_server_accept_pty;
}
} else {
if (server_accept_unix(server, &response, remote) < 0) {
goto error_server_accept_unix;
}
}
if (sock_save_by_addr(server->socks_pending_accept,
remote,
&local->local) < 0) {
goto error_save_by_addr;
}
if (sock_save_by_fd(server->socks_by_fd, remote) < 0) {
goto error_save_by_fd;
}
return 0;
error_sock_by_fd:
2020-06-16 18:48:29 -04:00
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
return 0;
error_save_by_fd:
error_save_by_addr:
2020-06-16 18:48:29 -04:00
error_server_accept_unix:
error_server_accept_pty:
free(remote);
2020-06-16 01:59:47 -04:00
2020-06-16 18:48:29 -04:00
error_malloc_remote:
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;
if (read(client, &request, sizeof(request)) < 0) {
goto error_io;
}
if ((sock = sock_by_fd(server->socks_by_fd, request.socket)) == NULL) {
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
}
memcpy(&sock->remote, &request.peer, sizeof(request.peer));
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;
}
response.ret = 0;
response.eno = 0;
error_invalid_status:
2020-06-18 18:50:24 -04:00
error_sock_by_fd:
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
return 0;
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.socket)) == NULL) {
response.ret = -1;
response.eno = EBADF;
goto error_sock_by_fd;
}
if (sock_delete(server, sock) < 0) {
response.ret = -1;
response.eno = EBADF;
goto error_sock_delete;
}
if (close(request.socket) < 0) {
response.ret = -1;
response.eno = errno;
goto error_close;
}
response.ret = 0;
response.eno = 0;
error_close:
error_sock_delete:
error_sock_by_fd:
if (write(client, &response, sizeof(response)) < 0) {
goto error_io;
}
return 0;
error_io:
return -1;
}