Previously, netmgr, taskmgr, timermgr and socketmgr all had their own isc_<*>mgr_create() and isc_<*>mgr_destroy() functions. The new isc_managers_create() and isc_managers_destroy() fold all four into a single function and makes sure the objects are created and destroy in correct order. Especially now, when taskmgr runs on top of netmgr, the correct order is important and when the code was duplicated at many places it's easy to make mistake. The former isc_<*>mgr_create() and isc_<*>mgr_destroy() functions were made private and a single call to isc_managers_create() and isc_managers_destroy() is required at the program startup / shutdown.
322 lines
7.1 KiB
C
322 lines
7.1 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* 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>
|
|
|
|
#define DEFAULT_DOH_PATH "/dns-query"
|
|
|
|
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_nm_t *netmgr = NULL;
|
|
|
|
static protocol_t protocol;
|
|
static in_port_t port;
|
|
static isc_netaddr_t netaddr;
|
|
static isc_sockaddr_t sockaddr __attribute__((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:
|
|
INSIST(0);
|
|
}
|
|
}
|
|
|
|
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
|
|
_signal(int sig, void (*handler)(int)) {
|
|
struct sigaction sa = { .sa_handler = handler };
|
|
|
|
RUNTIME_CHECK(sigfillset(&sa.sa_mask) == 0);
|
|
RUNTIME_CHECK(sigaction(sig, &sa, NULL) >= 0);
|
|
}
|
|
|
|
static void
|
|
setup(void) {
|
|
sigset_t sset;
|
|
|
|
_signal(SIGPIPE, SIG_IGN);
|
|
_signal(SIGHUP, SIG_DFL);
|
|
_signal(SIGTERM, SIG_DFL);
|
|
_signal(SIGINT, SIG_DFL);
|
|
|
|
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(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0);
|
|
|
|
isc_mem_create(&mctx);
|
|
|
|
isc_managers_create(mctx, workers, 0, 0, &netmgr, NULL, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
teardown(void) {
|
|
isc_managers_destroy(&netmgr, NULL, NULL, NULL);
|
|
isc_mem_destroy(&mctx);
|
|
if (tls_ctx) {
|
|
isc_tlsctx_free(&tls_ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
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_nmiface_t *)&sockaddr,
|
|
read_cb, NULL, 0, &sock);
|
|
break;
|
|
case TCP:
|
|
result = isc_nm_listentcpdns(netmgr, (isc_nmiface_t *)&sockaddr,
|
|
read_cb, NULL, accept_cb, NULL, 0,
|
|
0, NULL, &sock);
|
|
break;
|
|
case DOT: {
|
|
isc_tlsctx_createserver(NULL, NULL, &tls_ctx);
|
|
|
|
result = isc_nm_listentlsdns(netmgr, (isc_nmiface_t *)&sockaddr,
|
|
read_cb, NULL, accept_cb, NULL, 0,
|
|
0, NULL, tls_ctx, &sock);
|
|
break;
|
|
}
|
|
case HTTPS:
|
|
case HTTP: {
|
|
bool is_https = protocol == HTTPS;
|
|
if (is_https) {
|
|
isc_tlsctx_createserver(NULL, NULL, &tls_ctx);
|
|
}
|
|
result = isc_nm_listenhttp(netmgr, (isc_nmiface_t *)&sockaddr,
|
|
0, NULL, tls_ctx, &sock);
|
|
if (result == ISC_R_SUCCESS) {
|
|
result = isc_nm_http_endpoint(sock, DEFAULT_DOH_PATH,
|
|
read_cb, NULL, 0);
|
|
}
|
|
} break;
|
|
default:
|
|
INSIST(0);
|
|
ISC_UNREACHABLE();
|
|
}
|
|
REQUIRE(result == ISC_R_SUCCESS);
|
|
|
|
yield();
|
|
|
|
isc_nm_stoplistening(sock);
|
|
isc_nmsocket_close(&sock);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv) {
|
|
parse_options(argc, argv);
|
|
|
|
setup();
|
|
|
|
run();
|
|
|
|
teardown();
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|