diff --git a/include/patty/ax25/call.h b/include/patty/ax25/call.h index cb8618f..b9da27e 100644 --- a/include/patty/ax25/call.h +++ b/include/patty/ax25/call.h @@ -2,7 +2,7 @@ #define _PATTY_AX25_CALL_H enum patty_ax25_call { - PATTY_AX25_CALL_UNKNOWN, + PATTY_AX25_CALL_NONE, PATTY_AX25_CALL_SOCKET, PATTY_AX25_CALL_BIND, PATTY_AX25_CALL_LISTEN, @@ -10,7 +10,8 @@ enum patty_ax25_call { PATTY_AX25_CALL_CONNECT, PATTY_AX25_CALL_CLOSE, PATTY_AX25_CALL_SENDTO, - PATTY_AX25_CALL_RECVFROM + PATTY_AX25_CALL_RECVFROM, + PATTY_AX25_CALL_COUNT }; /* diff --git a/include/patty/ax25/if.h b/include/patty/ax25/if.h index f0d31f1..15194e3 100644 --- a/include/patty/ax25/if.h +++ b/include/patty/ax25/if.h @@ -11,6 +11,8 @@ #define PATTY_AX25_IF_OPT_TYPE(n) \ ((n) & PATTY_AX25_IF_OPT_TYPE_MASK) +#define PATTY_AX25_IF_BUFSZ 4096 + enum patty_ax25_if_type { PATTY_AX25_IF_UNKNOWN, PATTY_AX25_IF_KISS_TNC, @@ -31,6 +33,9 @@ typedef struct _patty_ax25_if { char name[8]; + void *rx_buf; + size_t rx_bufsz; + patty_kiss_tnc *tnc; patty_list *addrs; } patty_ax25_if; @@ -48,4 +53,11 @@ int patty_ax25_if_add_addr(patty_ax25_if *iface, int patty_ax25_if_delete_addr(patty_ax25_if *iface, const char *callsign, uint8_t ssid); +ssize_t patty_ax25_if_recv(patty_ax25_if *iface, + void **buf); + +ssize_t patty_ax25_if_send(patty_ax25_if *iface, + const void *buf, + size_t len); + #endif /* _PATTY_AX25_IF_H */ diff --git a/include/patty/ax25/server.h b/include/patty/ax25/server.h index 49324fc..0e35839 100644 --- a/include/patty/ax25/server.h +++ b/include/patty/ax25/server.h @@ -1,13 +1,14 @@ #ifndef _PATTY_AX25_SERVER_H #define _PATTY_AX25_SERVER_H +#define PATTY_AX25_SERVER_PATH "/var/run/patty/patty.sock" +#define PATTY_AX25_SERVER_CLIENT_PATH_FORMAT "/var/run/patty/%d.sock" + typedef struct _patty_ax25_server patty_ax25_server; int patty_ax25_server_init(patty_ax25_server *server); -void patty_ax25_server_stop(patty_ax25_server *server); - -int patty_ax25_server_run(patty_ax25_server *server); +void patty_ax25_server_destroy(patty_ax25_server *server); int patty_ax25_server_add_if(patty_ax25_server *server, patty_ax25_if *iface); @@ -22,4 +23,6 @@ int patty_ax25_server_each_if(patty_ax25_server *server, int (*callback)(patty_ax25_if *, void *), void *ctx); +int patty_ax25_server_run(patty_ax25_server *server); + #endif /* _PATTY_AX25_SERVER_H */ diff --git a/include/patty/dict.h b/include/patty/dict.h index b040dda..f14d04b 100644 --- a/include/patty/dict.h +++ b/include/patty/dict.h @@ -29,13 +29,14 @@ patty_dict *patty_dict_new(); patty_dict_slot *patty_dict_slot_find(patty_dict *dict, uint32_t hash); -typedef int (*patty_dict_callback)( - void * key, - size_t keysz, - void * value, - void * ctx); +typedef int (*patty_dict_callback)(void *key, + size_t keysz, + void *value, + void *ctx); -int patty_dict_each(patty_dict *dict, patty_dict_callback callback, void *ctx); +int patty_dict_each(patty_dict *dict, + patty_dict_callback callback, + void *ctx); void *patty_dict_get(patty_dict *dict, void *key, size_t keysz); diff --git a/include/patty/list.h b/include/patty/list.h index 05299d6..b2f582c 100644 --- a/include/patty/list.h +++ b/include/patty/list.h @@ -59,9 +59,8 @@ void patty_list_reset(patty_list_iterator *iterator); void patty_list_finish(patty_list_iterator *iterator); -void patty_list_each( - patty_list * list, - patty_list_callback callback, - void * ctx); +void patty_list_each(patty_list *list, + patty_list_callback callback, + void *ctx); #endif /* _PATTY_LIST_H */ diff --git a/src/call.c b/src/call.c index e1b279f..88a7fd2 100644 --- a/src/call.c +++ b/src/call.c @@ -6,7 +6,7 @@ int patty_ax25_call_socket(int server, int type) { - int call = PATTY_AX25_CALL_SOCKET; + enum patty_ax25_call call = PATTY_AX25_CALL_SOCKET; patty_ax25_call_socket_request request = { type @@ -37,7 +37,7 @@ error_io: int patty_ax25_call_bind(int server, int socket, patty_ax25_addr *peer) { - int call = PATTY_AX25_CALL_BIND; + enum patty_ax25_call call = PATTY_AX25_CALL_BIND; patty_ax25_call_bind_request request = { socket @@ -69,7 +69,7 @@ error_io: int patty_ax25_call_listen(int server, int socket) { - int call = PATTY_AX25_CALL_LISTEN; + enum patty_ax25_call call = PATTY_AX25_CALL_LISTEN; patty_ax25_call_listen_request request = { socket @@ -101,7 +101,7 @@ int patty_ax25_call_accept(int server, int socket, patty_ax25_addr *peer, char *path) { - int call = PATTY_AX25_CALL_ACCEPT; + enum patty_ax25_call call = PATTY_AX25_CALL_ACCEPT; patty_ax25_call_accept_request request = { socket @@ -138,7 +138,7 @@ int patty_ax25_call_connect(int server, int socket, patty_ax25_addr *peer, char *path) { - int call = PATTY_AX25_CALL_CONNECT; + enum patty_ax25_call call = PATTY_AX25_CALL_CONNECT; patty_ax25_call_connect_request request = { socket @@ -174,7 +174,7 @@ error_io: int patty_ax25_call_close(int server, int socket) { - int call = PATTY_AX25_CALL_CLOSE; + enum patty_ax25_call call = PATTY_AX25_CALL_CLOSE; patty_ax25_call_close_request request = { socket diff --git a/src/if.c b/src/if.c index 87deb09..796c1a3 100644 --- a/src/if.c +++ b/src/if.c @@ -5,17 +5,9 @@ #include -static patty_ax25_if *create_tnc(const char *device) { - patty_ax25_if *iface; - - static char * prefix = "kiss"; - static int number = 0; - - if ((iface = malloc(sizeof(*iface))) == NULL) { - goto error_malloc_iface; - } - - memset(iface, '\0', sizeof(*iface)); +static int init_tnc(patty_ax25_if *iface, const char *device) { + static char *prefix = "kiss"; + static int number = 0; if ((iface->tnc = patty_kiss_tnc_open(device)) == NULL) { goto error_kiss_tnc_open; @@ -25,58 +17,79 @@ static patty_ax25_if *create_tnc(const char *device) { snprintf(iface->name, sizeof(iface->name), "%s%d", prefix, number++); - return iface; + return 0; error_kiss_tnc_open: - free(iface); - -error_malloc_iface: - return NULL; + return -1; } -static void destroy_tnc(patty_ax25_if *iface) { +static void close_tnc(patty_ax25_if *iface) { patty_kiss_tnc_close(iface->tnc); } patty_ax25_if *patty_ax25_if_create(int opts, void *info) { patty_ax25_if *iface; - switch (PATTY_AX25_IF_OPT_TYPE(opts)) { - case PATTY_AX25_IF_KISS_TNC: { - iface = create_tnc((const char *)info); - - break; - } - - default: { - errno = EINVAL; - - goto error; - } + if ((iface = malloc(sizeof(*iface))) == NULL) { + goto error_malloc_iface; } - if (iface == NULL) { - goto error; + memset(iface, '\0', sizeof(*iface)); + + if ((iface->rx_buf = malloc(PATTY_AX25_IF_BUFSZ)) == NULL) { + goto error_malloc_rx_buf; + } else { + iface->rx_bufsz = PATTY_AX25_IF_BUFSZ; } if ((iface->addrs = patty_list_new()) == NULL) { - goto error; + goto error_list_new; + } + + switch (PATTY_AX25_IF_OPT_TYPE(opts)) { + case PATTY_AX25_IF_KISS_TNC: + if (init_tnc(iface, (const char *)info) < 0) { + goto error_init; + } + + break; + + default: + errno = EINVAL; + + goto error_invalid_if_type; } return iface; -error: +error_invalid_if_type: +error_init: + patty_list_destroy(iface->addrs); + +error_list_new: + free(iface->rx_buf); + +error_malloc_rx_buf: + free(iface); + +error_malloc_iface: return NULL; } void patty_ax25_if_destroy(patty_ax25_if *iface) { switch (iface->type) { - case PATTY_AX25_IF_KISS_TNC: { - destroy_tnc(iface); break; - } + case PATTY_AX25_IF_KISS_TNC: + close_tnc(iface); + break; - default: break; + default: + break; } + + patty_list_destroy(iface->addrs); + + free(iface->rx_buf); + free(iface); } int patty_ax25_if_each_addr(patty_ax25_if *iface, @@ -221,3 +234,37 @@ error_ntop: return -1; } +ssize_t patty_ax25_if_recv(patty_ax25_if *iface, + void **buf) { + ssize_t readlen; + int port; + + if ((readlen = patty_kiss_tnc_recv(iface->tnc, + iface->rx_buf, + iface->rx_bufsz, + &port)) < 0) { + goto error_kiss_tnc_recv; + } + + *buf = iface->rx_buf; + + return readlen; + +error_kiss_tnc_recv: + return -1; +} + +ssize_t patty_ax25_if_send(patty_ax25_if *iface, + const void *buf, + size_t len) { + ssize_t wrlen; + + if ((wrlen = patty_kiss_tnc_send(iface->tnc, buf, len, 0)) < 0) { + goto error_kiss_tnc_send; + } + + return wrlen; + +error_kiss_tnc_send: + return -1; +} diff --git a/src/server.c b/src/server.c index 18f5138..7752c3f 100644 --- a/src/server.c +++ b/src/server.c @@ -10,7 +10,20 @@ #include +typedef int (*patty_ax25_server_call)(patty_ax25_server *, int); + struct _patty_ax25_server { + int fd, /* fd of UNIX domain socket */ + fd_max; + + struct timeval timeout; + + fd_set fds_watch, /* fds to monitor with select() */ + fds_socks, /* fds belonging to socks */ + fds_clients, /* fds belonging to clients */ + fds_r, /* fds select()ed for reading */ + fds_w; /* fds select()ed for writing */ + patty_list *ifaces; patty_dict *socks_by_fd, @@ -18,13 +31,8 @@ struct _patty_ax25_server { *socks_pending_connect, *socks_established; - patty_dict *clients_by_sock; - - int fd_max; - - fd_set fds_watch, /* fds to monitor with select() */ - fds_socks, /* fds belonging to socks */ - fds_clients; /* fds belonging to clients */ + patty_dict *clients, + *clients_by_sock; }; int patty_ax25_server_init(patty_ax25_server *server) { @@ -50,6 +58,10 @@ int patty_ax25_server_init(patty_ax25_server *server) { goto error_dict_new_socks_established; } + 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; } @@ -57,6 +69,9 @@ int patty_ax25_server_init(patty_ax25_server *server) { return 0; 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: @@ -81,17 +96,6 @@ static int destroy_if(patty_ax25_if *iface, void *ctx) { return 0; } -void patty_ax25_server_stop(patty_ax25_server *server) { - patty_dict_destroy(server->clients_by_sock); - 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_list_destroy(server->ifaces); -} - static void sock_init(patty_ax25_sock *sock, enum patty_ax25_sock_type type) { memset(sock, '\0', sizeof(*sock)); @@ -576,7 +580,10 @@ static int server_accept_unix(patty_ax25_server *server, goto error_socket; } - if (snprintf(response->path, PATTY_AX25_SOCK_PATH_SIZE, "/var/run/patty/%d.sock", sock->fd) < 0) { + if (snprintf(response->path, + PATTY_AX25_SOCK_PATH_SIZE, + PATTY_AX25_SERVER_CLIENT_PATH_FORMAT, + sock->fd) < 0) { goto error_snprintf; } @@ -787,3 +794,295 @@ error_sock_by_fd: error_io: return -1; } + +static patty_ax25_server_call server_calls[PATTY_AX25_CALL_COUNT] = { + NULL, + server_socket, + 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; + + if (server->fd) { + errno = EBUSY; + + goto error_listening; + } + + 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; + } + + 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; + socklen_t 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, + NULL + fd, + sizeof(fd), + NULL + fd) == NULL) { + goto error_dict_set; + } + + FD_SET(fd, &server->fds_clients); + +done: + return 0; + +error_dict_set: + close(fd); + +error_accept: + return -1; +} + +static int handle_client(void *key, + size_t keysz, + void *value, + void *ctx) { + patty_ax25_server *server = ctx; + int client = (int)(key - NULL); + + ssize_t readlen; + enum patty_ax25_call call; + + if (!FD_ISSET(client, &server->fds_r)) { + goto done; + } + + if ((readlen = read(server->fd, &call, sizeof(call))) < 0) { + goto error_io; + } else if (readlen == 0) { + FD_CLR(client, &server->fds_watch); + FD_CLR(client, &server->fds_clients); + + if (patty_dict_delete(server->clients, + key, + keysz) < 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; + } + + return server_calls[call](server, client); + +done: + return 0; + +error_dict_delete: +error_io: + return -1; +} + +static int handle_clients(patty_ax25_server *server) { + return patty_dict_each(server->clients, handle_client, server); +} + +static int handle_packet_rx(patty_ax25_server *server, + patty_ax25_if *iface, + void *buf, + size_t len) { + patty_ax25_frame frame; + + if (patty_ax25_frame_decode(&frame, buf, len) < 0) { + goto error_io; + } + + /* + * TODO: Handle inbound packet + */ + + return 0; + +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) { + FD_CLR(fd, &server->fds_watch); + + goto done; + } + + if (handle_packet_rx(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_iterator *iter; + patty_ax25_if *iface; + + if ((iter = patty_list_start(server->ifaces)) == NULL) { + goto error_list_start; + } + + while ((iface = patty_list_next(iter)) != NULL) { + if (handle_iface(server, iface) < 0) { + goto error_io; + } + } + + patty_list_finish(iter); + + return 0; + +error_io: + patty_list_finish(iter); + +error_list_start: + return -1; +} + +static int handle_sock(void *key, + size_t keysz, + void *value, + void *ctx) { + patty_ax25_server *server = ctx; + patty_ax25_sock *sock = value; + int fd = (int)(key - NULL); + + ssize_t readlen; + + if (!FD_ISSET(fd, &server->fds_r)) { + goto done; + } + + /* + * TODO: Finish this + */ + +done: + return 0; +} + +static int handle_socks(patty_ax25_server *server) { + return patty_dict_each(server->socks_by_fd, + handle_sock, + server); +} + +int patty_ax25_server_run(patty_ax25_server *server) { + if (listen_unix(server, PATTY_AX25_SERVER_PATH) < 0) { + goto error_listen_unix; + } + + while (1) { + int nready; + + FD_COPY(&server->fds_watch, &server->fds_r); + FD_COPY(&server->fds_watch, &server->fds_w); + + if ((nready = select( server->fd_max, + &server->fds_r, + &server->fds_w, + NULL, + &server->timeout)) < 0) { + goto error_io; + } + + if (accept_client(server) < 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; + } + } + + close(server->fd); + + return 0; + +error_io: + close(server->fd); + +error_listen_unix: + return -1; +} + +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_list_destroy(server->ifaces); +}