1
0
Fork 0
bind9/bin/tests/test_server.c
Daniel Baumann f66ff7eae6
Adding upstream version 1:9.20.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:32:37 +02:00

307 lines
6.7 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <getopt.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <isc/managers.h>
#include <isc/mem.h>
#include <isc/netaddr.h>
#include <isc/netmgr.h>
#include <isc/os.h>
#include <isc/sockaddr.h>
#include <isc/string.h>
#include <isc/util.h>
typedef enum { UDP, TCP, DOT, HTTPS, HTTP } protocol_t;
static const char *protocols[] = { "udp", "tcp", "dot", "https", "http-plain" };
static isc_mem_t *mctx = NULL;
static isc_loopmgr_t *loopmgr = NULL;
static isc_nm_t *netmgr = NULL;
static protocol_t protocol;
static in_port_t port;
static isc_netaddr_t netaddr;
static isc_sockaddr_t sockaddr ISC_ATTR_UNUSED;
static int workers;
static isc_tlsctx_t *tls_ctx = NULL;
static void
read_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
void *cbarg);
static void
send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg);
static isc_result_t
parse_port(const char *input) {
char *endptr = NULL;
long val = strtol(input, &endptr, 10);
if ((*endptr != '\0') || (val <= 0) || (val >= 65536)) {
return ISC_R_BADNUMBER;
}
port = (in_port_t)val;
return ISC_R_SUCCESS;
}
static isc_result_t
parse_protocol(const char *input) {
for (size_t i = 0; i < ARRAY_SIZE(protocols); i++) {
if (!strcasecmp(input, protocols[i])) {
protocol = i;
return ISC_R_SUCCESS;
}
}
return ISC_R_BADNUMBER;
}
static isc_result_t
parse_address(const char *input) {
struct in6_addr in6;
struct in_addr in;
if (inet_pton(AF_INET6, input, &in6) == 1) {
isc_netaddr_fromin6(&netaddr, &in6);
return ISC_R_SUCCESS;
}
if (inet_pton(AF_INET, input, &in) == 1) {
isc_netaddr_fromin(&netaddr, &in);
return ISC_R_SUCCESS;
}
return ISC_R_BADADDRESSFORM;
}
static int
parse_workers(const char *input) {
char *endptr = NULL;
long val = strtol(input, &endptr, 10);
if ((*endptr != '\0') || (val <= 0) || (val >= 128)) {
return ISC_R_BADNUMBER;
}
workers = val;
return ISC_R_SUCCESS;
}
static void
parse_options(int argc, char **argv) {
char buf[ISC_NETADDR_FORMATSIZE];
/* Set defaults */
RUNTIME_CHECK(parse_protocol("UDP") == ISC_R_SUCCESS);
RUNTIME_CHECK(parse_port("53000") == ISC_R_SUCCESS);
RUNTIME_CHECK(parse_address("::1") == ISC_R_SUCCESS);
workers = isc_os_ncpus();
while (true) {
int c;
int option_index = 0;
static struct option long_options[] = {
{ "port", required_argument, NULL, 'p' },
{ "address", required_argument, NULL, 'a' },
{ "protocol", required_argument, NULL, 'P' },
{ "workers", required_argument, NULL, 'w' },
{ 0, 0, NULL, 0 }
};
c = getopt_long(argc, argv, "a:p:P:w:", long_options,
&option_index);
if (c == -1) {
break;
}
switch (c) {
case 'a':
RUNTIME_CHECK(parse_address(optarg) == ISC_R_SUCCESS);
break;
case 'p':
RUNTIME_CHECK(parse_port(optarg) == ISC_R_SUCCESS);
break;
case 'P':
RUNTIME_CHECK(parse_protocol(optarg) == ISC_R_SUCCESS);
break;
case 'w':
RUNTIME_CHECK(parse_workers(optarg) == ISC_R_SUCCESS);
break;
default:
UNREACHABLE();
}
}
isc_sockaddr_fromnetaddr(&sockaddr, &netaddr, port);
isc_sockaddr_format(&sockaddr, buf, sizeof(buf));
printf("Will listen at %s://%s, %d workers\n", protocols[protocol], buf,
workers);
}
static void
setup(void) {
isc_managers_create(&mctx, workers, &loopmgr, &netmgr);
}
static void
teardown(void) {
if (tls_ctx) {
isc_tlsctx_free(&tls_ctx);
}
isc_managers_destroy(&mctx, &loopmgr, &netmgr);
}
static void
test_server_yield(void) {
sigset_t sset;
int sig;
RUNTIME_CHECK(sigemptyset(&sset) == 0);
RUNTIME_CHECK(sigaddset(&sset, SIGHUP) == 0);
RUNTIME_CHECK(sigaddset(&sset, SIGINT) == 0);
RUNTIME_CHECK(sigaddset(&sset, SIGTERM) == 0);
RUNTIME_CHECK(sigwait(&sset, &sig) == 0);
fprintf(stderr, "Shutting down...\n");
}
static void
read_cb(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
void *cbarg) {
isc_region_t *reply = NULL;
REQUIRE(handle != NULL);
REQUIRE(eresult == ISC_R_SUCCESS);
UNUSED(cbarg);
fprintf(stderr, "RECEIVED %u bytes\n", region->length);
if (region->length >= 12) {
/* long enough to be a DNS header, set QR bit */
((uint8_t *)region->base)[2] ^= 0x80;
}
reply = isc_mem_get(mctx, sizeof(isc_region_t) + region->length);
reply->length = region->length;
reply->base = (uint8_t *)reply + sizeof(isc_region_t);
memmove(reply->base, region->base, region->length);
isc_nm_send(handle, reply, send_cb, reply);
return;
}
static void
send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_region_t *reply = cbarg;
REQUIRE(handle != NULL);
REQUIRE(eresult == ISC_R_SUCCESS);
isc_mem_put(mctx, cbarg, sizeof(isc_region_t) + reply->length);
}
static isc_result_t
accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
REQUIRE(handle != NULL);
REQUIRE(eresult == ISC_R_SUCCESS);
UNUSED(cbarg);
return ISC_R_SUCCESS;
}
static void
run(void) {
isc_result_t result;
isc_nmsocket_t *sock = NULL;
switch (protocol) {
case UDP:
result = isc_nm_listenudp(netmgr, ISC_NM_LISTEN_ALL, &sockaddr,
read_cb, NULL, &sock);
break;
case TCP:
result = isc_nm_listenstreamdns(netmgr, ISC_NM_LISTEN_ALL,
&sockaddr, read_cb, NULL,
accept_cb, NULL, 0, NULL, NULL,
ISC_NM_PROXY_NONE, &sock);
break;
case DOT: {
isc_tlsctx_createserver(NULL, NULL, &tls_ctx);
result = isc_nm_listenstreamdns(
netmgr, ISC_NM_LISTEN_ALL, &sockaddr, read_cb, NULL,
accept_cb, NULL, 0, NULL, tls_ctx, ISC_NM_PROXY_NONE,
&sock);
break;
}
#if HAVE_LIBNGHTTP2
case HTTPS:
case HTTP: {
bool is_https = protocol == HTTPS;
isc_nm_http_endpoints_t *eps = NULL;
if (is_https) {
isc_tlsctx_createserver(NULL, NULL, &tls_ctx);
}
eps = isc_nm_http_endpoints_new(mctx);
result = isc_nm_http_endpoints_add(
eps, ISC_NM_HTTP_DEFAULT_PATH, read_cb, NULL);
if (result == ISC_R_SUCCESS) {
result = isc_nm_listenhttp(
netmgr, ISC_NM_LISTEN_ALL, &sockaddr, 0, NULL,
tls_ctx, eps, 0, ISC_NM_PROXY_NONE, &sock);
}
isc_nm_http_endpoints_detach(&eps);
} break;
#endif
default:
UNREACHABLE();
}
REQUIRE(result == ISC_R_SUCCESS);
test_server_yield();
isc_nm_stoplistening(sock);
isc_nmsocket_close(&sock);
}
int
main(int argc, char **argv) {
parse_options(argc, argv);
setup();
run();
teardown();
return EXIT_SUCCESS;
}