#include <signal.h>
#include <stdlib.h>
#include <string.h>
-#include <signal.h>
#include <getopt.h>
#include <libgen.h>
#include <uv.h>
#define CAN_FORK_EARLY 1
#endif
-/*
- * Globals
- */
-static bool g_quiet = false;
-static bool g_interactive = true;
+/* @internal Array of ip address shorthand. */
+typedef array_t(char*) addr_array_t;
+
+struct args {
+ int forks;
+ addr_array_t addr_set;
+ addr_array_t tls_set;
+ fd_array_t fd_set;
+ fd_array_t tls_fd_set;
+ char *keyfile;
+ int keyfile_unmanaged;
+ const char *moduledir;
+ const char *config;
+ int control_fd;
+ const char *rundir;
+ bool interactive;
+ bool quiet;
+ bool tty_binary_output;
+};
/* lua_pcall helper function */
static inline char *lua_strerror(int lua_err) {
*
* For parameters see http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb
*
- * - This is just basic read-eval-print; libedit is supported through krsec;
- * - stream->data represents a bool determining binary output mode (used by kresc);
- * - binary output: uint32_t length in network order, followed by that many bytes.
+ * - This is just basic read-eval-print; libedit is supported through kresc;
+ * - stream->data contains program arguments (struct args);
*/
static void tty_process_input(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
{
/* Set output streams */
FILE *out = stdout;
uv_os_fd_t stream_fd = 0;
+ struct args *args = stream->data;
if (uv_fileno((uv_handle_t *)stream, &stream_fd)) {
uv_close((uv_handle_t *)stream, (uv_close_cb) free);
free(cmd);
cmd[nread] = '\0';
}
- /* Pseudo-command for switching to "binary output";
- * beware: void* <-> bool */
- bool is_binary = stream->data;
+ /* Pseudo-command for switching to "binary output"; */
if (strcmp(cmd, "__binary") == 0) {
- stream->data = (void *)(is_binary = true);
+ args->tty_binary_output = true;
goto finish;
}
}
/* Simpler output in binary mode */
- if (is_binary) {
+ if (args->tty_binary_output) {
size_t len_s = strlen(message);
if (len_s > UINT32_MAX)
goto finish;
}
/* Log to remote socket if connected */
- const char *delim = g_quiet ? "" : "> ";
+ const char *delim = args->quiet ? "" : "> ";
if (stream_fd != STDIN_FILENO) {
fprintf(stdout, "%s\n", cmd); /* Duplicate command to logs */
if (message)
fprintf(out, "%s", message); /* Duplicate output to sender */
- if (message || !g_quiet)
+ if (message || !args->quiet)
fprintf(out, "\n");
fprintf(out, "%s", delim);
}
FILE *fp_out = ret ? stderr : stdout;
if (message)
fprintf(fp_out, "%s", message);
- if (message || !g_quiet)
+ if (message || !args->quiet)
fprintf(fp_out, "\n");
fprintf(fp_out, "%s", delim);
lua_settop(L, 0);
static void tty_accept(uv_stream_t *master, int status)
{
uv_tcp_t *client = malloc(sizeof(*client));
+ struct args *args = master->data;
if (client) {
uv_tcp_init(master->loop, client);
if (uv_accept(master, (uv_stream_t *)client) != 0) {
free(client);
return;
}
- client->data = 0;
+ client->data = args;
uv_read_start((uv_stream_t *)client, tty_alloc, tty_process_input);
/* Write command line */
- if (!g_quiet) {
+ if (!args->quiet) {
uv_buf_t buf = { "> ", 2 };
uv_try_write((uv_stream_t *)client, &buf, 1);
}
" [rundir] Path to the working directory (default: .)\n");
}
-static int run_worker(uv_loop_t *loop, struct engine *engine, fd_array_t *ipc_set, bool leader, int control_fd)
+static int run_worker(uv_loop_t *loop, struct engine *engine, fd_array_t *ipc_set, bool leader, struct args *args)
{
/* Control sockets or TTY */
auto_free char *sock_file = NULL;
uv_pipe_t pipe;
uv_pipe_init(loop, &pipe, 0);
- pipe.data = 0;
- if (g_interactive) {
- if (!g_quiet)
+ pipe.data = args;
+ if (args->interactive) {
+ if (!args->quiet)
printf("[system] interactive mode\n> ");
fflush(stdout);
uv_pipe_open(&pipe, 0);
uv_read_start((uv_stream_t*) &pipe, tty_alloc, tty_process_input);
} else {
int pipe_ret = -1;
- if (control_fd != -1) {
- pipe_ret = uv_pipe_open(&pipe, control_fd);
+ if (args->control_fd != -1) {
+ pipe_ret = uv_pipe_open(&pipe, args->control_fd);
} else {
(void) mkdir("tty", S_IRWXU|S_IRWXG);
sock_file = afmt("tty/%ld", getpid());
}
#endif
-static int init_keyfile(struct engine *engine, const char *keyfile, bool keyfile_unmanaged)
+static int set_keyfile(struct engine *engine, char *keyfile, bool unmanaged)
{
+ assert(keyfile != NULL);
auto_free char *cmd = afmt("trust_anchors.config('%s',%s)",
- keyfile, keyfile_unmanaged ? "true" : "nil");
+ keyfile, unmanaged ? "true" : "nil");
if (!cmd) {
kr_log_error("[system] not enough memory\n");
return kr_error(ENOMEM);
kr_log_error("[ ta ] keyfile '%s': failed to load (%s)\n",
keyfile, lua_strerror(lua_ret));
}
- return kr_error(EIO);
+ return lua_ret;
}
lua_settop(engine->L, 0);
- return 0;
+ return kr_ok();
}
-int main(int argc, char **argv)
+
+static void args_init(struct args *args)
{
- int forks = 1;
- array_t(char*) addr_set;
- array_t(char*) tls_set;
- array_init(addr_set);
- array_init(tls_set);
- array_t(int) fd_set;
- array_init(fd_set);
- array_t(int) tls_fd_set;
- array_init(tls_fd_set);
- char *keyfile = NULL;
- int keyfile_unmanaged = 0;
- char *moduledir = MODULEDIR;
- const char *config = NULL;
- int control_fd = -1;
+ memset(args, 0, sizeof(struct args));
+ args->forks = -1;
+ array_init(args->addr_set);
+ array_init(args->tls_set);
+ array_init(args->fd_set);
+ array_init(args->tls_fd_set);
+ args->moduledir = MODULEDIR;
+ args->control_fd = -1;
+ args->interactive = true;
+ args->quiet = false;
+}
+int parse_args(int argc, char **argv, struct args *args)
+{
/* Long options. */
- int c = 0, li = 0, ret = 0;
+ int c = 0, li = 0;
struct option opts[] = {
{"addr", required_argument, 0, 'a'},
{"tls", required_argument, 0, 't'},
switch (c)
{
case 'a':
- array_push(addr_set, optarg);
+ array_push(args->addr_set, optarg);
break;
case 't':
- array_push(tls_set, optarg);
+ array_push(args->tls_set, optarg);
break;
case 'S':
- array_push(fd_set, strtol(optarg, NULL, 10));
+ array_push(args->fd_set, strtol(optarg, NULL, 10));
break;
case 'T':
- array_push(tls_fd_set, strtol(optarg, NULL, 10));
+ array_push(args->tls_fd_set, strtol(optarg, NULL, 10));
break;
case 'c':
- config = optarg;
+ args->config = optarg;
break;
case 'f':
- g_interactive = false;
- forks = strtol(optarg, NULL, 10);
- if (forks <= 0) {
+ args->interactive = false;
+ args->forks = strtol(optarg, NULL, 10);
+ if (args->forks <= 0) {
kr_log_error("[system] error '-f' requires a positive"
" number, not '%s'\n", optarg);
return EXIT_FAILURE;
}
break;
case 'K':
- keyfile_unmanaged = 1;
+ args->keyfile_unmanaged = 1;
case 'k':
- if (keyfile != NULL) {
+ if (args->keyfile != NULL) {
kr_log_error("[system] error only one of '--keyfile' and '--keyfile-ro' allowed\n");
return EXIT_FAILURE;
}
- keyfile = optarg;
+ args->keyfile = optarg;
break;
case 'm':
- moduledir = optarg;
+ args->moduledir = optarg;
break;
case 'v':
kr_verbose_set(true);
#endif
break;
case 'q':
- g_quiet = true;
+ args->quiet = true;
break;
case 'V':
kr_log_info("%s, version %s\n", "Knot DNS Resolver", PACKAGE_VERSION);
help(argc, argv);
return EXIT_FAILURE;
}
+ }
+ if (optind < argc) {
+ args->rundir = argv[optind];
+ }
+ return EXIT_SUCCESS;
+}
+
+static int bind_fds(struct network *net, fd_array_t *fd_set, bool tls) {
+ int ret = 0;
+ for (size_t i = 0; i < fd_set->len; ++i) {
+ ret = network_listen_fd(net, fd_set->at[i], tls);
+ if (ret != 0) {
+ kr_log_error("[system] %slisten on fd=%d %s\n",
+ tls ? "TLS " : "", fd_set->at[i], kr_strerror(ret));
+ break;
+ }
+ }
+ return ret;
+}
+
+static int bind_sockets(struct network *net, addr_array_t *addr_set, bool tls) {
+ uint32_t flags = tls ? NET_TCP|NET_TLS : NET_UDP|NET_TCP;
+ int ret = 0;
+ for (size_t i = 0; i < addr_set->len; ++i) {
+ int port = tls ? KR_DNS_TLS_PORT : KR_DNS_PORT;
+ const char *addr = set_addr(addr_set->at[i], &port);
+ ret = network_listen(net, addr, (uint16_t)port, flags);
+ if (ret != 0) {
+ kr_log_error("[system] bind to '%s@%d' %s%s\n",
+ addr, port, tls ? "(TLS) " : "", kr_strerror(ret));
+ break;
+ }
+ }
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int ret = 0;
+ struct args args;
+ args_init(&args);
+ if ((ret = parse_args(argc, argv, &args)) != EXIT_SUCCESS) {
+ return ret;
}
#ifdef HAS_SYSTEMD
for (int i = 0; i < sd_nsocks; ++i) {
int fd = SD_LISTEN_FDS_START + i;
/* when run under systemd supervision, do not use interactive mode */
- g_interactive = false;
- if (forks != 1) {
+ args.interactive = false;
+ if (args.forks != 1) {
kr_log_error("[system] when run under systemd-style supervision, "
- "use single-process only (bad: --fork=%d).\n", forks);
+ "use single-process only (bad: --fork=%d).\n", args.forks);
free_sd_socket_names(socket_names, sd_nsocks);
return EXIT_FAILURE;
}
if (!strcasecmp("control",socket_names[i])) {
- control_fd = fd;
+ args.control_fd = fd;
} else if (!strcasecmp("tls",socket_names[i])) {
- array_push(tls_fd_set, fd);
+ array_push(args.tls_fd_set, fd);
} else {
- array_push(fd_set, fd);
+ array_push(args.fd_set, fd);
}
}
free_sd_socket_names(socket_names, sd_nsocks);
#endif
/* Switch to rundir. */
- if (optind < argc) {
- const char *rundir = argv[optind];
+ if (args.rundir != NULL) {
/* FIXME: access isn't a good way if we start as root and drop privileges later */
- if (access(rundir, W_OK) != 0) {
- kr_log_error("[system] rundir '%s': %s\n", rundir, strerror(errno));
+ if (access(args.rundir, W_OK) != 0) {
+ kr_log_error("[system] rundir '%s': %s\n", args.rundir, strerror(errno));
return EXIT_FAILURE;
}
- ret = chdir(rundir);
+ ret = chdir(args.rundir);
if (ret != 0) {
- kr_log_error("[system] rundir '%s': %s\n", rundir, strerror(errno));
+ kr_log_error("[system] rundir '%s': %s\n", args.rundir, strerror(errno));
return EXIT_FAILURE;
}
}
- if (config && strcmp(config, "-") != 0 && access(config, R_OK) != 0) {
- kr_log_error("[system] config '%s': %s\n", config, strerror(errno));
+ if (args.config && strcmp(args.config, "-") != 0 && access(args.config, R_OK) != 0) {
+ kr_log_error("[system] config '%s': %s\n", args.config, strerror(errno));
return EXIT_FAILURE;
}
- if (!config && access("config", R_OK) == 0) {
- config = "config";
+ if (!args.config && access("config", R_OK) == 0) {
+ args.config = "config";
}
#ifndef CAN_FORK_EARLY
fd_array_t ipc_set;
array_init(ipc_set);
/* Fork subprocesses if requested */
- int fork_id = fork_workers(&ipc_set, forks);
+ int fork_id = fork_workers(&ipc_set, args.forks);
if (fork_id < 0) {
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}
/* Create worker */
- struct worker_ctx *worker = worker_create(&engine, &pool, fork_id, forks);
+ struct worker_ctx *worker = worker_create(&engine, &pool, fork_id, args.forks);
if (!worker) {
kr_log_error("[system] not enough memory\n");
return EXIT_FAILURE;
}
- /* Bind to passed fds and run */
- for (size_t i = 0; i < fd_set.len; ++i) {
- ret = network_listen_fd(&engine.net, fd_set.at[i], false);
- if (ret != 0) {
- kr_log_error("[system] listen on fd=%d %s\n", fd_set.at[i], kr_strerror(ret));
- ret = EXIT_FAILURE;
- break;
- }
- }
- /* Do the same for TLS */
- for (size_t i = 0; i < tls_fd_set.len; ++i) {
- ret = network_listen_fd(&engine.net, tls_fd_set.at[i], true);
- if (ret != 0) {
- kr_log_error("[system] TLS listen on fd=%d %s\n", tls_fd_set.at[i], kr_strerror(ret));
- ret = EXIT_FAILURE;
- break;
- }
- }
- /* Bind to sockets and run */
- if (ret == 0) {
- for (size_t i = 0; i < addr_set.len; ++i) {
- int port = 53;
- const char *addr = set_addr(addr_set.at[i], &port);
- ret = network_listen(&engine.net, addr, (uint16_t)port, NET_UDP|NET_TCP);
- if (ret != 0) {
- kr_log_error("[system] bind to '%s@%d' %s\n", addr, port, kr_strerror(ret));
- ret = EXIT_FAILURE;
- break;
- }
- }
- }
- /* Bind to TLS sockets */
- if (ret == 0) {
- for (size_t i = 0; i < tls_set.len; ++i) {
- int port = KR_DNS_TLS_PORT;
- const char *addr = set_addr(tls_set.at[i], &port);
- ret = network_listen(&engine.net, addr, (uint16_t)port, NET_TCP|NET_TLS);
- if (ret != 0) {
- kr_log_error("[system] bind to '%s@%d' (TLS) %s\n", addr, port, kr_strerror(ret));
- ret = EXIT_FAILURE;
- break;
- }
- }
+
+ /* Bind to passed fds and sockets*/
+ if (bind_fds(&engine.net, &args.fd_set, false) != 0 ||
+ bind_fds(&engine.net, &args.tls_fd_set, true) != 0 ||
+ bind_sockets(&engine.net, &args.addr_set, false) != 0 ||
+ bind_sockets(&engine.net, &args.tls_set, true) != 0
+ ) {
+ ret = EXIT_FAILURE;
+ goto cleanup;
}
/* Workaround for https://github.com/libuv/libuv/issues/45
goto cleanup;
}
- engine_set_moduledir(&engine, moduledir);
+ engine_set_moduledir(&engine, args.moduledir);
/* Block signals. */
uv_loop_t *loop = uv_default_loop();
worker->loop = loop;
loop->data = worker;
- ret = engine_load_sandbox(&engine);
- if (ret == 0 && config != NULL && strcmp(config, "-") !=0) {
- ret = engine_loadconf(&engine, config);
- }
-
- if (ret != 0) {
+ if (engine_load_sandbox(&engine) != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
-
- if (keyfile) {
- ret = init_keyfile(&engine, keyfile, keyfile_unmanaged);
- if (ret != 0) {
- kr_log_error("[system] failed to initialized keyfile: %s\n", kr_strerror(ret));
+ if (args.config != NULL && strcmp(args.config, "-") != 0) {
+ if(engine_loadconf(&engine, args.config) != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
+ lua_settop(engine.L, 0);
}
-
- if (config == NULL || strcmp(config, "-") !=0) {
- ret = engine_load_defaults(&engine);
- }
-
- if (ret != 0) {
+ if (args.keyfile != NULL && set_keyfile(&engine, args.keyfile, args.keyfile_unmanaged) != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
-
- ret = engine_start(&engine);
- if (ret != 0) {
+ if (args.config == NULL || strcmp(args.config, "-") !=0) {
+ if(engine_load_defaults(&engine) != 0) {
+ ret = EXIT_FAILURE;
+ goto cleanup;
+ }
+ }
+ if (engine_start(&engine) != 0) {
ret = EXIT_FAILURE;
goto cleanup;
}
/* Run the event loop */
- ret = run_worker(loop, &engine, &ipc_set, fork_id == 0, control_fd);
+ ret = run_worker(loop, &engine, &ipc_set, fork_id == 0, &args);
if (ret != 0) {
perror("[system] worker failed");
ret = EXIT_FAILURE;
engine_deinit(&engine);
worker_reclaim(worker);
mp_delete(pool.ctx);
- array_clear(addr_set);
- array_clear(tls_set);
+ array_clear(args.addr_set);
+ array_clear(args.tls_set);
kr_crypto_cleanup();
return ret;
}