1
0
Fork 0
bind9/bin/tests/test_client.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

435 lines
9.2 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 <fcntl.h>
#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <isc/loop.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_POST,
HTTPS_GET,
HTTP_POST,
HTTP_GET
} protocol_t;
static const char *protocols[] = { "udp", "tcp",
"dot", "https-post",
"https-get", "http-plain-post",
"http-plain-get" };
static isc_mem_t *mctx = NULL;
static isc_loopmgr_t *loopmgr = NULL;
static isc_nm_t *netmgr = NULL;
static protocol_t protocol;
static const char *address;
static const char *port;
static int family = AF_UNSPEC;
static isc_sockaddr_t sockaddr_local;
static isc_sockaddr_t sockaddr_remote;
static int workers;
static int timeout;
static uint8_t messagebuf[2 * 65536];
static isc_region_t message = { .length = 0, .base = messagebuf };
static int out = -1;
static isc_tlsctx_t *tls_ctx = NULL;
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 = input;
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) {
family = AF_INET6;
address = input;
return ISC_R_SUCCESS;
}
if (inet_pton(AF_INET, input, &in) == 1) {
family = AF_INET;
address = input;
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 isc_result_t
parse_timeout(const char *input) {
char *endptr = NULL;
long val = strtol(input, &endptr, 10);
if ((*endptr != '\0') || (val <= 0) || (val >= 120)) {
return ISC_R_BADNUMBER;
}
timeout = (in_port_t)val * 1000;
return ISC_R_SUCCESS;
}
static isc_result_t
parse_input(const char *input) {
int in = -1;
if (!strcmp(input, "-")) {
in = 0;
} else {
in = open(input, O_RDONLY);
}
RUNTIME_CHECK(in >= 0);
message.length = read(in, message.base, sizeof(messagebuf));
close(in);
return ISC_R_SUCCESS;
}
static isc_result_t
parse_output(const char *input) {
if (!strcmp(input, "-")) {
out = 1;
} else {
out = open(input, O_WRONLY | O_CREAT,
S_IRUSR | S_IRGRP | S_IROTH);
}
RUNTIME_CHECK(out >= 0);
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("::0") == 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' },
{ "timeout", required_argument, NULL, 't' },
{ "input", required_argument, NULL, 'i' },
{ "output", required_argument, NULL, 'o' },
{ 0, 0, NULL, 0 }
};
c = getopt_long(argc, argv, "a:p:P:w:t:i:o:", 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;
case 't':
RUNTIME_CHECK(parse_timeout(optarg) == ISC_R_SUCCESS);
break;
case 'i':
RUNTIME_CHECK(parse_input(optarg) == ISC_R_SUCCESS);
break;
case 'o':
RUNTIME_CHECK(parse_output(optarg) == ISC_R_SUCCESS);
break;
default:
UNREACHABLE();
}
}
{
struct addrinfo hints = {
.ai_family = family,
.ai_socktype = (protocol == UDP) ? SOCK_DGRAM
: SOCK_STREAM,
};
struct addrinfo *result = NULL;
int r = getaddrinfo(address, NULL, &hints, &result);
RUNTIME_CHECK(r == 0);
for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next)
{
RUNTIME_CHECK(isc_sockaddr_fromsockaddr(&sockaddr_local,
rp->ai_addr) ==
ISC_R_SUCCESS);
}
freeaddrinfo(result);
}
{
struct addrinfo hints = {
.ai_family = family,
.ai_socktype = (protocol == UDP) ? SOCK_DGRAM
: SOCK_STREAM,
};
struct addrinfo *result = NULL;
int r = getaddrinfo(argv[optind], port, &hints, &result);
RUNTIME_CHECK(r == 0);
for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next)
{
RUNTIME_CHECK(isc_sockaddr_fromsockaddr(
&sockaddr_remote, rp->ai_addr) ==
ISC_R_SUCCESS);
}
freeaddrinfo(result);
}
isc_sockaddr_format(&sockaddr_local, buf, sizeof(buf));
printf("Will connect from %s://%s", protocols[protocol], buf);
isc_sockaddr_format(&sockaddr_remote, buf, sizeof(buf));
printf(" to %s, %d workers\n", buf, workers);
}
static void
setup(void) {
isc_managers_create(&mctx, workers, &loopmgr, &netmgr);
}
static void
teardown(void) {
if (out > 0) {
close(out);
}
if (tls_ctx) {
isc_tlsctx_free(&tls_ctx);
}
isc_managers_destroy(&mctx, &loopmgr, &netmgr);
}
static void
waitforsignal(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_nmhandle_t *readhandle = cbarg;
REQUIRE(handle != NULL);
REQUIRE(eresult == ISC_R_SUCCESS || eresult == ISC_R_CANCELED ||
eresult == ISC_R_EOF);
REQUIRE(cbarg != NULL);
fprintf(stderr, "%s(..., %s, ...)\n", __func__,
isc_result_totext(eresult));
if (eresult == ISC_R_SUCCESS) {
printf("RECEIVED %u bytes\n", region->length);
if (out >= 0) {
ssize_t len = write(out, region->base, region->length);
close(out);
REQUIRE((size_t)len == region->length);
}
}
isc_nmhandle_detach(&readhandle);
kill(getpid(), SIGTERM);
}
static void
send_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
REQUIRE(handle != NULL);
REQUIRE(eresult == ISC_R_SUCCESS || eresult == ISC_R_CANCELED ||
eresult == ISC_R_EOF);
REQUIRE(cbarg == NULL);
}
static void
connect_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmhandle_t *readhandle = NULL;
REQUIRE(handle != NULL);
UNUSED(cbarg);
fprintf(stderr, "ECHO_CLIENT:%s:%s\n", __func__,
isc_result_totext(eresult));
if (eresult != ISC_R_SUCCESS) {
kill(getpid(), SIGTERM);
return;
}
isc_nmhandle_attach(handle, &readhandle);
isc_nm_read(handle, read_cb, readhandle);
isc_nm_send(handle, &message, send_cb, NULL);
}
static void
run(void) {
switch (protocol) {
case UDP:
isc_nm_udpconnect(netmgr, &sockaddr_local, &sockaddr_remote,
connect_cb, NULL, timeout);
break;
case TCP:
isc_nm_streamdnsconnect(netmgr, &sockaddr_local,
&sockaddr_remote, connect_cb, NULL,
timeout, NULL, NULL, NULL,
ISC_NM_PROXY_NONE, NULL);
break;
case DOT: {
isc_tlsctx_createclient(&tls_ctx);
isc_nm_streamdnsconnect(netmgr, &sockaddr_local,
&sockaddr_remote, connect_cb, NULL,
timeout, tls_ctx, NULL, NULL,
ISC_NM_PROXY_NONE, NULL);
break;
}
#if HAVE_LIBNGHTTP2
case HTTP_GET:
case HTTPS_GET:
case HTTPS_POST:
case HTTP_POST: {
bool is_https = (protocol == HTTPS_POST ||
protocol == HTTPS_GET);
bool is_post = (protocol == HTTPS_POST ||
protocol == HTTP_POST);
char req_url[256];
isc_nm_http_makeuri(is_https, &sockaddr_remote, NULL, 0,
ISC_NM_HTTP_DEFAULT_PATH, req_url,
sizeof(req_url));
if (is_https) {
isc_tlsctx_createclient(&tls_ctx);
}
isc_nm_httpconnect(netmgr, &sockaddr_local, &sockaddr_remote,
req_url, is_post, connect_cb, NULL, tls_ctx,
NULL, NULL, timeout, ISC_NM_PROXY_NONE,
NULL);
} break;
#endif
default:
UNREACHABLE();
}
waitforsignal();
}
int
main(int argc, char **argv) {
parse_options(argc, argv);
setup();
run();
teardown();
return EXIT_SUCCESS;
}