Browse Source
- Add socket_compat.h/c with platform abstraction for POSIX/Windows - socket_t typedef: int on POSIX, SOCKET on Windows - Add socket_platform_init/cleanup for WSAStartup on Windows - Add socket operations: create_udp, sendto, recvfrom, set_nonblocking - Add socket options: set_buffers, set_reuseaddr, set_mark (Linux), bind_to_device (Linux) - Update u_async: add socket_t support with uasync_add_socket_t/remove_socket_t - Update ETCP: use socket_t and socket_compat functions - Add DEBUG_CATEGORY_SOCKET for socket debugging - All 22 tests pass on Linux - MSYS2 UCRT64 compatiblenodeinfo-routing-update
8 changed files with 574 additions and 104 deletions
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Socket compatibility layer implementation |
||||
*/ |
||||
|
||||
#include "socket_compat.h" |
||||
#include "debug_config.h" |
||||
|
||||
#ifdef _WIN32 |
||||
// ==================== Windows Implementation ====================
|
||||
|
||||
int socket_platform_init(void) { |
||||
WSADATA wsaData; |
||||
int result = WSAStartup(MAKEWORD(2, 2), &wsaData); |
||||
if (result != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "WSAStartup failed: %d", result); |
||||
return -1; |
||||
} |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "Winsock 2.2 initialized"); |
||||
return 0; |
||||
} |
||||
|
||||
void socket_platform_cleanup(void) { |
||||
WSACleanup(); |
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "Winsock cleanup completed"); |
||||
} |
||||
|
||||
int socket_set_nonblocking(socket_t sock) { |
||||
u_long mode = 1; |
||||
if (ioctlsocket(sock, FIONBIO, &mode) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET,
|
||||
"ioctlsocket(FIONBIO) failed: %d", socket_get_error()); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int socket_close_wrapper(socket_t sock) { |
||||
return closesocket(sock); |
||||
} |
||||
|
||||
const char* socket_strerror(int err) { |
||||
static char buf[256]; |
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
||||
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
||||
buf, sizeof(buf), NULL); |
||||
return buf; |
||||
} |
||||
|
||||
#else |
||||
// ==================== POSIX Implementation ====================
|
||||
|
||||
int socket_platform_init(void) { |
||||
// No initialization needed on POSIX
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "POSIX socket subsystem ready"); |
||||
return 0; |
||||
} |
||||
|
||||
void socket_platform_cleanup(void) { |
||||
// No cleanup needed on POSIX
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "POSIX socket cleanup completed"); |
||||
} |
||||
|
||||
int socket_set_nonblocking(socket_t sock) { |
||||
int flags = fcntl(sock, F_GETFL, 0); |
||||
if (flags < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET,
|
||||
"fcntl(F_GETFL) failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, |
||||
"fcntl(F_SETFL) failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int socket_close_wrapper(socket_t sock) { |
||||
return close(sock); |
||||
} |
||||
|
||||
const char* socket_strerror(int err) { |
||||
return strerror(err); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
// ==================== Universal Implementations ====================
|
||||
|
||||
socket_t socket_create_udp(int family) { |
||||
socket_t sock = socket(family, SOCK_DGRAM, IPPROTO_UDP); |
||||
if (sock == SOCKET_INVALID) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, |
||||
"socket(SOCK_DGRAM) failed: %s", socket_strerror(socket_get_error())); |
||||
return SOCKET_INVALID; |
||||
} |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "Created UDP socket: %ld", (long)sock); |
||||
return sock; |
||||
} |
||||
|
||||
int socket_set_buffers(socket_t sock, int sndbuf, int rcvbuf) { |
||||
int err = 0; |
||||
|
||||
#ifdef _WIN32 |
||||
// Windows uses char* for optval
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, |
||||
(const char*)&sndbuf, sizeof(sndbuf)) != 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_SNDBUF failed: %s", socket_strerror(socket_get_error())); |
||||
err = -1; |
||||
} |
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, |
||||
(const char*)&rcvbuf, sizeof(rcvbuf)) != 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_RCVBUF failed: %s", socket_strerror(socket_get_error())); |
||||
err = -1; |
||||
} |
||||
#else |
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_SNDBUF failed: %s", strerror(errno)); |
||||
err = -1; |
||||
} |
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_RCVBUF failed: %s", strerror(errno)); |
||||
err = -1; |
||||
} |
||||
#endif |
||||
|
||||
return err; |
||||
} |
||||
|
||||
int socket_set_reuseaddr(socket_t sock, int reuse) { |
||||
#ifdef _WIN32 |
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, |
||||
(const char*)&reuse, sizeof(reuse)) != 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_REUSEADDR failed: %s", socket_strerror(socket_get_error())); |
||||
return -1; |
||||
} |
||||
#else |
||||
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_REUSEADDR failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
#endif |
||||
return 0; |
||||
} |
||||
|
||||
int socket_bind_to_device(socket_t sock, const char* ifname) { |
||||
#ifndef _WIN32 |
||||
#ifdef SO_BINDTODEVICE |
||||
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_BINDTODEVICE failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
#else |
||||
(void)sock; |
||||
(void)ifname; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SO_BINDTODEVICE not available"); |
||||
return -1; |
||||
#endif |
||||
#else |
||||
// Windows doesn't support SO_BINDTODEVICE
|
||||
(void)sock; |
||||
(void)ifname; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SO_BINDTODEVICE not available on Windows"); |
||||
return -1; |
||||
#endif |
||||
} |
||||
|
||||
int socket_set_mark(socket_t sock, int mark) { |
||||
#ifndef _WIN32 |
||||
#ifdef SO_MARK |
||||
if (setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, |
||||
"SO_MARK failed: %s", strerror(errno)); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
#else |
||||
(void)sock; |
||||
(void)mark; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SO_MARK not available"); |
||||
return -1; |
||||
#endif |
||||
#else |
||||
// Windows doesn't support SO_MARK
|
||||
(void)sock; |
||||
(void)mark; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SO_MARK not available on Windows"); |
||||
return -1; |
||||
#endif |
||||
} |
||||
|
||||
ssize_t socket_sendto(socket_t sock, const void* buf, size_t len, |
||||
struct sockaddr* dest, socklen_t dest_len) { |
||||
#ifdef _WIN32 |
||||
return sendto(sock, (const char*)buf, (int)len, 0, dest, dest_len); |
||||
#else |
||||
return sendto(sock, buf, len, 0, dest, dest_len); |
||||
#endif |
||||
} |
||||
|
||||
ssize_t socket_recvfrom(socket_t sock, void* buf, size_t len, |
||||
struct sockaddr* src, socklen_t* src_len) { |
||||
#ifdef _WIN32 |
||||
int fromlen = src_len ? (int)*src_len : 0; |
||||
ssize_t ret = recvfrom(sock, (char*)buf, (int)len, 0, src, src_len ? &fromlen : NULL); |
||||
if (src_len) *src_len = (socklen_t)fromlen; |
||||
return ret; |
||||
#else |
||||
return recvfrom(sock, buf, len, 0, src, src_len); |
||||
#endif |
||||
} |
||||
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Socket compatibility layer for cross-platform support (POSIX / Windows) |
||||
* MSYS2 UCRT64 compatible |
||||
*/ |
||||
|
||||
#ifndef SOCKET_COMPAT_H |
||||
#define SOCKET_COMPAT_H |
||||
|
||||
#ifdef _WIN32 |
||||
#include <winsock2.h> |
||||
#include <ws2tcpip.h> |
||||
|
||||
typedef SOCKET socket_t; |
||||
#define SOCKET_INVALID INVALID_SOCKET |
||||
#define SOCKET_ERROR_CODE SOCKET_ERROR |
||||
|
||||
// Error codes
|
||||
#define ERR_WOULDBLOCK WSAEWOULDBLOCK |
||||
#define ERR_AGAIN WSAEWOULDBLOCK |
||||
#define ERR_INTR WSAEINTR |
||||
|
||||
#else |
||||
// POSIX systems
|
||||
#include <sys/socket.h> |
||||
#include <arpa/inet.h> |
||||
#include <netinet/in.h> |
||||
#include <fcntl.h> |
||||
#include <unistd.h> |
||||
#include <errno.h> |
||||
#include <string.h> |
||||
|
||||
typedef int socket_t; |
||||
#define SOCKET_INVALID (-1) |
||||
#define SOCKET_ERROR_CODE (-1) |
||||
|
||||
#define ERR_WOULDBLOCK EWOULDBLOCK |
||||
#define ERR_AGAIN EAGAIN |
||||
#define ERR_INTR EINTR |
||||
#endif |
||||
|
||||
// Platform initialization/cleanup
|
||||
int socket_platform_init(void); |
||||
void socket_platform_cleanup(void); |
||||
|
||||
// Socket operations
|
||||
socket_t socket_create_udp(int family); |
||||
int socket_set_nonblocking(socket_t sock); |
||||
int socket_close_wrapper(socket_t sock); |
||||
static inline int socket_get_error(void) { |
||||
#ifdef _WIN32 |
||||
return WSAGetLastError(); |
||||
#else |
||||
return errno; |
||||
#endif |
||||
} |
||||
|
||||
// Socket options
|
||||
int socket_set_buffers(socket_t sock, int sndbuf, int rcvbuf); |
||||
int socket_set_reuseaddr(socket_t sock, int reuse); |
||||
int socket_bind_to_device(socket_t sock, const char* ifname); |
||||
int socket_set_mark(socket_t sock, int mark); |
||||
|
||||
// I/O operations
|
||||
ssize_t socket_sendto(socket_t sock, const void* buf, size_t len, |
||||
struct sockaddr* dest, socklen_t dest_len); |
||||
ssize_t socket_recvfrom(socket_t sock, void* buf, size_t len, |
||||
struct sockaddr* src, socklen_t* src_len); |
||||
|
||||
// Utility
|
||||
const char* socket_strerror(int err); |
||||
|
||||
#endif // SOCKET_COMPAT_H
|
||||
Loading…
Reference in new issue