You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

342 lines
11 KiB

#include "test_udp_socket.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#ifndef DEBUG_CATEGORY_TEST
#define DEBUG_CATEGORY_TEST (1 << 30)
#endif
// Global socket registry for fd-based operations
static struct test_udp_socket* g_socket_registry[1024] = {NULL};
pthread_mutex_t g_registry_mutex = PTHREAD_MUTEX_INITIALIZER; // Make it non-static for external access
static int g_next_fd = 1000; // Start with high fd numbers to avoid conflicts
// Helper function to allocate packet
static struct test_udp_packet* allocate_packet(const uint8_t* data, size_t len,
const struct sockaddr* addr, socklen_t addr_len) {
struct test_udp_packet* packet = calloc(1, sizeof(struct test_udp_packet));
if (!packet) return NULL;
packet->data = malloc(len);
if (!packet->data) {
free(packet);
return NULL;
}
memcpy(packet->data, data, len);
packet->len = len;
if (addr && addr_len > 0 && addr_len <= sizeof(struct sockaddr_storage)) {
memcpy(&packet->addr, addr, addr_len);
packet->addr_len = addr_len;
} else {
packet->addr_len = 0;
}
return packet;
}
// Helper function to free packet
static void free_packet(struct test_udp_packet* packet) {
if (!packet) return;
if (packet->data) free(packet->data);
free(packet);
}
// Create virtual UDP socket
struct test_udp_socket* test_udp_socket_create(int family) {
if (family != AF_INET && family != AF_INET6) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Unsupported address family: %d", family);
return NULL;
}
struct test_udp_socket* sock = calloc(1, sizeof(struct test_udp_socket));
if (!sock) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate UDP socket");
return NULL;
}
// Assign virtual file descriptor
pthread_mutex_lock(&g_registry_mutex);
sock->fd = g_next_fd++;
if (g_next_fd >= 2000) g_next_fd = 1000; // Wrap around
pthread_mutex_unlock(&g_registry_mutex);
sock->family = family;
sock->bound = false;
sock->nonblocking = false;
sock->recv_queue_head = NULL;
sock->recv_queue_tail = NULL;
sock->recv_queue_size = 0;
// Register socket
test_udp_socket_register(sock);
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Created virtual UDP socket: fd=%d, family=%d", sock->fd, family);
return sock;
}
// Destroy virtual UDP socket
void test_udp_socket_destroy(struct test_udp_socket* sock) {
if (!sock) return;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Destroying virtual UDP socket: fd=%d (stats: sent=%zu/%zu, recv=%zu/%zu, errors=%zu/%zu)",
sock->fd, sock->stats.packets_sent, sock->stats.bytes_sent,
sock->stats.packets_received, sock->stats.bytes_received,
sock->stats.send_errors, sock->stats.recv_errors);
// Unregister socket
test_udp_socket_unregister(sock);
// Free receive queue
struct test_udp_packet* packet = sock->recv_queue_head;
while (packet) {
struct test_udp_packet* next = packet->next;
free_packet(packet);
packet = next;
}
free(sock);
}
// Bind virtual socket
int test_udp_socket_bind(struct test_udp_socket* sock, const struct sockaddr* addr, socklen_t len) {
if (!sock || !addr || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for socket bind");
return -1;
}
if (sock->bound) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Socket already bound");
return 0;
}
if (len > sizeof(struct sockaddr_storage)) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Address too large: %d", len);
return -1;
}
memcpy(&sock->local_addr, addr, len);
sock->bound = true;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Bound virtual UDP socket fd=%d", sock->fd);
return 0;
}
// Set socket options (limited implementation for testing)
int test_udp_socket_setsockopt(struct test_udp_socket* sock, int level, int optname,
const void *optval, socklen_t optlen) {
if (!sock) return -1;
// For testing purposes, just log the option setting
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "setsockopt on fd=%d: level=%d, optname=%d, optlen=%d",
sock->fd, level, optname, optlen);
// Handle some common options
if (level == SOL_SOCKET) {
switch (optname) {
case SO_REUSEADDR:
case SO_REUSEPORT:
// Always allow for testing
return 0;
case SO_BROADCAST:
return 0;
default:
break;
}
}
return 0; // Pretend success for most options
}
// Get socket options
int test_udp_socket_getsockopt(struct test_udp_socket* sock, int level, int optname,
void *optval, socklen_t *optlen) {
if (!sock || !optval || !optlen) return -1;
// For testing purposes, return reasonable defaults
if (level == SOL_SOCKET) {
switch (optname) {
case SO_ERROR:
*(int*)optval = 0;
*optlen = sizeof(int);
return 0;
default:
break;
}
}
return -1; // Not implemented
}
// Set non-blocking mode
int test_udp_socket_set_nonblocking(struct test_udp_socket* sock, bool nonblocking) {
if (!sock) return -1;
sock->nonblocking = nonblocking;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Set nonblocking on fd=%d: %s", sock->fd, nonblocking ? "true" : "false");
return 0;
}
// Send packet using virtual socket
ssize_t test_udp_socket_sendto(struct test_udp_socket* sock, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addr_len) {
if (!sock || !buf || len == 0) {
return -1;
}
const uint8_t* data = (const uint8_t*)buf;
// Call packet sent callback if available
if (sock->packet_sent) {
sock->packet_sent(data, len, dest_addr, addr_len, sock->context);
}
sock->stats.packets_sent++;
sock->stats.bytes_sent += len;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Sent packet on fd=%d: %zu bytes to family=%d",
sock->fd, len, dest_addr ? dest_addr->sa_family : -1);
return len;
}
// Receive packet from virtual socket
ssize_t test_udp_socket_recvfrom(struct test_udp_socket* sock, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addr_len) {
if (!sock || !buf || len == 0) {
return -1;
}
uint8_t* buffer = (uint8_t*)buf;
// Check if we have packets in queue
pthread_mutex_lock(&g_registry_mutex);
struct test_udp_packet* packet = sock->recv_queue_head;
if (!packet) {
pthread_mutex_unlock(&g_registry_mutex);
errno = EAGAIN;
return -1;
}
// Remove packet from queue
sock->recv_queue_head = packet->next;
if (!sock->recv_queue_head) {
sock->recv_queue_tail = NULL;
}
sock->recv_queue_size--;
pthread_mutex_unlock(&g_registry_mutex);
// Copy packet data
size_t copy_len = packet->len < len ? packet->len : len;
memcpy(buffer, packet->data, copy_len);
// Copy source address if requested
if (src_addr && addr_len && packet->addr_len > 0) {
socklen_t copy_addr_len = packet->addr_len < *addr_len ? packet->addr_len : *addr_len;
memcpy(src_addr, &packet->addr, copy_addr_len);
*addr_len = copy_addr_len;
}
sock->stats.packets_received++;
sock->stats.bytes_received += copy_len;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Received packet on fd=%d: %zu bytes", sock->fd, copy_len);
free_packet(packet);
return copy_len;
}
// Inject packet into receive queue (simulates incoming packet)
int test_udp_socket_inject(struct test_udp_socket* sock,
const uint8_t* data, size_t len,
const struct sockaddr* src_addr, socklen_t addr_len) {
if (!sock || !data || len == 0) {
return -1;
}
if (sock->recv_queue_size >= TEST_UDP_MAX_QUEUE_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Receive queue full on fd=%d", sock->fd);
return -1;
}
struct test_udp_packet* packet = allocate_packet(data, len, src_addr, addr_len);
if (!packet) {
return -1;
}
pthread_mutex_lock(&g_registry_mutex);
if (sock->recv_queue_tail) {
sock->recv_queue_tail->next = packet;
} else {
sock->recv_queue_head = packet;
}
sock->recv_queue_tail = packet;
sock->recv_queue_size++;
pthread_mutex_unlock(&g_registry_mutex);
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Injected packet into fd=%d: %zu bytes", sock->fd, len);
return 0;
}
// Get virtual socket file descriptor
int test_udp_socket_get_fd(struct test_udp_socket* sock) {
return sock ? sock->fd : -1;
}
// Get socket statistics
void test_udp_socket_get_stats(struct test_udp_socket* sock, size_t* packets_sent,
size_t* packets_received, size_t* bytes_sent,
size_t* bytes_received, size_t* send_errors, size_t* recv_errors) {
if (!sock) return;
if (packets_sent) *packets_sent = sock->stats.packets_sent;
if (packets_received) *packets_received = sock->stats.packets_received;
if (bytes_sent) *bytes_sent = sock->stats.bytes_sent;
if (bytes_received) *bytes_received = sock->stats.bytes_received;
if (send_errors) *send_errors = sock->stats.send_errors;
if (recv_errors) *recv_errors = sock->stats.recv_errors;
}
// Reset socket statistics
void test_udp_socket_reset_stats(struct test_udp_socket* sock) {
if (!sock) return;
memset(&sock->stats, 0, sizeof(sock->stats));
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Reset statistics for virtual UDP socket fd=%d", sock->fd);
}
// Global virtual socket registry for fd-based operations
struct test_udp_socket* test_udp_socket_find_by_fd(int fd) {
pthread_mutex_lock(&g_registry_mutex);
struct test_udp_socket* sock = NULL;
if (fd >= 0 && fd < 1024) {
sock = g_socket_registry[fd];
}
pthread_mutex_unlock(&g_registry_mutex);
return sock;
}
void test_udp_socket_register(struct test_udp_socket* sock) {
if (!sock) return;
pthread_mutex_lock(&g_registry_mutex);
if (sock->fd >= 0 && sock->fd < 1024) {
g_socket_registry[sock->fd] = sock;
}
pthread_mutex_unlock(&g_registry_mutex);
}
void test_udp_socket_unregister(struct test_udp_socket* sock) {
if (!sock) return;
pthread_mutex_lock(&g_registry_mutex);
if (sock->fd >= 0 && sock->fd < 1024) {
g_socket_registry[sock->fd] = NULL;
}
pthread_mutex_unlock(&g_registry_mutex);
}