163 lines
8.9 KiB
C
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;
|
|
}
|