connman.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
From: matthias.gerstner@suse.de
To: connman@lists.linux.dev
Subject: [PATCH 7/7] dnsproxy: add standalone test version of dnsproxy and a test script for it
Date: Thu, 17 Feb 2022 12:31:13 +0100	[thread overview]
Message-ID: <20220217113113.24088-8-matthias.gerstner@suse.de> (raw)
In-Reply-To: <20220217113113.24088-1-matthias.gerstner@suse.de>

From: Matthias Gerstner <matthias.gerstner@suse.de>

Since dnsproxy is not really modular this requires building complete
connman minus the original main function. It makes testing a lot easier
though, since this standalone version doesn't require root privileges or
configuration aspects like the D-Bus connection etc.

Add this as EXTRA_PROGRAMS, not as CHECK_PROGRAMS, because the test
might need some specific settings to work (e.g. a working
/etc/resolv.conf with domain entry).
---
 Makefile.am                 |  13 +++
 tools/dnsproxy-simple-test  | 112 ++++++++++++++++++++++++++
 tools/dnsproxy-standalone.c | 154 ++++++++++++++++++++++++++++++++++++
 3 files changed, 279 insertions(+)
 create mode 100755 tools/dnsproxy-simple-test
 create mode 100644 tools/dnsproxy-standalone.c

diff --git a/Makefile.am b/Makefile.am
index e5718b19..ea532071 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -106,6 +106,7 @@ builtin_cflags =
 
 noinst_PROGRAMS =
 bin_PROGRAMS =
+EXTRA_PROGRAMS =
 
 unit_objects =
 
@@ -412,6 +413,18 @@ endif
 tools_dnsproxy_test_SOURCES = tools/dnsproxy-test.c
 tools_dnsproxy_test_LDADD = @GLIB_LIBS@
 
+tools_dnsproxy_standalone_CFLAGS = $(src_connmand_CFLAGS) -I$(srcdir)/src
+tools_dnsproxy_standalone_SOURCES = tools/dnsproxy-standalone.c $(src_connmand_SOURCES)
+# for EXTRA_PROGRAMS the BUILT_SOURCES aren't automatically added as
+# dependency, so let's do it explicitly
+tools/dnsproxy-standalone.c: $(BUILT_SOURCES)
+tools_dnsproxy_standalone_LDADD = $(src_connmand_LDADD)
+# pass -zmuldefs to let the linker tolerate the duplicate definition of
+# main(), the first definition from dnsproxy-standalone should be used
+tools_dnsproxy_standalone_LDFLAGS = $(src_connmand_LDFLAGS) -Wl,-zmuldefs
+
+EXTRA_PROGRAMS += tools/dnsproxy-standalone
+
 endif
 
 test_scripts = test/get-state test/list-services \
