387 lines
8.1 KiB
C
387 lines
8.1 KiB
C
|
#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>
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if ((server->socks = patty_dict_new()) == NULL) {
|
||
|
goto error_dict_new_socks;
|
||
|
}
|
||
|
|
||
|
if ((server->socks_by_addrpair = patty_dict_new()) == NULL) {
|
||
|
goto error_dict_new_socks_by_addrpair;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error_dict_new_socks_by_addrpair:
|
||
|
patty_dict_destroy(server->socks);
|
||
|
|
||
|
error_dict_new_socks:
|
||
|
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_by_addrpair);
|
||
|
patty_dict_destroy(server->socks);
|
||
|
|
||
|
patty_ax25_if_each(server, destroy_if, NULL);
|
||
|
patty_list_destroy(server->ifaces);
|
||
|
}
|
||
|
|
||
|
static void sock_init(patty_ax25_sock *sock) {
|
||
|
memset(sock, '\0', sizeof(*sock));
|
||
|
|
||
|
sock->status = PATTY_AX25_SOCK_OPEN;
|
||
|
sock->mode = PATTY_AX25_SOCK_DM;
|
||
|
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 uint32_t hash_addrpair(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);
|
||
|
|
||
|
return hash;
|
||
|
}
|
||
|
|
||
|
static int sock_save(patty_ax25_server *server, patty_ax25_sock *sock) {
|
||
|
uint32_t hash = hash_addrpair(&sock->local, &sock->remote);
|
||
|
|
||
|
if (patty_dict_set(server->socks,
|
||
|
NULL + sock->fd,
|
||
|
sizeof(sock->fd),
|
||
|
sock) == NULL) {
|
||
|
goto error_dict_set;
|
||
|
}
|
||
|
|
||
|
if (patty_dict_set_with_hash(server->socks_by_addrpair,
|
||
|
NULL + sock->fd,
|
||
|
sizeof(sock->fd),
|
||
|
sock,
|
||
|
hash) == NULL) {
|
||
|
goto error_dict_set;
|
||
|
}
|
||
|
|
||
|
return sock->fd;
|
||
|
|
||
|
error_dict_set:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_open(patty_ax25_server *server, const char *ifname) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = malloc(sizeof(*sock))) == NULL) {
|
||
|
goto error_malloc_sock;
|
||
|
}
|
||
|
|
||
|
sock_init(sock);
|
||
|
|
||
|
if ((sock->iface = patty_ax25_get_if(server, ifname)) == NULL) {
|
||
|
goto error_get_if;
|
||
|
}
|
||
|
|
||
|
return sock_save(server, sock);
|
||
|
|
||
|
error_get_if:
|
||
|
free(sock);
|
||
|
|
||
|
error_malloc_sock:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static patty_ax25_sock *sock_by_fd(patty_ax25_server *server, int fd) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = patty_dict_get(server->socks, &fd, sizeof(fd))) == NULL) {
|
||
|
errno = EBADF;
|
||
|
|
||
|
goto error_dict_get;
|
||
|
}
|
||
|
|
||
|
return sock;
|
||
|
|
||
|
error_dict_get:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static patty_ax25_sock *sock_get_fd(patty_ax25_server *server,
|
||
|
int fd) {
|
||
|
return patty_dict_get(server->socks,
|
||
|
NULL + fd,
|
||
|
sizeof(fd));
|
||
|
}
|
||
|
|
||
|
static patty_ax25_sock *sock_get_addrpair(patty_ax25_server *server,
|
||
|
patty_ax25_addr *local,
|
||
|
patty_ax25_addr *remote) {
|
||
|
patty_dict_slot *slot;
|
||
|
|
||
|
uint32_t hash = hash_addrpair(local, remote);
|
||
|
|
||
|
if ((slot = patty_dict_slot_find(server->socks_by_addrpair, hash)) == NULL) {
|
||
|
errno = EBADF;
|
||
|
|
||
|
goto error_dict_slot_find;
|
||
|
}
|
||
|
|
||
|
return (patty_ax25_sock *)slot->value;
|
||
|
|
||
|
error_dict_slot_find:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int server_socket_pty(patty_ax25_server *server,
|
||
|
patty_ax25_sock *sock,
|
||
|
int client) {
|
||
|
int fd;
|
||
|
char name[64];
|
||
|
size_t len;
|
||
|
|
||
|
if ((fd = open("/dev/ptmx", O_RDWR)) < 0) {
|
||
|
goto error_open;
|
||
|
}
|
||
|
|
||
|
if (grantpt(fd) < 0) {
|
||
|
goto error_grantpt;
|
||
|
}
|
||
|
|
||
|
if (unlockpt(fd) < 0) {
|
||
|
goto error_unlockpt;
|
||
|
}
|
||
|
|
||
|
if (ptsname_r(fd, name, sizeof(name)) < 0) {
|
||
|
goto error_ptsname_r;
|
||
|
}
|
||
|
|
||
|
len = strnlen(name, sizeof(name) - 1);
|
||
|
|
||
|
if (write(client, name, sizeof(len)) < 0) {
|
||
|
goto error_write;
|
||
|
}
|
||
|
|
||
|
if (write(client, name, len) < 0) {
|
||
|
goto error_write;
|
||
|
}
|
||
|
|
||
|
return sock->fd = fd;
|
||
|
|
||
|
error_write:
|
||
|
error_ptsname_r:
|
||
|
error_unlockpt:
|
||
|
error_grantpt:
|
||
|
close(fd);
|
||
|
|
||
|
error_open:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_socket_unix(patty_ax25_server *server,
|
||
|
patty_ax25_sock *sock,
|
||
|
int client) {
|
||
|
int fd;
|
||
|
char name[256];
|
||
|
size_t len;
|
||
|
|
||
|
struct sockaddr_un addr;
|
||
|
|
||
|
if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
|
||
|
goto error_socket;
|
||
|
}
|
||
|
|
||
|
if (snprintf(name, sizeof(name), "/var/run/patty/%d.sock", fd) < 0) {
|
||
|
goto error_snprintf_name;
|
||
|
}
|
||
|
|
||
|
len = strnlen(name, sizeof(name));
|
||
|
|
||
|
addr.sun_family = AF_UNIX;
|
||
|
|
||
|
memset(&addr, 0, sizeof(addr));
|
||
|
strncpy(addr.sun_path, name, sizeof(addr.sun_path));
|
||
|
|
||
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||
|
goto error_bind;
|
||
|
}
|
||
|
|
||
|
if (write(client, &len, sizeof(len)) < 0) {
|
||
|
goto error_write;
|
||
|
}
|
||
|
|
||
|
if (write(client, name, len) < 0) {
|
||
|
goto error_write;
|
||
|
}
|
||
|
|
||
|
return sock->fd = fd;
|
||
|
|
||
|
error_write:
|
||
|
error_bind:
|
||
|
error_snprintf_name:
|
||
|
close(fd);
|
||
|
|
||
|
error_socket:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_socket(patty_ax25_server *server,
|
||
|
int client,
|
||
|
int sockopts) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = malloc(sizeof(*sock))) == NULL) {
|
||
|
goto error_malloc_sock;
|
||
|
}
|
||
|
|
||
|
sock_init(sock);
|
||
|
|
||
|
if (sockopts & PATTY_AX25_SOCK_PTY) {
|
||
|
if (server_socket_pty(server, sock, client) < 0) {
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return sock->fd;
|
||
|
|
||
|
error:
|
||
|
free(sock);
|
||
|
|
||
|
error_malloc_sock:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_bind(patty_ax25_server *server,
|
||
|
int socket,
|
||
|
patty_ax25_addr *addr) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = sock_get_fd(server, socket)) == NULL) {
|
||
|
goto error_sock_get_fd;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If there is already a local address associated with this socket, then
|
||
|
* that's a problem.
|
||
|
*/
|
||
|
if (sock->local.callsign[0] != '\0') {
|
||
|
errno = EADDRINUSE;
|
||
|
|
||
|
goto error_exists;
|
||
|
}
|
||
|
|
||
|
memcpy(&sock->local, addr, sizeof(*addr));
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error_exists:
|
||
|
error_sock_get_fd:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_listen(patty_ax25_server *server,
|
||
|
int socket) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = sock_get_fd(server, socket)) == NULL) {
|
||
|
errno = EBADF;
|
||
|
|
||
|
goto error_sock_get_fd;
|
||
|
}
|
||
|
|
||
|
if (sock->local.callsign[0] == '\0') {
|
||
|
errno = EINVAL;
|
||
|
|
||
|
goto error_invalid_fd;
|
||
|
}
|
||
|
|
||
|
errno = ENOSYS;
|
||
|
|
||
|
error_invalid_fd:
|
||
|
error_sock_get_fd:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_connect(patty_ax25_server *server,
|
||
|
int socket,
|
||
|
patty_ax25_addr *addr) {
|
||
|
patty_ax25_sock *sock;
|
||
|
|
||
|
if ((sock = sock_get_fd(server, socket)) == NULL) {
|
||
|
goto error_sock_get_fd;
|
||
|
}
|
||
|
|
||
|
if (sock->remote.callsign[0] != '\0') {
|
||
|
errno = EEXIST;
|
||
|
|
||
|
goto error_exists;
|
||
|
}
|
||
|
|
||
|
memcpy(&sock->remote, addr, sizeof(*addr));
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error_exists:
|
||
|
error_sock_get_fd:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int server_accept(patty_ax25_server *server, int socket) {
|
||
|
errno = ENOSYS;
|
||
|
|
||
|
return -1;
|
||
|
}
|