summaryrefslogtreecommitdiff
path: root/src/socket
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-10-10 20:59:07 -0400
committerAndrew Opalach <andrew@akon.city> 2023-10-10 20:59:07 -0400
commita723efcc67b6c14ff0dd1efd1ba88596e959daaa (patch)
tree45384ce34e9601533fdc5bfbbdaf1b63ea7f1310 /src/socket
downloadlibnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.tar.gz
libnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.tar.bz2
libnaunet-a723efcc67b6c14ff0dd1efd1ba88596e959daaa.zip
Add libakiyo
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src/socket')
-rw-r--r--src/socket/socket.h66
-rw-r--r--src/socket/socket_linux.c259
-rw-r--r--src/socket/socket_windows.c130
3 files changed, 455 insertions, 0 deletions
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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#else
+#include "../winwrap.h"
+#endif
+
+#include <al/types.h>
+#include <al/str.h>
+
+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 <unistd.h>
+#include <sys/fcntl.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <signal.h>
+#include <al/log.h>
+
+#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 <al/log.h>
+
+#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);
+}