/* test-srpl.c * * Copyright (c) 2023-2024 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This file contains tools for SRP replication. */ #include #include "srp.h" #include "srp-test-runner.h" #include "srp-api.h" #include "dns-msg.h" #include "ioloop.h" #include "srp-proxy.h" #include "srp-dnssd.h" #include "srp-mdns-proxy.h" #include "test-api.h" #include "srp-replication.h" #include "test-packet.h" #include "test-dnssd.h" #include "dnssd-proxy.h" #include "test-srpl.h" #include "srp-tls.h" #include #define MAX_PACKETS 10 typedef struct test_packet_state test_packet_state_t; struct test_packet_state { test_state_t *test_state; client_state_t *current_client; srpl_connection_t *srpl_connection; message_t *packets[MAX_PACKETS]; int num_packets; }; static bool tls_init = false; static void test_srpl_input(comm_t *comm, message_t *message, void *context) { srp_server_t *server_state = context; dns_proxy_input_for_server(comm, server_state, message, NULL); } static void test_srpl_listener_ready(void *context, uint16_t port) { srp_server_t *server = context; srpl_connection_t *srpl_connection; srpl_instance_service_t *service; address_query_t *address_query; // listener is ready, so we start to connect on the outgoing connections for (srpl_connection = server->connections; srpl_connection != NULL; srpl_connection = srpl_connection->next) { srpl_instance_t *instance = srpl_connection->instance; service = calloc(1, sizeof(*service)); RETAIN_HERE(service, srpl_instance_service); service->outgoing_port = port; address_query = calloc(1, sizeof(*address_query)); RETAIN_HERE(address_query, address_query); inet_pton(AF_INET, "127.0.0.1", &address_query->addresses[0].sin.sin_addr); address_query->addresses[0].sa.sa_family = AF_INET; address_query->num_addresses = 1; address_query->cur_address = -1; service->address_query = address_query; service->instance = instance; RETAIN_HERE(instance, srpl_instance); service->domain = instance->domain; RETAIN_HERE(service->domain, srpl_domain); instance->services = service; srpl_connection_next_state(srpl_connection, srpl_state_next_address_get); address_query->cur_address = -1; } } static void test_srpl_connection_connected(comm_t *connection, void *context) { srp_server_t *server = context; connection->srp_server = server; } srp_server_t * test_srpl_add_server(test_state_t *state) { static int server_id = 1; srp_server_t *server = server_state_create("srp-mdns-proxy-2", 3600 * 27, // max lease time one day plus 20% 30, // min lease time 30 seconds 3600 * 24 * 7, // max key lease 7 days 30); // min key lease time 30s server->advertise_interface = if_nametoindex("lo0"); // Test is local to the device. server->test_state = state; server->srp_replication_enabled = true; srp_test_enable_stub_router(state, server); server->server_id = server_id; server_id++; return server; } static srpl_instance_t* test_srpl_instance_create(test_state_t *state, srp_server_t *server) { srpl_instance_t *instance = calloc(1, sizeof (*instance)); TEST_FAIL_CHECK(state, instance != NULL, "no memory for instance"); instance->instance_name = strdup("single-srpl-instance"); TEST_FAIL_CHECK(state, instance->instance_name != NULL, "no memory for instance name"); instance->domain = srpl_domain_create_or_copy(server, "openthread.thread.home.arpa"); TEST_FAIL_CHECK(state, instance->domain != NULL, "no domain created"); instance->domain->srpl_opstate = SRPL_OPSTATE_ROUTINE; instance->have_partner_id = true; server->current_thread_domain_name = strdup(instance->domain->name); instance->domain->instances = instance; RETAIN_HERE(instance->domain, srpl_domain); RETAIN_HERE(instance->domain->instances, srpl_instance); return instance; } static srpl_connection_t * test_srpl_instance_connection_create(test_state_t *state, srp_server_t *server) { srpl_instance_t *instance = test_srpl_instance_create(state, server); TEST_FAIL_CHECK(state, instance != NULL, "no memory for instance"); srpl_connection_t *srpl_connection = srpl_connection_create(instance, true); TEST_FAIL_CHECK(state, srpl_connection != NULL, "srpl_connection_create failed"); srpl_connection->state = srpl_state_connecting; instance->connection = srpl_connection; RETAIN_HERE(instance->connection, srpl_connection); srpl_connection->test_state = state; return srpl_connection; } // create outgoing connection from client to server. It'll connect once the listener // on the server side is ready. srpl_connection_t * test_srpl_connection_create(test_state_t *state, srp_server_t *server, srp_server_t *client) { srpl_connection_t *connection = test_srpl_instance_connection_create(state, client); connection->next = server->connections; connection->server = server; server->connections = connection; return connection; } // server starts listener and gets ready for srpl connection void test_srpl_start_replication(srp_server_t *server, int16_t port) { test_state_t *state = server->test_state; // create a domain, and an instance and tie them together. Also create a address_query so that // the incoming connection can be recognized. srpl_instance_t *instance = test_srpl_instance_create(state, server); TEST_FAIL_CHECK(state, instance != NULL, "no memory for instance"); srpl_instance_service_t *service = calloc(1, sizeof (*service)); TEST_FAIL_CHECK(state, service != NULL, "no memory for service"); service->instance = instance; RETAIN_HERE(instance, srpl_instance); instance->services = service; RETAIN_HERE(service, srpl_instance_service); // create an address_query for local loopback address so that the incoming // connection can be recognized. addr_t addr; memset(&addr, 0, sizeof(addr)); inet_pton(AF_INET, "127.0.0.1", &addr.sin.sin_addr); addr.sa.sa_family = AF_INET; addr.sin.sin_port = htons(port); addr.sa.sa_len = sizeof(addr.sin); address_query_t *address_query = calloc(1, sizeof (*address_query)); TEST_FAIL_CHECK(state, address_query != NULL, "no memory for address query"); address_query->num_addresses = 1; address_query->cur_address = 0; memcpy(&address_query->addresses[0], &addr, sizeof(addr)); service->address_query = address_query; RETAIN_HERE(address_query, address_query); instance->domain->srpl_opstate = SRPL_OPSTATE_ROUTINE; // we intentionaly pick the smallest partner id so that it would not reject // any incoming connection. instance->domain->partner_id = 0; if (!tls_init) { bool succeeded = srp_tls_init(); TEST_FAIL_CHECK(state, succeeded, "srp_tls_init failed"); tls_init = true; } server->srpl_listener = ioloop_listener_create(true, true, false, NULL, 0, &addr, NULL, "SRPL Listener", test_srpl_input, test_srpl_connection_connected, NULL, test_srpl_listener_ready, NULL, srp_tls_configure, 0, server); TEST_FAIL_CHECK(state, server->srpl_listener != NULL, "no memory for listener"); server->srpl_listener->srp_server = server; } // Local Variables: // mode: C // tab-width: 4 // c-file-style: "bsd" // c-basic-offset: 4 // fill-column: 108 // indent-tabs-mode: nil // End: