From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms13.migadu.com with LMTPS id gA3NNkrvfmcsSwAAe85BDQ:P1 (envelope-from ) for ; Wed, 08 Jan 2025 21:34:03 +0000 Received: from aspmx1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2.migadu.com with LMTPS id gA3NNkrvfmcsSwAAe85BDQ (envelope-from ) for ; Wed, 08 Jan 2025 22:34:03 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=mfRfYVt9; dkim=fail ("body hash did not verify") header.d=soeren-tempel.net header.s=opensmtpd header.b=WAXQG3RK; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed)" header.from=soeren-tempel.net (policy=none) ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1736372042; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding:resent-cc: resent-from:resent-sender:resent-message-id:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=qXVcl9wWy8rPYIdwJMLUOAQEsArjG8hFL2avJ3Q1/3g=; b=qChqCl6IlaIwF1qoV3ZPpOwBJPtJw/L3wnRqISVgoWhVQZ5Y0Kr+a9+H6lph56B914D3/7 u0WajY9k34m9B3FBY6en8ir79R+rsWNjusXZ4EE2tYMCr0Exp4MObzMqRvKCcAbEarWWu7 kamJGuqctTmuSBSIDj4b/aWv5C2la7uMkUtoRPFrlEkDs262GcRlvuPsBmDuf3H0o/BRrk wjUCATx0I4cj18K/Ghk0ZukqVliH2r46R45JoPbvOam/7pvlvZCx+wxeMhCmYFPTTRuttA T8LmUhUHfE/iJ/Yk1TZTbgX2g7IN+VTDAk4bEnDBzK2GDJddfaDHgevDe7ld1A== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=fail ("body hash did not verify") header.d=debbugs.gnu.org header.s=debbugs-gnu-org header.b=mfRfYVt9; dkim=fail ("body hash did not verify") header.d=soeren-tempel.net header.s=opensmtpd header.b=WAXQG3RK; spf=pass (aspmx1.migadu.com: domain of "guix-patches-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="guix-patches-bounces+larch=yhetil.org@gnu.org"; dmarc=fail reason="SPF not aligned (relaxed)" header.from=soeren-tempel.net (policy=none) ARC-Seal: i=1; s=key1; d=yhetil.org; t=1736372042; a=rsa-sha256; cv=none; b=p2+iX8KgOKan7MRP1WeJRXcQDOrMUwiK7PO4c1dSIfcg05uvOdQw5FLymXbiUBhQKmo7c1 5D2WxHTzjOXKh9kMs3cifd98sJy/G+abInRGIPO0Jenb2aIn/tz27O4KrIuvAax3MiKyhl B41NG87iTeM5x9ceZT0pNZHMl0PN3IJAkLLpjc1kjNK/08g2zsEHlFl4M/0PwupM6Cu7+1 4aVyCCSFSQtJxa1fcmnhf8Ztk9GoDMd3BYM101pGAfyX0e/IsWlsvc5ZKl+2+KKft0xgOS iC3gMw+nFQPPfUSYeDJ3nquKRWjApOkXS7FLjKH8tAwyFESi+J96DVRg/q72Jw== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 4D7191B734 for ; Wed, 08 Jan 2025 22:34:02 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tVdOJ-0008Dj-Tr; Wed, 08 Jan 2025 16:15:03 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tVdOI-0008DU-HO for guix-patches@gnu.org; Wed, 08 Jan 2025 16:15:02 -0500 Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1tVdOI-0002Dz-6S for guix-patches@gnu.org; Wed, 08 Jan 2025 16:15:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=debbugs.gnu.org; s=debbugs-gnu-org; h=MIME-Version:Date:From:To:In-Reply-To:References:Subject; bh=iKrPZUKEaz93D5/EmbRVHzNGFsuG+XEutgdHidWvG+Q=; b=mfRfYVt9nsfCn5XBlhryt5BwboVu5nLG5NTZpadg5t8ePlDdzS7PrVu1VRWIx94KUQUcURqixhSCElwhy0UvzpK2c0PlHpwoz93OKwfZ1A508cuA6aXQgJBOR5+4dxxEqjG7V6QsZM6zH0mZTWInO6JdG2ZMMWbLm0a68VwDZ4L32gsFFpmxJ6hBOac7tLgAfcNLC+LmvT2oOOD4vfN+d5nTlNv2AP/hd2wW/tjQhf7OezCtpEDND2GxgAdmJFrz0+4qUE4Dg6pIoAUowOzPCbp/GOzOYp+bM5ss4+u0wyMgEpmjVdV2K7t0/zwtiCmAkJzbgwgB+wwOUGcxmnUZ3w==; Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1tVdOI-0003lo-2K for guix-patches@gnu.org; Wed, 08 Jan 2025 16:15:02 -0500 X-Loop: help-debbugs@gnu.org Subject: [bug#68757] [PATCH v3 1/1] services: dns: Add unbound service References: <20240127121040.7156-2-soeren@soeren-tempel.net> In-Reply-To: <20240127121040.7156-2-soeren@soeren-tempel.net> Resent-From: soeren@soeren-tempel.net Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Wed, 08 Jan 2025 21:15:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 68757 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 68757@debbugs.gnu.org Cc: ludo@gnu.org Received: via spool by 68757-submit@debbugs.gnu.org id=B68757.173637087614436 (code B ref 68757); Wed, 08 Jan 2025 21:15:02 +0000 Received: (at 68757) by debbugs.gnu.org; 8 Jan 2025 21:14:36 +0000 Received: from localhost ([127.0.0.1]:49231 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1tVdNq-0003kk-TV for submit@debbugs.gnu.org; Wed, 08 Jan 2025 16:14:35 -0500 Received: from magnesium.8pit.net ([2001:19f0:6c01:4ae:5400:ff:fe66:af9d]:7644) by debbugs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1tVdNn-0003kW-8J; Wed, 08 Jan 2025 16:14:33 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; s=opensmtpd; bh=iKrPZUKE az93D5/EmbRVHzNGFsuG+XEutgdHidWvG+Q=; h=date:subject:cc:to:from; d=soeren-tempel.net; b=WAXQG3RKRzwB71xB0NXM1OLLNbXFb9c9F+0BDKehLfaOzvn RXsQrr7XH7RygkbJCC60iaumwKllw9IttKDK/RuSss5KmoDcs3OjINk65ew1H1b/cC0LM4 fAykYMniGPeta9XFkHHD/8GANnppFToS21gGsoSs8KUauiErwXkLP8= Received: from localhost ( [2a02:560:4d3d:df00:27ca:6ad:4496:a67a]) by magnesium.8pit.net (OpenSMTPD) with ESMTPSA id 142249df (TLSv1.3:TLS_AES_256_GCM_SHA384:256:YES); Wed, 8 Jan 2025 22:14:25 +0100 (CET) From: soeren@soeren-tempel.net Date: Wed, 8 Jan 2025 22:13:54 +0100 Message-ID: <20250108211416.27602-1-soeren@soeren-tempel.net> X-Mailer: git-send-email 2.47.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: guix-patches-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Scanner: mx12.migadu.com X-Migadu-Spam-Score: 6.82 X-Spam-Score: 6.82 X-Migadu-Queue-Id: 4D7191B734 X-TUID: b1j56OP6Q6x9 From: Sören Tempel This allows using Unbound as a local DNSSEC-enabled resolver. This commit also allows configuration of the Unbound DNS resolver via a Scheme API. The API currently provides very common options and includes an escape hatch to enable less common configurations. * gnu/service/dns.scm (unbound-serialize-field): New procedure. * gnu/service/dns.scm (unbound-serialize-alist): New procedure. * gnu/service/dns.scm (unbound-serialize-section): New procedure. * gnu/service/dns.scm (unbound-serialize-string): New procedure. * gnu/service/dns.scm (unbound-serialize-boolean): New procedure. * gnu/service/dns.scm (unbound-serialize-list-of-strings): New procedure. * gnu/service/dns.scm (unbound-zone): New record. * gnu/service/dns.scm (unbound-serialize-unbound-zone): New procedure. * gnu/service/dns.scm (unbound-serialize-list-of-unbound-zone): New procedure. * gnu/service/dns.scm (unbound-remote): New record. * gnu/service/dns.scm (unbound-serialize-unbound-remote): New procedure. * gnu/service/dns.scm (unbound-server): New record. * gnu/service/dns.scm (unbound-serialize-unbound-server): New procedure. * gnu/service/dns.scm (unbound-configuration): New record. * gnu/service/dns.scm (unbound-config-file): New procedure. * gnu/service/dns.scm (unbound-shepherd-service): New procedure. * gnu/service/dns.scm (unbound-account-service): New constant. * gnu/service/dns.scm (unbound-service-type): New services. * gnu/tests/dns.scm: New file. * gnu/local.mk: Add new files. * doc/guix.texi: Add documentation. Signed-off-by: Sören Tempel --- Changes since v2: Added a system test and documentation. doc/guix.texi | 95 +++++++++++++++++++++ gnu/local.mk | 1 + gnu/services/dns.scm | 192 ++++++++++++++++++++++++++++++++++++++++++- gnu/tests/dns.scm | 110 +++++++++++++++++++++++++ 4 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 gnu/tests/dns.scm diff --git a/doc/guix.texi b/doc/guix.texi index caebe3b03c..d9ed112494 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -34300,6 +34300,101 @@ command-line arguments to @command{dnsmasq} as a list of strings. @end table @end deftp +@subsubheading Unbound Service + +@defvar unbound-service-type +This is the type of the unbound service, whose value should be a +@code{unbound-configuration} object as in this example: + +@lisp +(service unbound-service-type + (unbound-configuration + (forward-zone + (list + (unbound-zone + (name ".") + (forward-addr '("149.112.112.112#dns.quad9.net" + "2620:fe::9#dns.quad9.net")) + (forward-tls-upstream #t)))))) +@end lisp +@end defvar + +@deftp {Data Type} unbound-configuration +Available @code{unbound-configuration} fields are: + +@table @asis +@item @code{server} (type: unbound-server) +General options for the Unbound server. + +@item @code{remote-control} (type: unbound-remote) +Remote control options for the daemon. + +@item @code{forward-zone} (default: @code{()}) (type: list-of-unbound-zone) +A zone for which queries should be forwarded to another resolver. + +@item @code{extra-content} (type: maybe-string) +Raw content to add to the configuration file. + +@end table +@end deftp + +@deftp {Data Type} unbound-server +Available @code{unbound-server} fields are: + +@table @asis +@item @code{interface} (type: maybe-list-of-strings) +Interfaces listened on for queries from clients. + +@item @code{hide-version} (type: maybe-boolean) +Refuse the version.server and version.bind queries. + +@item @code{hide-identity} (type: maybe-boolean) +Refuse the id.server and hostname.bind queries. + +@item @code{tls-cert-bundle} (type: maybe-string) +Certificate bundle file, used for DNS over TLS. + +@item @code{extra-options} (default: @code{()}) (type: alist) +An association list of options to append. + +@end table +@end deftp + +@deftp {Data Type} unbound-remote +Available @code{unbound-remote} fields are: + +@table @asis +@item @code{control-enable} (type: maybe-boolean) +Enable remote control. + +@item @code{control-interface} (type: maybe-string) +IP address or local socket path to listen on for remote control. + +@item @code{extra-options} (default: @code{()}) (type: alist) +An association list of options to append. + +@end table +@end deftp + +@deftp {Data Type} unbound-zone +Available @code{unbound-zone} fields are: + +@table @asis +@item @code{name} (type: string) +Zone name. + +@item @code{forward-addr} (type: maybe-list-of-strings) +IP address of server to forward to. + +@item @code{forward-tls-upstream} (type: maybe-boolean) +Whether the queries to this forwarder use TLS for transport. + +@item @code{extra-options} (default: @code{()}) (type: alist) +An association list of options to append. + +@end table +@end deftp + @node VNC Services @subsection VNC Services @cindex VNC (virtual network computing) diff --git a/gnu/local.mk b/gnu/local.mk index f118fe4442..5d550b0639 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -832,6 +832,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/cups.scm \ %D%/tests/databases.scm \ %D%/tests/desktop.scm \ + %D%/tests/dns.scm \ %D%/tests/dict.scm \ %D%/tests/docker.scm \ %D%/tests/emacs.scm \ diff --git a/gnu/services/dns.scm b/gnu/services/dns.scm index 532e20e38a..c74001fac2 100644 --- a/gnu/services/dns.scm +++ b/gnu/services/dns.scm @@ -3,6 +3,7 @@ ;;; Copyright © 2020 Pierre Langlois ;;; Copyright © 2021 Maxime Devos ;;; Copyright © 2022 Remco van 't Veer +;;; Copyright © 2024 Sören Tempel ;;; ;;; This file is part of GNU Guix. ;;; @@ -52,7 +53,21 @@ (define-module (gnu services dns) knot-resolver-configuration dnsmasq-service-type - dnsmasq-configuration)) + dnsmasq-configuration + + unbound-service-type + unbound-zone + unbound-server + unbound-configuration + unbound-configuration? + unbound-configuration-server + unbound-configuration-remote-control + unbound-configuration-forward-zone + unbound-configuration-stub-zone + unbound-configuration-auth-zone + unbound-configuration-view + unbound-configuration-python + unbound-configuration-dynlib)) ;;; ;;; Knot DNS. @@ -902,3 +917,178 @@ (define dnsmasq-service-type dnsmasq-activation))) (default-value (dnsmasq-configuration)) (description "Run the dnsmasq DNS server."))) + + +;;; +;;; Unbound. +;;; + +(define (unbound-serialize-field field-name value) + (let ((field (object->string field-name)) + (value (cond + ((boolean? value) (if value "yes" "no")) + ((string? value) value) + (else (object->string value))))) + (if (string=? field "extra-content") + #~(string-append #$value "\n") + #~(format #f " ~a: ~s~%" #$field #$value)))) + +(define (unbound-serialize-alist field-name value) + #~(string-append #$@(generic-serialize-alist list + unbound-serialize-field + value))) + +(define (unbound-serialize-section section-name value fields) + #~(format #f "~a:~%~a" + #$(object->string section-name) + #$(serialize-configuration value fields))) + +(define unbound-serialize-string unbound-serialize-field) +(define unbound-serialize-boolean unbound-serialize-field) + +(define-maybe string (prefix unbound-)) +(define-maybe list-of-strings (prefix unbound-)) +(define-maybe boolean (prefix unbound-)) + +(define (unbound-serialize-list-of-strings field-name value) + #~(string-append #$@(map (cut unbound-serialize-string field-name <>) value))) + +(define-configuration unbound-zone + (name + string + "Zone name.") + + (forward-addr + maybe-list-of-strings + "IP address of server to forward to.") + + (forward-tls-upstream + maybe-boolean + "Whether the queries to this forwarder use TLS for transport.") + + (extra-options + (alist '()) + "An association list of options to append.") + + (prefix unbound-)) + +(define (unbound-serialize-unbound-zone field-name value) + (unbound-serialize-section field-name value unbound-zone-fields)) + +(define (unbound-serialize-list-of-unbound-zone field-name value) + #~(string-append #$@(map (cut unbound-serialize-unbound-zone field-name <>) + value))) + +(define list-of-unbound-zone? (list-of unbound-zone?)) + +(define-configuration unbound-remote + (control-enable + maybe-boolean + "Enable remote control.") + + (control-interface + maybe-string + "IP address or local socket path to listen on for remote control.") + + (extra-options + (alist '()) + "An association list of options to append.") + + (prefix unbound-)) + +(define (unbound-serialize-unbound-remote field-name value) + (unbound-serialize-section field-name value unbound-remote-fields)) + +(define-configuration unbound-server + (interface + maybe-list-of-strings + "Interfaces listened on for queries from clients.") + + (hide-version + maybe-boolean + "Refuse the version.server and version.bind queries.") + + (hide-identity + maybe-boolean + "Refuse the id.server and hostname.bind queries.") + + (tls-cert-bundle + maybe-string + "Certificate bundle file, used for DNS over TLS.") + + (extra-options + (alist '()) + "An association list of options to append.") + + (prefix unbound-)) + +(define (unbound-serialize-unbound-server field-name value) + (unbound-serialize-section field-name value unbound-server-fields)) + +(define-configuration unbound-configuration + (server + (unbound-server + (unbound-server + (interface '("127.0.0.1" "::1")) + + (hide-version #t) + (hide-identity #t) + + (tls-cert-bundle "/etc/ssl/certs/ca-certificates.crt"))) + "General options for the Unbound server.") + + (remote-control + (unbound-remote + (unbound-remote + (control-enable #t) + (control-interface "/run/unbound.sock"))) + "Remote control options for the daemon.") + + (forward-zone + (list-of-unbound-zone '()) + "A zone for which queries should be forwarded to another resolver.") + + (extra-content + maybe-string + "Raw content to add to the configuration file.") + + (prefix unbound-)) + +(define (unbound-config-file config) + (mixed-text-file "unbound.conf" + (serialize-configuration + config + unbound-configuration-fields))) + +(define (unbound-shepherd-service config) + (let ((config-file (unbound-config-file config))) + (list (shepherd-service + (documentation "Unbound daemon.") + (provision '(unbound dns)) + (requirement '(networking)) + (actions (list (shepherd-configuration-action config-file))) + (start #~(make-forkexec-constructor + (list (string-append #$unbound "/sbin/unbound") + "-d" "-p" "-c" #$config-file))) + (stop #~(make-kill-destructor)))))) + +(define unbound-account-service + (list (user-group (name "unbound") (system? #t)) + (user-account + (name "unbound") + (group "unbound") + (system? #t) + (comment "Unbound daemon user") + (home-directory "/var/empty") + (shell "/run/current-system/profile/sbin/nologin")))) + +(define unbound-service-type + (service-type (name 'unbound) + (description "Run the unbound DNS resolver.") + (extensions + (list (service-extension account-service-type + (const unbound-account-service)) + (service-extension shepherd-root-service-type + unbound-shepherd-service))) + (compose concatenate) + (default-value (unbound-configuration)))) diff --git a/gnu/tests/dns.scm b/gnu/tests/dns.scm new file mode 100644 index 0000000000..ff42456760 --- /dev/null +++ b/gnu/tests/dns.scm @@ -0,0 +1,110 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2025 Sören Tempel +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see . + +(define-module (gnu tests dns) + #:use-module (gnu tests) + #:use-module (gnu system) + #:use-module (gnu system vm) + #:use-module (gnu services) + #:use-module (gnu services dns) + #:use-module (gnu services networking) + #:use-module (gnu packages dns) + #:use-module (guix gexp) + #:export (%test-unbound)) + +(define %unbound-os + ;; TODO: Unbound config + (let ((base-os + (simple-operating-system + (service dhcp-client-service-type) + (service unbound-service-type + (unbound-configuration + (server + (unbound-server + (interface '("127.0.0.1" "::1")) + (extra-options + '((local-data . "example.local A 192.0.2.1")))))))))) + (operating-system + (inherit base-os) + (packages + (append (list + `(,isc-bind "utils") + unbound) + (operating-system-packages base-os)))))) + +(define (run-unbound-test) + "Run tests in %unbound-os with a running unbound daemon on localhost." + (define os + (marionette-operating-system + %unbound-os + #:imported-modules '((gnu services herd)))) + + (define vm + (virtual-machine os)) + + (define test + (with-imported-modules '((gnu build marionette)) + #~(begin + (use-modules (srfi srfi-64) + (gnu build marionette)) + (define marionette + (make-marionette (list #$vm))) + + (test-runner-current (system-test-runner #$output)) + (test-begin "unbound") + + (test-assert "service is running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + + ;; Make sure the 'unbound-control' and 'host' command is found. + (setenv "PATH" "/run/current-system/profile/bin:/run/current-system/profile/sbin") + + (start-service 'unbound)) + marionette)) + + (test-equal "unbound remote control works" + 0 + (marionette-eval + '(status:exit-val + (system* "unbound-control" "-s" "/run/unbound.sock" "status")) + marionette)) + + ;; We use a custom local-data A record here to avoid depending + ;; on network access and being able to contact the root servers. + (test-equal "resolves local-data domain" + "192.0.2.1" + (marionette-eval + '(begin + (use-modules (ice-9 popen) (rnrs io ports)) + + (let* ((port (open-input-pipe "dig @127.0.0.1 example.local +short")) + (out (get-string-all port))) + (close-port port) + (string-drop-right out 1))) ;; drop newline + marionette)) + + (test-end)))) + (gexp->derivation "unbound-test" test)) + +(define %test-unbound + (system-test + (name "unbound") + (description "Test that the unbound can respond to queries.") + (value (run-unbound-test))))