diff --git a/tools/dnsproxy-simple-test b/tools/dnsproxy-simple-test
new file mode 100755
index 00000000..b8fd7f77
--- /dev/null
+++ b/tools/dnsproxy-simple-test
@@ -0,0 +1,112 @@
+#!/bin/bash
+
+# this script runs the dnsproxy-standalone test program and runs a couple of
+# standard DNS queries against it, using the currently configured DNS server
+# in the system as dnsproxy configuration.
+
+echoerr() {
+	echo $@ 1>&2
+	exit 1
+}
+
+while [ $# -gt 0 ]; do
+	case "$1" in
+	"--valgrind")
+		VALGRIND=`which valgrind`
+		if [ -z "$VALGRIND" ]; then
+			echoerr "no valgrind executable found"
+		fi
+		;;
+	"-h")
+		echo "$0 [--valgrind]"
+		echo "--valgrind: run dnsproxy-standalone in valgrind"
+		exit 2
+		;;
+	*)
+		echoerr "Unknown argument $1"
+		;;
+	esac
+	shift
+done
+
+if [ -e "Makefile" ]; then
+	BUILDROOT="$PWD"
+else
+	if [ ! -n "$BUILDROOT" ]; then
+		echoerr "You need to set the BUILDROOT environment variable or run this script from the connman build tree root"
+	fi
+
+	pushd "$BUILDROOT" >/dev/null || echoerr "couldn't enter $BUILDROOT"
+fi
+make tools/dnsproxy-standalone || echoerr "failed to build dnsproxy-standalone"
+
+HOST=`which host`
+if [ -z "$HOST" ]; then
+	echoerr "Couldn't find 'host' DNS utility"
+fi
+
+DNSPROXY="$BUILDROOT/tools/dnsproxy-standalone"
+
+if [ ! -f "$DNSPROXY" ]; then
+	echoerr "standalone dnsproxy does not exist at $DNSPROXY"
+fi
+
+NS1=`grep -w nameserver -m 1 /etc/resolv.conf | cut -d ' ' -f 2`
+if [ -z "$NS1" ]; then
+	echoerr "Failed to determine system's nameserver from /etc/resolv.conf"
+fi
+
+DOMAIN1=`grep -w search -m 1 /etc/resolv.conf | cut -d ' ' -f 2`
+if [ -z "$DOMAIN1" ]; then
+	echoerr "Failed to determine default DNS domain from /etc/resolv.conf"
+fi
+
+# use an unprivileged port for the proxy so we don't need special permissions
+# to run this test
+PORT=8053
+
+# run the proxy in the background
+$VALGRIND $DNSPROXY $PORT "$DOMAIN1" "$NS1" &
+proxy_pid=$!
+
+cleanup() {
+	if [ $proxy_pid -eq -1 ]; then
+		return 0
+	fi
+	kill $proxy_pid
+	wait $proxy_pid
+	ret=$?
+	proxy_pid=-1
+	return $ret
+}
+
+trap cleanup err exit
+
+sleep 1
+echo -e "\n\n"
+
+# test both UDP and TCP mode
+for TRANSPORT in -U -T; do
+	# test both IPv4 and IPv6
+	for IP in -4 -6; do
+		echo "Testing resolution using transport $TRANSPORT and IP${IP}"
+		set -x
+		$HOST $TRANSPORT $IP -p$PORT www.example.com 127.0.0.1
+		RES=$?
+		set +x
+		if [ $RES -ne 0 ]; then
+			echoerr "resolution failed"
+		fi
+
+		echo -e "\n\n"
+	done
+done
+
+echo -e "\n\nDNS resolution succeeded for all test combinations"
+cleanup
+if [ $? -eq 0 ]; then
+	exit 0
+else
+	echoerr "dnsproxy returned non-zero exit status $?"
+fi
+
diff --git a/tools/dnsproxy-standalone.c b/tools/dnsproxy-standalone.c
new file mode 100644
index 00000000..690b4313
--- /dev/null
+++ b/tools/dnsproxy-standalone.c
@@ -0,0 +1,154 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "connman.h"
+
+/*
+ * This is a minimal connman setup  that only runs the internal dnsproxy
+ * component for testing. The advantage is that we can do a full integration
+ * test of the dnsproxy logic without requiring root privileges or setting up
+ * other complexities like D-Bus access etc.
+ */
+
+static GMainLoop *main_loop = NULL;
+
+static void usage(const char *prog)
+{
+	fprintf(stderr, "%s: <listen-port> <dns-domain> <dns-server>\n", prog);
+	exit(1);
+}
+
+static unsigned int to_uint(const char *s)
+{
+	char *end = NULL;
+	unsigned int ret;
+
+	ret = strtoul(s, &end, 10);
+
+	if (*end != '\0') {
+		fprintf(stderr, "invalid argument: %s", s);
+		exit(1);
+	}
+
+	return ret;
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	result = read(fd, &si, sizeof(si));
+	if (result != sizeof(si))
+		return FALSE;
+
+	switch (si.ssi_signo) {
+	case SIGINT:
+	case SIGTERM:
+		printf("Terminating due to signal\n");
+		g_main_loop_quit(main_loop);
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+	GIOChannel *channel;
+	guint source;
+	sigset_t mask;
+	int fd;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+		perror("Failed to set signal mask");
+		return 0;
+	}
+
+	fd = signalfd(-1, &mask, 0);
+	if (fd < 0) {
+		perror("Failed to create signal descriptor");
+		return 0;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				signal_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+int main(int argc, const char **argv)
+{
+	unsigned int port = 0;
+	const char *domain = argv[2];
+	const char *server = argv[3];
+	guint signal = 0;
+
+	if (argc != 4)
+	{
+		usage(argv[0]);
+	}
+
+	port = to_uint(argv[1]);
+
+	__connman_util_init();
+	printf("Listening on local port %u\n", port);
+	__connman_dnsproxy_set_listen_port(port);
+
+	if (__connman_dnsproxy_init() < 0) {
+		fprintf(stderr, "failed to initialize dnsproxy\n");
+		return 1;
+	}
+
+	printf("Using DNS server %s on domain %s\n", server, domain);
+
+	if (__connman_dnsproxy_append(-1, domain, server) < 0) {
+		fprintf(stderr, "failed to add DNS server\n");
+		return 1;
+	}
+
+	// we need to trick a bit to make the server entry enter "enabled"
+	// state in dnsproxy. Appending and removing an arbitrary entry causes
+	// "enable_fallback()" to be called which does what we want. Doesn't
+	// make much sense but it is good enough for the standalone server at
+	// the moment.
+	__connman_dnsproxy_append(15, domain, server);
+	__connman_dnsproxy_remove(15, domain, server);
+
+	signal = setup_signalfd();
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	g_main_loop_run(main_loop);
+
+	__connman_dnsproxy_cleanup();
+	__connman_util_cleanup();
+	g_source_remove(signal);
+
+	return 0;
+}
-- 
2.34.1


  parent reply	other threads:[~2022-02-17 11:31 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-17 11:31 dnsproxy: some initial refactoring and integration test matthias.gerstner
2022-02-17 11:31 ` [PATCH 1/7] dnsproxy: remove unused domain parameter from `remove_server()` matthias.gerstner
2022-02-17 11:31 ` [PATCH 2/7] dnsproxy: remove unnecessarily shadowed variable matthias.gerstner
2022-02-17 11:31 ` [PATCH 3/7] dnsproxy: protocol_offset: remove error return case and return size_t matthias.gerstner
2022-02-21  8:49   ` Daniel Wagner
2022-02-17 11:31 ` [PATCH 4/7] .gitignore: also ignore backup configure script matthias.gerstner
2022-02-21  8:49   ` Daniel Wagner
2022-02-17 11:31 ` [PATCH 5/7] dnsproxy: support programmatic configuration of the default listen port matthias.gerstner
2022-02-17 11:31 ` [PATCH 6/7] dnsproxy-test: support command line specification of dnsproxy port matthias.gerstner
2022-02-17 11:31 ` matthias.gerstner [this message]
2022-02-21  8:48   ` [PATCH 7/7] dnsproxy: add standalone test version of dnsproxy and a test script for it Daniel Wagner
2022-02-28 13:19     ` dnsproxy: some initial refactoring and integration test matthias.gerstner
2022-02-28 13:19       ` [PATCH] dnsproxy: add standalone test version of dnsproxy and a test script for it matthias.gerstner
2022-03-04  8:50         ` Daniel Wagner
2022-02-21  8:51 ` dnsproxy: some initial refactoring and integration test Daniel Wagner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220217113113.24088-8-matthias.gerstner@suse.de \
    --to=matthias.gerstner@suse.de \
    --cc=connman@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).