From a723efcc67b6c14ff0dd1efd1ba88596e959daaa Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 10 Oct 2023 20:59:07 -0400 Subject: Add libakiyo Signed-off-by: Andrew Opalach --- src/socket/socket.h | 66 +++++++++++ src/socket/socket_linux.c | 259 ++++++++++++++++++++++++++++++++++++++++++++ src/socket/socket_windows.c | 130 ++++++++++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 src/socket/socket.h create mode 100644 src/socket/socket_linux.c create mode 100644 src/socket/socket_windows.c (limited to 'src/socket') diff --git a/src/socket/socket.h b/src/socket/socket.h new file mode 100644 index 0000000..ea307e7 --- /dev/null +++ b/src/socket/socket.h @@ -0,0 +1,66 @@ +#pragma once + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#else +#include "../winwrap.h" +#endif + +#include +#include + +enum { + AKI_SOCKET_TCP = 0, + AKI_SOCKET_UDP, + AKI_SOCKET_UNIX +}; + +struct aki_socket { + u8 type; + struct { + bool blocking; + bool no_delay; + } config; +#ifndef _WIN32 + s32 sock; + struct sockaddr_in addr_in; + struct sockaddr_un addr_un; +#else + SOCKET sock; + SOCKADDR_IN addr_in; +#endif +}; + +#ifndef _WIN32 +#define aki_read read +#else +#define aki_read _read +#endif + +#ifndef _WIN32 +#define aki_write write +#else +#define aki_write _write +#endif + +bool aki_socket_init(struct aki_socket *s); + +void aki_socket_set_blocking(struct aki_socket *s, bool blocking); +void aki_socket_set_no_delay(struct aki_socket *s, bool no_delay); + +bool aki_socket_listen(struct aki_socket *s, str *addr, s32 port); +bool aki_socket_accept(struct aki_socket *s, struct aki_socket *c); +bool aki_socket_connect(struct aki_socket *s, str *addr, s32 port); + +s32 aki_socket_get_fd(struct aki_socket *s); + +ssize_t aki_socket_read(struct aki_socket *s, void *buf, size_t size); +ssize_t aki_socket_write(struct aki_socket *s, void *buf, size_t size); +ssize_t aki_socket_sendto(struct aki_socket *s, void *buf, size_t size); + +void aki_socket_shutdown(struct aki_socket *s); +void aki_socket_close(struct aki_socket *s); diff --git a/src/socket/socket_linux.c b/src/socket/socket_linux.c new file mode 100644 index 0000000..b466dea --- /dev/null +++ b/src/socket/socket_linux.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include + +#include "../util/error.h" + +#include "socket.h" + +static void socket_set_blocking(struct aki_socket *s, bool blocking) +{ + s32 flags = fcntl(s->sock, F_GETFL, 0); + al_assert(flags != -1); + flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + s32 ret = fcntl(s->sock, F_SETFL, flags); + al_assert(ret == 0); +} + +static void socket_set_no_delay(struct aki_socket *s, s32 no_delay) +{ + if (s->type == AKI_SOCKET_TCP) { + setsockopt(s->sock, IPPROTO_TCP, TCP_NODELAY, &no_delay, sizeof(no_delay)); + } +} + +static void socket_set_send_buf(struct aki_socket *s, u32 sndbuf) +{ + u32 send_queue_size = sndbuf; + setsockopt(s->sock, SOL_SOCKET, SO_SNDBUF, &send_queue_size, sizeof(send_queue_size)); + + socklen_t optlen = sizeof(send_queue_size); + getsockopt(s->sock, SOL_SOCKET, SO_SNDBUF, &send_queue_size, &optlen); + + if (send_queue_size != sndbuf * 2) { + al_log_warn("socket", "Failed to set SO_SNDBUF."); + } +} + +static void socket_set_recv_buf(struct aki_socket *s, u32 rcvbuf) +{ + u32 receive_queue_size = rcvbuf; + setsockopt(s->sock, SOL_SOCKET, SO_RCVBUF, &receive_queue_size, sizeof(receive_queue_size)); + + socklen_t optlen = sizeof(receive_queue_size); + getsockopt(s->sock, SOL_SOCKET, SO_RCVBUF, &receive_queue_size, &optlen); + + if (receive_queue_size != rcvbuf * 2) { + al_log_warn("socket", "Failed to set SO_RCVBUF."); + } +} + +bool aki_socket_init(struct aki_socket *s) +{ + switch (s->type) { + case AKI_SOCKET_TCP: + s->sock = socket(AF_INET, SOCK_STREAM, 0); + break; + case AKI_SOCKET_UDP: + s->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + break; + case AKI_SOCKET_UNIX: + s->sock = socket(AF_UNIX, SOCK_STREAM, 0); + break; + } + + if (s->sock < 0) { + al_log_error("socket", "Failed to create socket."); + return false; + } + + aki_socket_set_blocking(s, s->config.blocking); + aki_socket_set_no_delay(s, s->config.no_delay); + + return true; +} + +void aki_socket_set_blocking(struct aki_socket *s, bool blocking) +{ + s->config.blocking = blocking; + socket_set_blocking(s, blocking); +} + +void aki_socket_set_no_delay(struct aki_socket *s, bool no_delay) +{ + s->config.no_delay = no_delay; + socket_set_no_delay(s, no_delay); +} + +bool aki_socket_accept(struct aki_socket *s, struct aki_socket *c) +{ + c->type = s->type; + + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + if((c->sock = accept(s->sock, (struct sockaddr *)&addr, &len)) == -1) { + return false; + } + + aki_socket_set_blocking(c, c->config.blocking); + aki_socket_set_no_delay(c, c->config.no_delay); + + switch (s->type) { + case AKI_SOCKET_TCP: { + s32 port = 0; + char ipstr[INET6_ADDRSTRLEN] = {0}; + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + port = ntohs(s->sin_port); + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr)); + } else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + port = ntohs(s->sin6_port); + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr)); + } + al_log_info("socket", "Connection from %s:%d.", ipstr, port); + break; + } + case AKI_SOCKET_UNIX: { + al_log_info("socket", "Connection from a UNIX socket."); + break; + } + case AKI_SOCKET_UDP: { + al_assert(false); + } + } + + return true; +} + +bool aki_socket_listen(struct aki_socket *s, str *addr, s32 port) +{ + struct sockaddr *saddr = NULL; + socklen_t addrlen = 0; + + switch (s->type) { + case AKI_SOCKET_TCP: { + s->addr_in.sin_family = AF_INET; + s->addr_in.sin_addr.s_addr = htonl(INADDR_ANY); + s->addr_in.sin_port = htons(port); + saddr = (struct sockaddr *)&s->addr_in; + addrlen = sizeof(s->addr_in); + break; + } + case AKI_SOCKET_UNIX: { + char *c_str = al_str_to_c_str(addr); + s->addr_un.sun_family = AF_UNIX; + size_t len = al_strlen(c_str); + al_memcpy(s->addr_un.sun_path, c_str, len); + s->addr_un.sun_path[len] = '\0'; + al_free(c_str); + saddr = (struct sockaddr *)&s->addr_un; + addrlen = sizeof(s->addr_un); + break; + } + case AKI_SOCKET_UDP: { + al_assert(false); + } + } + + if (bind(s->sock, saddr, addrlen) < 0) { + al_log_error("socket", "Failed to bind (%.*s:%d).", AL_STR_PRINTF(addr), port); + return false; + } + + if (listen(s->sock, SOMAXCONN) < 0) { + al_log_error("socket", "Failed to listen (%.*s:%d).", AL_STR_PRINTF(addr), port); + return false; + } + + al_log_info("socket", "Listening on %.*s:%d.", AL_STR_PRINTF(addr), port); + + signal(SIGPIPE, SIG_IGN); + + return true; +} + +bool aki_socket_connect(struct aki_socket *s, str *addr, s32 port) +{ + switch (s->type) { + case AKI_SOCKET_TCP: { + char *c_str = al_str_to_c_str(addr); + struct hostent *hptr = gethostbyname(c_str); + al_free(c_str); + + if (!hptr) { + al_log_error("socket", "Failed to parse address."); + return false; + } + + if (hptr->h_addrtype != AF_INET) { + al_log_error("socket", "Bad address family."); + return false; + } + + s->addr_in.sin_family = AF_INET; + s->addr_in.sin_addr.s_addr = ((struct in_addr *)hptr->h_addr_list[0])->s_addr; + s->addr_in.sin_port = htons(port); + + s32 ret = connect(s->sock, (struct sockaddr *)&s->addr_in, sizeof(s->addr_in)); + + if (ret != 0 && errno != 115) { + al_log_error("socket", "Failed to connect: %s (%d).", aki_strerror(errno), errno); + return false; + } + + break; + } + case AKI_SOCKET_UNIX: { + s->addr_un.sun_family = AF_UNIX; + al_memcpy(s->addr_un.sun_path, addr->data, addr->len); + s->addr_un.sun_path[addr->len] = '\0'; + if (connect(s->sock, (struct sockaddr *)&s->addr_un, sizeof(s->addr_un)) < 0) { + al_log_error("socket", "Failed to connect: %s (%d).", aki_strerror(errno), errno); + return false; + } + break; + } + case AKI_SOCKET_UDP: { + al_assert(false); + } + } + + signal(SIGPIPE, SIG_IGN); + + return true; +} + +ssize_t aki_socket_read(struct aki_socket *s, void *buf, size_t size) +{ + return recv(s->sock, buf, size, 0); +} + +ssize_t aki_socket_write(struct aki_socket *s, void *buf, size_t size) +{ + return send(s->sock, buf, size, MSG_NOSIGNAL); +} + +ssize_t aki_socket_sendto(struct aki_socket *s, void *buf, size_t size) +{ + return sendto(s->sock, buf, size, 0, (const struct sockaddr *)&s->addr_un, sizeof(s->addr_un)); +} + +s32 aki_socket_get_fd(struct aki_socket *s) +{ + return s->sock; +} + +void aki_socket_shutdown(struct aki_socket *s) +{ + shutdown(s->sock, SHUT_RDWR); +} + +void aki_socket_close(struct aki_socket *s) +{ + close(s->sock); +} + diff --git a/src/socket/socket_windows.c b/src/socket/socket_windows.c new file mode 100644 index 0000000..6e72aea --- /dev/null +++ b/src/socket/socket_windows.c @@ -0,0 +1,130 @@ +#include + +#include "socket.h" + +static void socket_set_blocking(struct aki_socket *s, bool blocking) +{ + u_long mode = (blocking) ? 0 : 1; + ioctlsocket(s->sock, FIONBIO, &mode); +} + +static void socket_set_no_delay(struct aki_socket *s, s32 no_delay) +{ + (void)s; + (void)no_delay; +} + +bool aki_socket_init(struct aki_socket *s) +{ + switch (s->type) { + case AKI_SOCKET_TCP: + s->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + break; + case AKI_SOCKET_UDP: + al_assert(false); + return false; + case AKI_SOCKET_UNIX: + al_assert(false); + return false; + } + + if (s->sock == INVALID_SOCKET) { + al_log_error("socket", "Failed to create socket."); + return false; + } + + aki_socket_set_blocking(s, s->config.blocking); + aki_socket_set_no_delay(s, s->config.no_delay); + + return true; +} + +void aki_socket_set_blocking(struct aki_socket *s, bool blocking) +{ + s->config.blocking = blocking; + socket_set_blocking(s, blocking); +} + +void aki_socket_set_no_delay(struct aki_socket *s, bool no_delay) +{ + s->config.no_delay = no_delay; + socket_set_no_delay(s, no_delay); +} + +bool aki_socket_listen(struct aki_socket *s, str *addr, s32 port) +{ + s->addr_in.sin_family = AF_INET; + s->addr_in.sin_addr.s_addr = INADDR_ANY; + s->addr_in.sin_port = htons(port); + al_memset(&(s->addr_in.sin_zero), '\0', 8); + if (bind(s->sock, (SOCKADDR *)&s->addr_in, sizeof(s->addr_in)) == SOCKET_ERROR) { + al_log_error("socket", "Failed to bind (%.*s:%d).", AL_STR_PRINTF(addr), port); + return false; + } + if (listen(s->sock, SOMAXCONN) == SOCKET_ERROR) { + al_log_error("socket", "Failed to listen (%.*s:%d).", AL_STR_PRINTF(addr), port); + return false; + } + return true; +} + +bool aki_socket_accept(struct aki_socket *s, struct aki_socket *c) +{ + c->type = s->type; + + if ((c->sock = accept(s->sock, NULL, NULL)) == INVALID_SOCKET) { + return false; + } + + aki_socket_set_blocking(c, c->config.blocking); + aki_socket_set_no_delay(c, c->config.no_delay); + + return true; +} + +bool aki_socket_connect(struct aki_socket *s, str *addr, s32 port) +{ + s->addr_in.sin_family = AF_INET; + char *c_str = al_str_to_c_str(addr); + InetPton(AF_INET, c_str, &s->addr_in.sin_addr.s_addr); + al_free(c_str); + s->addr_in.sin_port = htons(port); + al_memset(&(s->addr_in.sin_zero), '\0', 8); + if (connect(s->sock, (SOCKADDR *)&s->addr_in, sizeof(s->addr_in)) == SOCKET_ERROR) { + return false; + } + return true; +} + +s32 aki_socket_get_fd(struct aki_socket *s) +{ + return _open_osfhandle(s->sock, 0); +} + +ssize_t aki_socket_read(struct aki_socket *s, void *buf, size_t size) +{ + return recv(s->sock, buf, (s32)size, 0); +} + +ssize_t aki_socket_write(struct aki_socket *s, void *buf, size_t size) +{ + return send(s->sock, buf, (s32)size, 0); +} + +ssize_t aki_socket_sendto(struct aki_socket *s, void *buf, size_t size) +{ + (void)s; + (void)buf; + (void)size; + return 0; +} + +void aki_socket_shutdown(struct aki_socket *s) +{ + if (shutdown(s->sock, SD_SEND) == SOCKET_ERROR) {} +} + +void aki_socket_close(struct aki_socket *s) +{ + closesocket(s->sock); +} -- cgit v1.2.3-101-g0448