blob: 5b2b5365739407c4be948bc61a3a92c388f2df3c [file] [log] [blame] [edit]
/* 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 <dns_sd.h>
#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 <arpa/inet.h>
#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: