Mercurial
view seobeo/s_network.c @ 184:8c74204fd362
[MD to HTML] Updated so it can be used through readme to html
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 23 Jan 2026 22:20:35 -0800 |
| parents | 043018c0f2f8 |
| children | 240337164a80 |
line wrap: on
line source
#include "seobeo/seobeo.h" Seobeo_Handle *Seobeo_Stream_Handle_Server_Create(const char *host, const char* port) { Seobeo_Handle *p_handle; struct addrinfo hints, *server_infos, *free_server_info; int32 socket_fd, yes = 1; // Need this for setsockopt memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(host, port, &hints, &server_infos) != 0) { perror("getaddrinfo"); return NULL; } for ( free_server_info = server_infos; free_server_info != NULL; free_server_info = free_server_info->ai_next ) { if((socket_fd = socket(free_server_info->ai_family, free_server_info->ai_socktype, free_server_info->ai_protocol)) == -1) { perror("socket"); continue; } if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { perror("setsockopt SO_REUSEADDR"); continue; } #ifdef SO_REUSEPORT // SO_REUSEPORT allows multiple threads/processes to bind to the same port // The kernel will distribute incoming connections among them if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) == -1) { perror("setsockopt SO_REUSEPORT"); continue; } #endif if (bind(socket_fd, free_server_info->ai_addr, free_server_info->ai_addrlen) == -1) { perror("v_network: Couldn't make socket non-blocking\n"); continue; } break; } if (listen(socket_fd, 16) != 0) { Seobeo_Log(SEOBEO_DEBUG, "Closing socket: %d\n", socket_fd); perror("listen"); close(socket_fd); return NULL; } int flags = fcntl(socket_fd, F_GETFL, 0); if(fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) != 0) { perror("fcntl"); return NULL; } freeaddrinfo(server_infos); p_handle = malloc(sizeof(*p_handle)); p_handle->socket = socket_fd; p_handle->type = SEOBEO_STREAM_TYPE_SERVER; p_handle->connected = FALSE; p_handle->host = host != NULL ? strdup(host) : "localhost"; p_handle->port = strdup(port); p_handle->ssl_ctx = NULL; p_handle->ssl = NULL; p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY; p_handle->read_buffer_len = 0; p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY; p_handle->write_buffer_len = 0; p_handle->destroyed = FALSE; return p_handle; } Seobeo_Handle *Seobeo_Stream_Handle_Client_Create(const char *host, const char* port, boolean use_tls) { Seobeo_Handle *p_handle; p_handle = malloc(sizeof(*p_handle)); struct addrinfo hints, *server_infos; int32 socket_fd; // Need this for setsockopt memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host, port, &hints, &server_infos) != 0) { perror("getaddrinfo"); return NULL; } if((socket_fd = socket(server_infos->ai_family, server_infos->ai_socktype, server_infos->ai_protocol)) == -1) { perror("socket"); return NULL; } if (connect(socket_fd, server_infos->ai_addr, server_infos->ai_addrlen) != 0) { perror("connect"); return NULL; } freeaddrinfo(server_infos); // Set non-blocking int flags = fcntl(socket_fd, F_GETFL, 0); if (flags == -1 || fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) != 0) { perror("fcntl"); close(socket_fd); free(p_handle); return NULL; } p_handle->socket = socket_fd; p_handle->type = SEOBEO_STREAM_TYPE_CLIENT; p_handle->ssl_ctx = NULL; p_handle->ssl = NULL; if (use_tls) { if (Seobeo_SSL_Setup_Client(p_handle, host, socket_fd) != 0) { free(p_handle); return NULL; } } p_handle->connected = TRUE; p_handle->host = host != NULL ? strdup(host) : "localhost"; p_handle->port = strdup(port); p_handle->read_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); p_handle->read_buffer_capacity = INITIAL_BUFFER_CAPACITY; p_handle->read_buffer_len = 0; p_handle->write_buffer = malloc(sizeof(*p_handle->read_buffer) * INITIAL_BUFFER_CAPACITY); p_handle->write_buffer_capacity = INITIAL_BUFFER_CAPACITY; p_handle->write_buffer_len = 0; p_handle->destroyed = FALSE; return p_handle; } Seobeo_Handle *Seobeo_Stream_Handle_Server_Accept(Seobeo_Handle *p_server_handle) { struct sockaddr_storage addr; socklen_t addrlen = sizeof addr; char client_inet_addr[INET6_ADDRSTRLEN]; int client_fd = accept(p_server_handle->socket, (struct sockaddr*)&addr, &addrlen); inet_ntop( addr.ss_family, Seobeo_Get_IP4_Or_IP6((struct sockaddr *)&addr), client_inet_addr, sizeof client_inet_addr); if (client_fd == -1) return NULL; // Set non blocking... int flags = fcntl(client_fd, F_GETFL, 0); if (flags == -1) return NULL; fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); Seobeo_Handle *p_client_handle = malloc(sizeof *p_client_handle); p_client_handle->socket = client_fd; p_client_handle->type = SEOBEO_STREAM_TYPE_CLIENT; p_client_handle->connected = TRUE; // TODO: support SSL in the future. p_client_handle->ssl_ctx = NULL; p_client_handle->ssl = NULL; p_client_handle->host = strdup(client_inet_addr); p_client_handle->port = NULL; p_client_handle->read_buffer_capacity = p_server_handle->read_buffer_capacity; p_client_handle->read_buffer_len = 0; p_client_handle->read_buffer = malloc(p_client_handle->read_buffer_capacity); p_client_handle->write_buffer_capacity = p_server_handle->write_buffer_capacity; p_client_handle->write_buffer_len = 0; p_client_handle->write_buffer = malloc(p_client_handle->write_buffer_capacity); p_client_handle->read_buffer_used = 0; p_client_handle->file = NULL; p_client_handle->text_copy = NULL; p_client_handle->file_name = NULL; p_client_handle->destroyed = FALSE; return p_client_handle; } void Seobeo_Handle_Destroy(Seobeo_Handle *p_handle) { if (!p_handle) return; boolean expected = FALSE; // Need to check if (!atomic_compare_exchange_strong(&p_handle->destroyed, &expected, TRUE)) { return; } if (p_handle->host) Dowa_Free(p_handle->host); if (p_handle->port) Dowa_Free(p_handle->port); Seobeo_SSL_Cleanup(p_handle); if (p_handle->socket) { Seobeo_Log(SEOBEO_DEBUG, "Closing handle socket: %d\n", p_handle->socket); close(p_handle->socket); } if (p_handle->read_buffer) Dowa_Free(p_handle->read_buffer); if (p_handle->write_buffer) Dowa_Free(p_handle->write_buffer); Dowa_Free(p_handle); } int32 Seobeo_Handle_Flush(Seobeo_Handle *p_handle) { uint32 total = p_handle->write_buffer_len; uint32 sent = 0; Seobeo_Log(SEOBEO_DEBUG, "Write buffer total: %d\n", p_handle->write_buffer_len); while (sent < total) { if (p_handle->ssl) { int n = Seobeo_SSL_Write(p_handle, p_handle->write_buffer + sent, total - sent); if (n < 0) return -1; if (n == 0) return 0; // would block sent += (uint32)n; }else { Seobeo_Log(SEOBEO_DEBUG, "Flushing socket: %d\n", p_handle->socket); ssize_t n = write( p_handle->socket, p_handle->write_buffer + sent, total - sent ); if (n < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) return 1; return -1; } sent += (uint32)n; } } p_handle->write_buffer_len = 0; return 0; } int32 Seobeo_Handle_Queue(Seobeo_Handle *p_handle, const uint8 *data, uint32 data_size) { if (p_handle->write_buffer_len + data_size > p_handle->write_buffer_capacity) { int32 rc = Seobeo_Handle_Flush(p_handle); if (rc < 0) return -1; if (rc > 0) return 1; } if (data_size > p_handle->write_buffer_capacity) { uint32 offset = 0; while (offset < data_size) { ssize_t n = write(p_handle->socket, data + offset, data_size - offset); if (n==0) { // DEBUG Seobeo_Log(SEOBEO_DEBUG, "Write offset: %d\n", offset); break; } if (n < 0) { if (errno == EINTR || errno == EAGAIN) { // DEBUG // printf("Partial write, returning early (offset=%d)\n", offset); continue; } if (errno == EAGAIN) return 1; return -1; } offset += (uint32)n; // DEBUG Seobeo_Log(SEOBEO_DEBUG, "Write completed - offset: %d, data_size: %d\n", offset, data_size); } // DEBUG Seobeo_Log(SEOBEO_DEBUG, "Total bytes written: %d\n", offset); return 0; } if (!p_handle) { Seobeo_Log(SEOBEO_ERROR, "p_handle is NULL before memcpy\n"); return -1; } if (!p_handle->write_buffer) { Seobeo_Log(SEOBEO_ERROR, "p_handle->write_buffer is NULL (len=%u, size=%u)\n", p_handle->write_buffer_len, data_size); return -1; } Seobeo_Log(SEOBEO_DEBUG, "memcpy -> dest=%p (write_buffer=%p + offset=%u), src=%p, size=%u\n", p_handle->write_buffer + p_handle->write_buffer_len, p_handle->write_buffer, p_handle->write_buffer_len, data, data_size); memcpy(p_handle->write_buffer + p_handle->write_buffer_len, data, data_size); p_handle->write_buffer_len += data_size; return 0; } int32 Seobeo_Handle_Read(Seobeo_Handle *p_handle) { int32 read_size; if (!p_handle) return -1; // How many bytes we can still read into the buffer uint32 free_space = p_handle->read_buffer_capacity - p_handle->read_buffer_len; if (free_space == 0) return -1; if (p_handle->ssl) { read_size = Seobeo_SSL_Read(p_handle, p_handle->read_buffer + p_handle->read_buffer_len, free_space); if (read_size < 0) return read_size; // -1 for error, -2 for closed if (read_size == 0) return 0; // would block } else { read_size = (int32)read(p_handle->socket, p_handle->read_buffer + p_handle->read_buffer_len, free_space); if (read_size == 0) return -2; if (read_size < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; return -1; } } p_handle->read_buffer_len += (uint32)read_size; return read_size; } void Seobeo_Handle_Consume(Seobeo_Handle *p_handle, uint32 consumed) { if (consumed >= p_handle->read_buffer_len) { p_handle->read_buffer_len = 0; return; } // Slide remaining bytes to the front memmove( p_handle->read_buffer, p_handle->read_buffer + consumed, p_handle->read_buffer_len - consumed ); p_handle->read_buffer_len -= consumed; } void *Seobeo_Get_IP4_Or_IP6(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); } return &(((struct sockaddr_in6*)sa)->sin6_addr); }