Files
utun2/tests/test_auto_socket_faults.c
T

163 lines
8.9 KiB
C

/* Отказы пересоздания, портовая SQLite и отмена pending scan на настоящем ядре. */
#include "nat_fixture.h"
#include "../src/transport_layer/auto_socket.h"
#include "../src/transport_layer/stcp_link.h"
#include "../lib/sqlite3.h"
#include <ifaddrs.h>
#include <net/if.h>
static int fake_network, present = 1, fail_udp, fail_tcp, fail_random, deny_save, creates;
static uint32_t address = 0x7f000001;
static struct sockaddr_in fake_address;
static struct ifaddrs fake_iface;
extern int __real_getifaddrs(struct ifaddrs**);
extern void __real_freeifaddrs(struct ifaddrs*);
int __wrap_getifaddrs(struct ifaddrs** out) {
if (!fake_network) return __real_getifaddrs(out);
memset(&fake_address, 0, sizeof(fake_address));
fake_address.sin_family = AF_INET; fake_address.sin_addr.s_addr = htonl(address);
fake_iface = (struct ifaddrs){.ifa_name = "lo", .ifa_flags = present ? IFF_UP : 0,
.ifa_addr = (struct sockaddr*)&fake_address};
*out = &fake_iface; return 0;
}
void __wrap_freeifaddrs(struct ifaddrs* list) { if (list != &fake_iface) __real_freeifaddrs(list); }
extern struct ETCP_SOCKET* __real_etcp_socket_add(struct UTUN_INSTANCE*, struct CFG_SERVER*);
struct ETCP_SOCKET* __wrap_etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server) {
if (!strncmp(server->name, "as_", 3)) {
creates++;
if (fail_udp) { errno = ENOMEM; return NULL; }
}
return __real_etcp_socket_add(instance, server);
}
extern struct stcp_server* __real_stcp_server_listen(struct stcp_link_config*, uint16_t,
stcp_server_on_link_cb, struct ETCP_SOCKET*);
struct stcp_server* __wrap_stcp_server_listen(struct stcp_link_config* config, uint16_t port,
stcp_server_on_link_cb callback, struct ETCP_SOCKET* socket) {
if (fail_tcp) { errno = ENOMEM; return NULL; }
return __real_stcp_server_listen(config, port, callback, socket);
}
extern int __real_random_bytes(uint8_t*, size_t);
int __wrap_random_bytes(uint8_t* out, size_t size) {
if (fail_random) { errno = EIO; return -1; }
return __real_random_bytes(out, size);
}
static struct ETCP_SOCKET* owned_socket(struct UTUN_INSTANCE* instance, int tcp) {
struct ETCP_SOCKET* result = NULL;
for (struct ETCP_SOCKET* es = instance->etcp_sockets; es; es = es->next) {
if (strncmp(es->name, "as_lo_", 6) || es->is_tcp != tcp) continue;
assert(!result); result = es;
}
return result;
}
static int port_count(struct UTUN_INSTANCE* instance, int* port) {
sqlite3_stmt* statement = NULL;
assert(sqlite3_prepare_v2(instance->topo_sqlite_db,
"SELECT count(*), max(port) FROM auto_socket_ports WHERE if_name='lo'", -1, &statement, NULL) == SQLITE_OK);
assert(sqlite3_step(statement) == SQLITE_ROW);
int count = sqlite3_column_int(statement, 0);
if (port) *port = sqlite3_column_int(statement, 1);
assert(sqlite3_finalize(statement) == SQLITE_OK);
return count;
}
static int authorizer(void* arg, int operation, const char* table, const char* column,
const char* database, const char* trigger) {
(void)arg; (void)column; (void)database; (void)trigger;
return deny_save && operation == SQLITE_INSERT && table && !strcmp(table, "auto_socket_ports") ? SQLITE_DENY : SQLITE_OK;
}
static void rescan(struct nat_fixture* fixture, struct UTUN_INSTANCE* instance) {
auto_socket_on_network_change(instance); uasync_poll(fixture->ua, 0);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
debug_set_category_level(DEBUG_CATEGORY_SOCKET, DEBUG_LEVEL_INFO);
struct nat_fixture fixture; nat_fixture_init(&fixture);
struct UTUN_INSTANCE* instance = nat_fixture_node(&fixture, "127.0.0.9", NULL, NULL);
struct ETCP_SOCKET* fixed = test_etcp_bound_socket(instance, "udp");
instance->config->global.auto_sockets = CFG_AUTO_SOCKETS_SYSTEM;
instance->config->global.auto_socket_protocols = CFG_AUTO_SOCKET_UDP;
instance->config->global.auto_socket_skip_no_default_route = 0;
fake_network = 1;
assert(auto_socket_init(instance) == 0);
struct ETCP_SOCKET* first = owned_socket(instance, 0); assert(first);
int saved_port; assert(port_count(instance, &saved_port) == 1 && saved_port == ss_get_port(&first->local_addr));
auto_socket_destroy(instance);
assert(!owned_socket(instance, 0) && port_count(instance, NULL) == 1 && instance->etcp_sockets == fixed);
assert(auto_socket_init(instance) == 0);
assert(ss_get_port(&owned_socket(instance, 0)->local_addr) == saved_port);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS auto port persistence and owned endpoint destruction");
address = 0x7f000002; fail_udp = 1;
rescan(&fixture, instance);
assert(!owned_socket(instance, 0));
fail_udp = 0; rescan(&fixture, instance);
assert(owned_socket(instance, 0) && ntohl(((struct sockaddr_in*)&owned_socket(instance, 0)->local_addr)->sin_addr.s_addr) == address);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS failed UDP replacement leaves no stale pointer; next scan recovers");
auto_socket_on_network_change(instance); auto_socket_on_network_change(instance);
int before = creates;
auto_socket_destroy(instance); uasync_poll(fixture.ua, 0);
assert(creates == before && !instance->auto_socket_state && !owned_socket(instance, 0));
assert(auto_socket_init(instance) == 0);
present = 0; rescan(&fixture, instance);
assert(!owned_socket(instance, 0) && port_count(instance, NULL) == 0);
auto_socket_destroy(instance); present = 1;
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS pending scan cancellation and pruning absent interfaces");
assert(sqlite3_set_authorizer(instance->topo_sqlite_db, authorizer, NULL) == SQLITE_OK);
deny_save = 1;
assert(auto_socket_init(instance) == 0 && owned_socket(instance, 0));
assert(sqlite3_get_autocommit(instance->topo_sqlite_db) && port_count(instance, NULL) == 0);
auto_socket_destroy(instance); deny_save = 0;
assert(sqlite3_set_authorizer(instance->topo_sqlite_db, NULL, NULL) == SQLITE_OK);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS failed port save does not leave a SQLite transaction open");
fail_random = 1;
assert(auto_socket_init(instance) == -1 && !instance->auto_socket_state && !owned_socket(instance, 0));
fail_random = 0;
instance->config->global.auto_socket_protocols = CFG_AUTO_SOCKET_BOTH;
fail_tcp = 1;
assert(auto_socket_init(instance) == -1 && !instance->auto_socket_state && !owned_socket(instance, 0) && !owned_socket(instance, 1));
fail_tcp = 0;
assert(auto_socket_init(instance) == 0 && owned_socket(instance, 0) && owned_socket(instance, 1));
uint64_t tcp_generation = owned_socket(instance, 1)->generation;
uint64_t udp_generation = owned_socket(instance, 0)->generation;
rescan(&fixture, instance);
assert(owned_socket(instance, 1)->generation == tcp_generation && owned_socket(instance, 0)->generation == udp_generation);
address = 0x7f000003; rescan(&fixture, instance);
assert(owned_socket(instance, 1)->generation != tcp_generation && owned_socket(instance, 0)->generation != udp_generation);
auto_socket_on_network_change(instance); auto_socket_destroy(instance); uasync_poll(fixture.ua, 0);
assert(instance->etcp_sockets == fixed && !stcp_server_list_count(instance));
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS entropy failure and partial TCP/UDP init rollback, no resource leaks");
fake_network = 0; instance->config->global.auto_sockets = CFG_AUTO_SOCKETS_ANDROID;
assert(auto_socket_init(instance) == 0);
struct ETCP_SOCKET* android = test_etcp_bound_socket(instance, "udp");
uint64_t original = android->generation;
struct CFG_CLIENT* client = u_calloc(1, sizeof(*client)); assert(client);
struct CFG_CLIENT_LINK* link = u_calloc(1, sizeof(*link)); assert(link);
strcpy(client->name, "reference-test"); client->links = link; link->local_srv = instance->config->servers;
instance->config->clients = client;
const char* same = "[server:udp]\naddr=127.0.0.9:0\ntype=public\n";
struct utun_config* updated = parse_config_from_buf(same, strlen(same), "auto-reload-test"); assert(updated);
auto_socket_reload_config(instance, updated);
assert(test_etcp_bound_socket(instance, "udp")->generation == original);
assert(link->local_srv == instance->config->servers && !strcmp(link->server_name, "udp"));
const char* changed = "[server:udp]\naddr=127.0.0.9:0\ntype=public\nmtu=1150\n";
updated = parse_config_from_buf(changed, strlen(changed), "auto-reload-test"); assert(updated);
auto_socket_reload_config(instance, updated);
assert(test_etcp_bound_socket(instance, "udp")->generation != original && test_etcp_bound_socket(instance, "udp")->mtu == 1150);
assert(link->local_srv == instance->config->servers);
auto_socket_reload_config(instance, instance->config);
auto_socket_destroy(instance); nat_fixture_cleanup(&fixture);
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PASS unchanged ephemeral bind, changed options and config client reference rebinding");
return 0;
}