/* Отказы пересоздания, портовая 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 #include 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; }