diff --git a/src/system.c b/src/system.c index a278b16..a98a999 100644 --- a/src/system.c +++ b/src/system.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "system.h" #include "chunk_server.h" @@ -98,6 +99,9 @@ typedef struct { // last "poll" call. void *context; + // Whether this socket is configured as non-blocking + bool is_nonblocking; + // Address bound to this descriptor by the // "bind" call. DescriptorAddress address; @@ -811,6 +815,7 @@ SOCKET mock_socket(int domain, int type, int protocol) desc->events = 0; desc->revents = 0; desc->context = NULL; + desc->is_nonblocking = false; desc->address = (DescriptorAddress) { .type=DESC_ADDR_VOID }; current_process->num_desc++; @@ -912,6 +917,11 @@ SOCKET mock_accept(SOCKET fd, void *addr, socklen_t *addr_len) DescriptorHandle peer_handle; if (!accept_queue_pop(&desc->accept_queue, &peer_handle)) { + // Socket would block - abort if not configured as non-blocking + if (!desc->is_nonblocking) { + fprintf(stderr, "SIMULATION ERROR: accept() would block on socket %d but socket is not configured as non-blocking\n", (int)fd); + abort(); + } SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK); // Would block (no pending connections) return INVALID_SOCKET; } @@ -937,6 +947,7 @@ SOCKET mock_accept(SOCKET fd, void *addr, socklen_t *addr_len) new_desc->events = 0; new_desc->revents = 0; new_desc->context = NULL; + new_desc->is_nonblocking = false; new_desc->address = (DescriptorAddress) { .type=DESC_ADDR_VOID }; new_desc->connection_state = CONNECTION_ESTABLISHED; new_desc->connection_peer = peer_handle; @@ -1063,6 +1074,11 @@ int mock_recv(SOCKET fd, void *dst, int len, int flags) // If no data available, would block if (bytes_read == 0) { + // Socket would block - abort if not configured as non-blocking + if (!desc->is_nonblocking) { + fprintf(stderr, "SIMULATION ERROR: recv() would block on socket %d but socket is not configured as non-blocking\n", (int)fd); + abort(); + } SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK); return -1; } @@ -1106,6 +1122,11 @@ int mock_send(SOCKET fd, void *src, int len, int flags) // If queue is full, we would block if (bytes_written < len) { + // Socket would block - abort if not configured as non-blocking + if (!desc->is_nonblocking && bytes_written == 0) { + fprintf(stderr, "SIMULATION ERROR: send() would block on socket %d but socket is not configured as non-blocking\n", (int)fd); + abort(); + } SET_SOCKET_ERROR(SOCKET_ERROR_WOULDBLOCK); return bytes_written > 0 ? bytes_written : -1; } @@ -1451,6 +1472,37 @@ BOOL mock_QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) return TRUE; } +int mock_ioctlsocket(SOCKET fd, long cmd, u_long *argp) +{ + if (fd == INVALID_SOCKET || (int)fd < 0 || (int)fd >= MAX_DESCRIPTORS) { + SET_SOCKET_ERROR(SOCKET_ERROR_BADF); + return -1; + } + + int idx = (int) fd; + Descriptor *desc = ¤t_process->desc[idx]; + + if (desc->type == DESC_EMPTY) { + SET_SOCKET_ERROR(SOCKET_ERROR_BADF); + return -1; + } + + if (desc->type != DESC_SOCKET && + desc->type != DESC_LISTEN_SOCKET && + desc->type != DESC_CONNECTION_SOCKET) { + SET_SOCKET_ERROR(SOCKET_ERROR_NOTSOCK); + return -1; + } + + if (cmd == FIONBIO) { + desc->is_nonblocking = (*argp != 0); + return 0; + } + + SET_SOCKET_ERROR(SOCKET_ERROR_INVAL); + return -1; +} + #else int mock_clock_gettime(clockid_t clockid, struct timespec *tp) @@ -1630,6 +1682,60 @@ int mock_mkdir(char *path, mode_t mode) return mkdir(path, mode); } +int mock_fcntl(int fd, int cmd, ...) +{ + if (fd < 0 || fd >= MAX_DESCRIPTORS) { + errno = EBADF; + return -1; + } + + int idx = fd; + Descriptor *desc = ¤t_process->desc[idx]; + + if (desc->type == DESC_EMPTY) { + errno = EBADF; + return -1; + } + + if (cmd == F_GETFL) { + // Return flags - only O_NONBLOCK is tracked + if (desc->type == DESC_FILE) { + // For files, forward to real fcntl + return fcntl(desc->real_fd, cmd); + } + return desc->is_nonblocking ? O_NONBLOCK : 0; + } + + if (cmd == F_SETFL) { + if (desc->type == DESC_FILE) { + // For files, forward to real fcntl + va_list args; + va_start(args, cmd); + int flags = va_arg(args, int); + va_end(args); + return fcntl(desc->real_fd, cmd, flags); + } + + if (desc->type != DESC_SOCKET && + desc->type != DESC_LISTEN_SOCKET && + desc->type != DESC_CONNECTION_SOCKET) { + errno = EBADF; + return -1; + } + + va_list args; + va_start(args, cmd); + int flags = va_arg(args, int); + va_end(args); + + desc->is_nonblocking = (flags & O_NONBLOCK) != 0; + return 0; + } + + errno = EINVAL; + return -1; +} + #endif // !_WIN32 #endif // BUILD_TEST diff --git a/src/system.h b/src/system.h index de22ea2..aeea168 100644 --- a/src/system.h +++ b/src/system.h @@ -56,6 +56,7 @@ int mock_connect(SOCKET fd, void *addr, size_t addr_len); #ifdef _WIN32 int mock_closesocket(SOCKET fd); +int mock_ioctlsocket(SOCKET fd, long cmd, u_long *argp); HANDLE mock_CreateFileW(WCHAR *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); BOOL mock_CloseHandle(HANDLE handle); BOOL mock_LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh); @@ -80,6 +81,7 @@ int mock_fstat(int fd, struct stat *buf); int mock_mkstemp(char *path); char* mock_realpath(char *path, char *dst); int mock_mkdir(char *path, mode_t mode); +int mock_fcntl(int fd, int cmd, ...); #endif // Common @@ -100,6 +102,7 @@ int mock_mkdir(char *path, mode_t mode); // Windows #define sys__mkdir mock__mkdir #define sys_closesocket mock_closesocket +#define sys_ioctlsocket mock_ioctlsocket #define sys_CreateFileW mock_CreateFileW #define sys_CloseHandle mock_CloseHandle #define sys_LockFile mock_LockFile @@ -124,6 +127,7 @@ int mock_mkdir(char *path, mode_t mode); #define sys_mkstemp mock_mkstemp #define sys_realpath mock_realpath #define sys_clock_gettime mock_clock_gettime +#define sys_fcntl mock_fcntl #define sys_getrandom mock_getrandom #else @@ -146,6 +150,7 @@ int mock_mkdir(char *path, mode_t mode); // Windows #define sys__mkdir _mkdir #define sys_closesocket closesocket +#define sys_ioctlsocket ioctlsocket #define sys_CreateFileW CreateFileW #define sys_CloseHandle CloseHandle #define sys_LockFile LockFile @@ -170,6 +175,7 @@ int mock_mkdir(char *path, mode_t mode); #define sys_mkstemp mkstemp #define sys_realpath realpath #define sys_clock_gettime clock_gettime +#define sys_fcntl fcntl #endif #endif // SYSTEM_INCLUDED diff --git a/src/tcp.c b/src/tcp.c index 38a0f2d..0cef493 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -30,6 +30,21 @@ static SOCKET create_listen_socket(string addr, uint16_t port) if (fd == INVALID_SOCKET) return INVALID_SOCKET; + // Set socket as non-blocking +#ifdef _WIN32 + u_long mode = 1; + if (sys_ioctlsocket(fd, FIONBIO, &mode) != 0) { + CLOSE_SOCKET(fd); + return INVALID_SOCKET; + } +#else + int flags = sys_fcntl(fd, F_GETFL, 0); + if (flags < 0 || sys_fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + CLOSE_SOCKET(fd); + return INVALID_SOCKET; + } +#endif + char tmp[1<<10]; if (addr.len >= (int) sizeof(tmp)) return INVALID_SOCKET; @@ -200,6 +215,20 @@ int tcp_translate_events(TCP *tcp, Event *events, void **contexts, struct pollfd SOCKET new_fd = sys_accept(tcp->listen_fd, NULL, NULL); if (new_fd != INVALID_SOCKET) { + // Set accepted socket as non-blocking +#ifdef _WIN32 + u_long mode = 1; + if (sys_ioctlsocket(new_fd, FIONBIO, &mode) != 0) { + CLOSE_SOCKET(new_fd); + continue; + } +#else + int flags = sys_fcntl(new_fd, F_GETFL, 0); + if (flags < 0 || sys_fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) < 0) { + CLOSE_SOCKET(new_fd); + continue; + } +#endif events[num_events++] = (Event) { EVENT_CONNECT, tcp->num_conns }; conn_init(&tcp->conns[tcp->num_conns++], new_fd, false); } @@ -304,6 +333,21 @@ int tcp_connect(TCP *tcp, Address addr, int tag, ByteQueue **output) if (fd == INVALID_SOCKET) return -1; + // Set socket as non-blocking +#ifdef _WIN32 + u_long mode = 1; + if (sys_ioctlsocket(fd, FIONBIO, &mode) != 0) { + CLOSE_SOCKET(fd); + return -1; + } +#else + int flags = sys_fcntl(fd, F_GETFL, 0); + if (flags < 0 || sys_fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + CLOSE_SOCKET(fd); + return -1; + } +#endif + int ret; if (addr.is_ipv4) { struct sockaddr_in buf;