2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief DNS services via libunbound.
22 * @copyright 2013 The FreeRADIUS server project
23 * @copyright 2013 Brian S. Julin <bjulin@clarku.edu>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/log.h>
33 typedef struct rlm_unbound_t {
34 struct ub_ctx *ub; /* This must come first. Do not move */
35 fr_event_list_t *el; /* This must come second. Do not move. */
38 char const *xlat_a_name;
39 char const *xlat_aaaa_name;
40 char const *xlat_ptr_name;
50 FILE *log_pipe_stream[2];
55 * A mapping of configuration file names to internal variables.
57 static const CONF_PARSER module_config[] = {
58 { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_unbound_t, filename), "${modconfdir}/unbound/default.conf" },
59 { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_unbound_t, timeout), "3000" },
60 CONF_PARSER_TERMINATOR
64 * Callback sent to libunbound for xlat functions. Simply links the
65 * new ub_result via a pointer that has been allocated from the heap.
66 * This pointer has been pre-initialized to a magic value.
68 static void link_ubres(void* my_arg, int err, struct ub_result* result)
70 struct ub_result **ubres = (struct ub_result **)my_arg;
73 * Note that while result will be NULL on error, we are explicit
74 * here because that is actually a behavior that is suboptimal
75 * and only documented in the examples. It could change.
78 ERROR("rlm_unbound: %s", ub_strerror(err));
86 * Convert labels as found in a DNS result to a NULL terminated string.
88 * Result is written to memory pointed to by "out" but no result will
89 * be written unless it and its terminating NULL character fit in "left"
90 * bytes. Returns the number of bytes written excluding the terminating
91 * NULL, or -1 if nothing was written because it would not fit or due
92 * to a violation in the labels format.
94 static int rrlabels_tostr(char *out, char *rr, size_t left)
99 * TODO: verify that unbound results (will) always use this label
100 * format, and review the specs on this label format for nuances.
107 left = 253; /* DNS length limit */
109 /* As a whole this should be "NULL terminated" by the 0-length label */
110 if (strnlen(rr, left) > left - 1) {
114 /* It will fit, but does it it look well formed? */
118 count = *((unsigned char *)(rr + offset));
122 if (count > 63 || strlen(rr + offset) < count) {
128 /* Data is valid and fits. Copy it. */
133 count = *((unsigned char *)(rr));
137 *(out + offset) = '.';
142 memcpy(out + offset, rr, count);
147 *(out + offset) = '\0';
151 static int ub_common_wait(rlm_unbound_t *inst, REQUEST *request, char const *tag, struct ub_result **ub, int async_id)
153 useconds_t iv, waited;
155 iv = inst->timeout > 64 ? 64000 : inst->timeout * 1000;
156 ub_process(inst->ub);
158 for (waited = 0; (void*)*ub == (void *)inst; waited += iv, iv *= 2) {
160 if (waited + iv > (useconds_t)inst->timeout * 1000) {
161 usleep(inst->timeout * 1000 - waited);
162 ub_process(inst->ub);
168 /* Check if already handled by event loop */
169 if ((void *)*ub != (void *)inst) {
173 /* In case we are running single threaded */
174 ub_process(inst->ub);
177 if ((void *)*ub == (void *)inst) {
180 RDEBUG("rlm_unbound (%s): DNS took too long", tag);
182 res = ub_cancel(inst->ub, async_id);
184 REDEBUG("rlm_unbound (%s): ub_cancel: %s",
185 tag, ub_strerror(res));
193 static int ub_common_fail(REQUEST *request, char const *tag, struct ub_result *ub)
196 RWDEBUG("rlm_unbound (%s): Bogus DNS response", tag);
201 RDEBUG("rlm_unbound (%s): NXDOMAIN", tag);
206 RDEBUG("rlm_unbound (%s): empty result", tag);
213 static ssize_t xlat_a(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
215 rlm_unbound_t *inst = instance;
216 struct ub_result **ubres;
218 char *fmt2; /* For const warnings. Keep till new libunbound ships. */
220 /* This has to be on the heap, because threads. */
221 ubres = talloc(inst, struct ub_result *);
223 /* Used and thus impossible value from heap to designate incomplete */
224 *ubres = (void *)instance;
226 fmt2 = talloc_typed_strdup(inst, fmt);
227 ub_resolve_async(inst->ub, fmt2, 1, 1, ubres, link_ubres, &async_id);
230 if (ub_common_wait(inst, request, inst->xlat_a_name, ubres, async_id)) {
235 if (ub_common_fail(request, inst->xlat_a_name, *ubres)) {
239 if (!inet_ntop(AF_INET, (*ubres)->data[0], out, freespace)) {
243 ub_resolve_free(*ubres);
248 RWDEBUG("rlm_unbound (%s): no result", inst->xlat_a_name);
251 ub_resolve_free(*ubres); /* Handles NULL gracefully */
258 static ssize_t xlat_aaaa(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
260 rlm_unbound_t *inst = instance;
261 struct ub_result **ubres;
263 char *fmt2; /* For const warnings. Keep till new libunbound ships. */
265 /* This has to be on the heap, because threads. */
266 ubres = talloc(inst, struct ub_result *);
268 /* Used and thus impossible value from heap to designate incomplete */
269 *ubres = (void *)instance;
271 fmt2 = talloc_typed_strdup(inst, fmt);
272 ub_resolve_async(inst->ub, fmt2, 28, 1, ubres, link_ubres, &async_id);
275 if (ub_common_wait(inst, request, inst->xlat_aaaa_name, ubres, async_id)) {
280 if (ub_common_fail(request, inst->xlat_aaaa_name, *ubres)) {
283 if (!inet_ntop(AF_INET6, (*ubres)->data[0], out, freespace)) {
286 ub_resolve_free(*ubres);
291 RWDEBUG("rlm_unbound (%s): no result", inst->xlat_aaaa_name);
294 ub_resolve_free(*ubres); /* Handles NULL gracefully */
301 static ssize_t xlat_ptr(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
303 rlm_unbound_t *inst = instance;
304 struct ub_result **ubres;
306 char *fmt2; /* For const warnings. Keep till new libunbound ships. */
308 /* This has to be on the heap, because threads. */
309 ubres = talloc(inst, struct ub_result *);
311 /* Used and thus impossible value from heap to designate incomplete */
312 *ubres = (void *)instance;
314 fmt2 = talloc_typed_strdup(inst, fmt);
315 ub_resolve_async(inst->ub, fmt2, 12, 1, ubres, link_ubres, &async_id);
318 if (ub_common_wait(inst, request, inst->xlat_ptr_name,
324 if (ub_common_fail(request, inst->xlat_ptr_name, *ubres)) {
327 if (rrlabels_tostr(out, (*ubres)->data[0], freespace) < 0) {
330 ub_resolve_free(*ubres);
335 RWDEBUG("rlm_unbound (%s): no result", inst->xlat_ptr_name);
338 ub_resolve_free(*ubres); /* Handles NULL gracefully */
346 * Even when run in asyncronous mode, callbacks sent to libunbound still
347 * must be run in an application-side thread (via ub_process.) This is
348 * probably to keep the API usage consistent across threaded and forked
349 * embedded client modes. This callback function lets an event loop call
350 * ub_process when the instance's file descriptor becomes ready.
352 static void ub_fd_handler(UNUSED fr_event_list_t *el, UNUSED int sock, void *ctx)
354 rlm_unbound_t *inst = ctx;
357 err = ub_process(inst->ub);
359 ERROR("rlm_unbound (%s) async ub_process: %s",
360 inst->name, ub_strerror(err));
364 #ifndef HAVE_PTHREAD_H
366 /* If we have to use a pipe to redirect logging, this does the work. */
367 static void log_spew(UNUSED fr_event_list_t *el, UNUSED int sock, void *ctx)
369 rlm_unbound_t *inst = ctx;
373 * This works for pipes from processes, but not from threads
374 * right now. The latter is hinky and will require some fancy
375 * blocking/nonblocking trickery which is not figured out yet,
376 * since selecting on a pipe from a thread in the same process
377 * seems to behave differently. It will likely preclude the use
378 * of fgets and streams. Left for now since some unbound logging
379 * infrastructure is still global across multiple contexts. Maybe
380 * we can get unbound folks to provide a ub_ctx_debugout_async that
381 * takes a function hook instead to just bypass the piping when
382 * used in threaded mode.
384 while (fgets(line, 1024, inst->log_pipe_stream[0])) {
385 DEBUG("rlm_unbound (%s): %s", inst->name, line);
391 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
393 rlm_unbound_t *inst = instance;
395 inst->name = cf_section_name2(conf);
397 inst->name = cf_section_name1(conf);
400 if (inst->timeout > 10000) {
401 cf_log_err_cs(conf, "timeout must be 0 to 10000");
405 MEM(inst->xlat_a_name = talloc_typed_asprintf(inst, "%s-a", inst->name));
406 MEM(inst->xlat_aaaa_name = talloc_typed_asprintf(inst, "%s-aaaa", inst->name));
407 MEM(inst->xlat_ptr_name = talloc_typed_asprintf(inst, "%s-ptr", inst->name));
409 if (xlat_register(inst->xlat_a_name, xlat_a, NULL, inst) ||
410 xlat_register(inst->xlat_aaaa_name, xlat_aaaa, NULL, inst) ||
411 xlat_register(inst->xlat_ptr_name, xlat_ptr, NULL, inst)) {
412 cf_log_err_cs(conf, "Failed registering xlats");
419 static int mod_instantiate(CONF_SECTION *conf, void *instance)
421 rlm_unbound_t *inst = instance;
429 char k[64]; /* To silence const warns until newer unbound in distros */
431 inst->el = radius_event_list_corral(EVENT_CORRAL_AUX);
432 inst->log_pipe_stream[0] = NULL;
433 inst->log_pipe_stream[1] = NULL;
435 inst->log_pipe_in_use = false;
437 inst->ub = ub_ctx_create();
439 cf_log_err_cs(conf, "ub_ctx_create failed");
443 #ifdef HAVE_PTHREAD_H
445 * Note unbound threads WILL happen with -s option, if it matters.
446 * We cannot tell from here whether that option is in effect.
448 res = ub_ctx_async(inst->ub, 1);
451 * Uses forked subprocesses instead.
453 res = ub_ctx_async(inst->ub, 0);
458 /* Glean some default settings to match the main server. */
459 /* TODO: debug_level can be changed at runtime. */
460 /* TODO: log until fork when stdout or stderr and !rad_debug_lvl. */
463 if (rad_debug_lvl > 0) {
464 log_level = rad_debug_lvl;
466 } else if (main_config.debug_level > 0) {
467 log_level = main_config.debug_level;
471 /* TODO: This will need some tweaking */
482 log_level = 2; /* mid-to-heavy levels of output */
489 log_level = 3; /* Pretty crazy amounts of output */
493 log_level = 4; /* Insane amounts of output including crypts */
497 res = ub_ctx_debuglevel(inst->ub, log_level);
500 switch (default_log.dst) {
502 if (!rad_debug_lvl) {
503 log_dst = L_DST_NULL;
506 log_dst = L_DST_STDOUT;
507 log_fd = dup(STDOUT_FILENO);
511 if (!rad_debug_lvl) {
512 log_dst = L_DST_NULL;
515 log_dst = L_DST_STDOUT;
516 log_fd = dup(STDERR_FILENO);
520 if (main_config.log_file) {
523 strcpy(k, "logfile:");
524 /* 3rd argument isn't const'd in libunbounds API */
525 memcpy(&log_file, &main_config.log_file, sizeof(log_file));
526 res = ub_ctx_set_option(inst->ub, k, log_file);
530 log_dst = L_DST_FILES;
536 log_dst = L_DST_NULL;
540 log_dst = L_DST_SYSLOG;
544 /* Now load the config file, which can override gleaned settings. */
548 memcpy(&file, &inst->filename, sizeof(file));
549 res = ub_ctx_config(inst->ub, file);
554 * Check if the config file tried to use syslog. Unbound
555 * does not share syslog gracefully.
557 strcpy(k, "use-syslog");
558 res = ub_ctx_get_option(inst->ub, k, &optval);
559 if (res || !optval) goto error;
561 if (!strcmp(optval, "yes")) {
566 WARN("rlm_unbound (%s): Overriding syslog settings.", inst->name);
567 strcpy(k, "use-syslog:");
569 res = ub_ctx_set_option(inst->ub, k, v);
572 if (log_dst == L_DST_FILES) {
575 /* Reinstate the log file name JIC */
576 strcpy(k, "logfile:");
577 /* 3rd argument isn't const'd in libunbounds API */
578 memcpy(&log_file, &main_config.log_file, sizeof(log_file));
579 res = ub_ctx_set_option(inst->ub, k, log_file);
584 if (optval) free(optval);
585 strcpy(k, "logfile");
587 res = ub_ctx_get_option(inst->ub, k, &optval);
590 if (optval && strlen(optval)) {
591 log_dst = L_DST_FILES;
593 } else if (!rad_debug_lvl) {
594 log_dst = L_DST_NULL;
597 if (optval) free(optval);
603 * We have an fd to log to. And we've already attempted to
604 * dup it so libunbound doesn't close it on us.
607 cf_log_err_cs(conf, "Could not dup fd");
611 inst->log_stream = fdopen(log_fd, "w");
612 if (!inst->log_stream) {
613 cf_log_err_cs(conf, "error setting up log stream");
617 res = ub_ctx_debugout(inst->ub, inst->log_stream);
622 /* We gave libunbound a filename. It is on its own now. */
626 /* We tell libunbound not to log at all. */
627 res = ub_ctx_debugout(inst->ub, NULL);
632 #ifdef HAVE_PTHREAD_H
634 * Currently this wreaks havoc when running threaded, so just
635 * turn logging off until that gets figured out.
637 res = ub_ctx_debugout(inst->ub, NULL);
642 * We need to create a pipe, because libunbound does not
643 * share syslog nicely. Or the core added some new logsink.
645 if (pipe(inst->log_pipe)) {
647 cf_log_err_cs(conf, "Error setting up log pipes");
651 if ((fcntl(inst->log_pipe[0], F_SETFL, O_NONBLOCK) < 0) ||
652 (fcntl(inst->log_pipe[0], F_SETFD, FD_CLOEXEC) < 0)) {
656 /* Opaque to us when this can be closed, so we do not. */
657 if (fcntl(inst->log_pipe[1], F_SETFL, O_NONBLOCK) < 0) {
661 inst->log_pipe_stream[0] = fdopen(inst->log_pipe[0], "r");
662 inst->log_pipe_stream[1] = fdopen(inst->log_pipe[1], "w");
664 if (!inst->log_pipe_stream[0] || !inst->log_pipe_stream[1]) {
665 if (!inst->log_pipe_stream[1]) {
666 close(inst->log_pipe[1]);
669 if (!inst->log_pipe_stream[0]) {
670 close(inst->log_pipe[0]);
672 cf_log_err_cs(conf, "Error setting up log stream");
676 res = ub_ctx_debugout(inst->ub, inst->log_pipe_stream[1]);
679 if (!fr_event_fd_insert(inst->el, 0, inst->log_pipe[0], log_spew, inst)) {
680 cf_log_err_cs(conf, "could not insert log fd");
684 inst->log_pipe_in_use = true;
691 * Now we need to finalize the context.
693 * There's no clean API to just finalize the context made public
694 * in libunbound. But we can trick it by trying to delete data
695 * which as it happens fails quickly and quietly even though the
696 * data did not exist.
698 strcpy(k, "notar33lsite.foo123.nottld A 127.0.0.1");
699 ub_ctx_data_remove(inst->ub, k);
701 inst->log_fd = ub_fd(inst->ub);
702 if (inst->log_fd >= 0) {
703 if (!fr_event_fd_insert(inst->el, 0, inst->log_fd, ub_fd_handler, inst)) {
704 cf_log_err_cs(conf, "could not insert async fd");
714 cf_log_err_cs(conf, "%s", ub_strerror(res));
717 if (log_fd > -1) close(log_fd);
722 static int mod_detach(UNUSED void *instance)
724 rlm_unbound_t *inst = instance;
726 if (inst->log_fd >= 0) {
727 fr_event_fd_delete(inst->el, 0, inst->log_fd);
729 ub_process(inst->ub);
730 /* This can hang/leave zombies currently
731 * see upstream bug #519
732 * ...so expect valgrind to complain with -m
735 ub_ctx_delete(inst->ub);
740 if (inst->log_pipe_stream[1]) {
741 fclose(inst->log_pipe_stream[1]);
744 if (inst->log_pipe_stream[0]) {
745 if (inst->log_pipe_in_use) {
746 fr_event_fd_delete(inst->el, 0, inst->log_pipe[0]);
748 fclose(inst->log_pipe_stream[0]);
751 if (inst->log_stream) {
752 fclose(inst->log_stream);
758 extern module_t rlm_unbound;
759 module_t rlm_unbound = {
760 .magic = RLM_MODULE_INIT,
762 .type = RLM_TYPE_THREAD_SAFE,
763 .inst_size = sizeof(rlm_unbound_t),
764 .config = module_config,
765 .bootstrap = mod_bootstrap,
766 .instantiate = mod_instantiate,