From b7d0c146b7ab393f5a1c0d2de807de363c16ab20 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
Date: Thu, 26 Jan 2017 18:11:17 +0100
Subject: [PATCH] Proof-of-concept to enable WKS under chroot by command line
 parameter

---
 bin/check/named-checkconf.c       | 37 ++++++++++++++++++++++++++++++++++--
 bin/check/named-checkconf.docbook | 15 +++++++++++++++
 lib/dns/rdata/in_1/wks_11.c       | 40 +++------------------------------------
 lib/isc/unix/include/isc/net.h    | 26 +++++++++++++++++++++++++
 lib/isc/unix/net.c                | 38 +++++++++++++++++++++++++++++++++++++
 lib/isc/win32/include/isc/net.h   | 26 +++++++++++++++++++++++++
 lib/isc/win32/net.c               | 36 +++++++++++++++++++++++++++++++++++
 7 files changed, 179 insertions(+), 39 deletions(-)

diff --git a/bin/check/named-checkconf.c b/bin/check/named-checkconf.c
index 36b88ef..76e8798 100644
--- a/bin/check/named-checkconf.c
+++ b/bin/check/named-checkconf.c
@@ -59,7 +59,7 @@ usage(void) ISC_PLATFORM_NORETURN_POST;
 
 static void
 usage(void) {
-	fprintf(stderr, "usage: %s [-hjlvz] [-p [-x]] [-t directory] "
+	fprintf(stderr, "usage: %s [-hjlvz] [-p [-x]] [-w [proto:]service] [-t directory] "
 		"[named.conf]\n", program);
 	exit(1);
 }
@@ -518,6 +518,32 @@ output(void *closure, const char *text, int textlen) {
 	}
 }
 
+/** Accept single string as argument.
+ * \param proto_service proto:service or only service with default udp proto.
+ *
+ */
+static isc_boolean_t
+test_wksbyname(char *proto_service)
+{
+	long x;
+	char * service;
+	isc_boolean_t r;
+	const char * proto = "udp";
+	service = strchr(proto_service, ':');
+	if (service)
+	{
+		proto = proto_service;
+		*service = '\0';
+		++service;
+	}
+	else
+		service = proto_service;
+	r = isc_net_getprotobyname(proto, &x);
+	if (r != ISC_TRUE)
+		return r;
+	return isc_net_getservbyname(service, proto, &x);
+}
+
 /*% The main processing routine */
 int
 main(int argc, char **argv) {
@@ -539,7 +565,7 @@ main(int argc, char **argv) {
 	/*
 	 * Process memory debugging argument first.
 	 */
-#define CMDLINE_FLAGS "dhjlm:t:pvxz"
+#define CMDLINE_FLAGS "dhjlm:t:pvw:xz"
 	while ((c = isc_commandline_parse(argc, argv, CMDLINE_FLAGS)) != -1) {
 		switch (c) {
 		case 'm':
@@ -596,6 +622,13 @@ main(int argc, char **argv) {
 			printf(VERSION "\n");
 			exit(0);
 
+		case 'w':
+			if (test_wksbyname(isc_commandline_argument) != ISC_TRUE) {
+				fprintf(stderr, "test_wksbyname error\n");
+				exit(1);
+			}
+			break;
+
 		case 'x':
 			flags |= CFG_PRINTER_XKEY;
 			break;
diff --git a/bin/check/named-checkconf.docbook b/bin/check/named-checkconf.docbook
index 7075bd7..99dca4c 100644
--- a/bin/check/named-checkconf.docbook
+++ b/bin/check/named-checkconf.docbook
@@ -143,6 +143,21 @@
       </varlistentry>
 
       <varlistentry>
+        <term>-w <replaceable class="parameter">service</replaceable></term>
+        <listitem>
+          <para>
+            Get <filename>service</filename> number from system.
+            It is useful to initialize services database before switching
+            to chroot. Use before -t parameter. If used after -t, it tests
+            if it is possible to get service number from name in chroot.
+            service can be in form of proto:service or only service.
+            This parameter should help using WKS records with symbolic
+            names from chroot.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term>-x</term>
         <listitem>
           <para>
diff --git a/lib/dns/rdata/in_1/wks_11.c b/lib/dns/rdata/in_1/wks_11.c
index 5141c9f..a94c5bc 100644
--- a/lib/dns/rdata/in_1/wks_11.c
+++ b/lib/dns/rdata/in_1/wks_11.c
@@ -18,40 +18,9 @@
 
 #include <isc/net.h>
 #include <isc/netdb.h>
-#include <isc/once.h>
 
 #define RRTYPE_WKS_ATTRIBUTES (0)
 
-static isc_mutex_t wks_lock;
-
-static void init_lock(void) {
-	RUNTIME_CHECK(isc_mutex_init(&wks_lock) == ISC_R_SUCCESS);
-}
-
-static isc_boolean_t
-mygetprotobyname(const char *name, long *proto) {
-	struct protoent *pe;
-
-	LOCK(&wks_lock);
-	pe = getprotobyname(name);
-	if (pe != NULL)
-		*proto = pe->p_proto;
-	UNLOCK(&wks_lock);
-	return (ISC_TF(pe != NULL));
-}
-
-static isc_boolean_t
-mygetservbyname(const char *name, const char *proto, long *port) {
-	struct servent *se;
-
-	LOCK(&wks_lock);
-	se = getservbyname(name, proto);
-	if (se != NULL)
-		*port = ntohs(se->s_port);
-	UNLOCK(&wks_lock);
-	return (ISC_TF(se != NULL));
-}
-
 #ifdef _WIN32
 #include <windows.h>
 #include <winsock2.h>
@@ -60,7 +29,6 @@ mygetservbyname(const char *name, const char *proto, long *port) {
 
 static inline isc_result_t
 fromtext_in_wks(ARGS_FROMTEXT) {
-	static isc_once_t once = ISC_ONCE_INIT;
 	isc_token_t token;
 	isc_region_t region;
 	struct in_addr addr;
@@ -83,8 +51,6 @@ fromtext_in_wks(ARGS_FROMTEXT) {
 	UNUSED(options);
 	UNUSED(rdclass);
 
-	RUNTIME_CHECK(isc_once_do(&once, init_lock) == ISC_R_SUCCESS);
-
 #ifdef _WIN32
 	{
 		WORD wVersionRequested;
@@ -122,7 +88,7 @@ fromtext_in_wks(ARGS_FROMTEXT) {
 	proto = strtol(DNS_AS_STR(token), &e, 10);
 	if (*e == 0)
 		;
-	else if (!mygetprotobyname(DNS_AS_STR(token), &proto))
+	else if (!isc_net_getprotobyname(DNS_AS_STR(token), &proto))
 		CHECKTOK(DNS_R_UNKNOWNPROTO);
 
 	if (proto < 0 || proto > 0xff)
@@ -155,8 +121,8 @@ fromtext_in_wks(ARGS_FROMTEXT) {
 		port = strtol(DNS_AS_STR(token), &e, 10);
 		if (*e == 0)
 			;
-		else if (!mygetservbyname(service, ps, &port) &&
-			 !mygetservbyname(DNS_AS_STR(token), ps, &port))
+		else if (!isc_net_getservbyname(service, ps, &port) &&
+			 !isc_net_getservbyname(DNS_AS_STR(token), ps, &port))
 			CHECKTOK(DNS_R_UNKNOWNSERVICE);
 		if (port < 0 || port > 0xffff)
 			CHECKTOK(ISC_R_RANGE);
diff --git a/lib/isc/unix/include/isc/net.h b/lib/isc/unix/include/isc/net.h
index 7bf6b08..218af79 100644
--- a/lib/isc/unix/include/isc/net.h
+++ b/lib/isc/unix/include/isc/net.h
@@ -384,6 +384,32 @@ isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high);
  *	the range.
  */
 
+isc_boolean_t
+isc_net_getprotobyname(const char *name, long *proto);
+/*%<
+ * Get numeric protocol from system by its name.
+ *
+ *\li	'name' is protocol name as in SRV records.
+ *
+ * Returns:
+ *
+ *\li	*proto numeric value of protocol name
+ */
+
+isc_boolean_t
+isc_net_getservbyname(const char *name, const char *proto, long *port);
+/*%<
+ * Get numeric port from system by service 'name' and protocol 'proto'.
+ *
+ *\li	'name' service name as in SRV records.
+ *\li   'proto' protocol name as in SRV records.
+ *
+ * Returns:
+ *
+ *\li	*port numeric port of protocol of service.
+ */
+
+
 #ifdef ISC_PLATFORM_NEEDNTOP
 const char *
 isc_net_ntop(int af, const void *src, char *dst, size_t size);
diff --git a/lib/isc/unix/net.c b/lib/isc/unix/net.c
index 8f80c32..5d30c56 100644
--- a/lib/isc/unix/net.c
+++ b/lib/isc/unix/net.c
@@ -26,6 +26,7 @@
 
 #include <isc/log.h>
 #include <isc/msgs.h>
+#include <isc/mutex.h>
 #include <isc/net.h>
 #include <isc/netdb.h>
 #include <isc/once.h>
@@ -118,6 +119,7 @@ static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
 
 static isc_once_t 	once = ISC_ONCE_INIT;
 static isc_once_t 	once_dscp = ISC_ONCE_INIT;
+static isc_once_t	once_wks  = ISC_ONCE_INIT;
 
 static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
 static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
@@ -126,6 +128,8 @@ static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
 static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
 static unsigned int	dscp_result = 0;
 
+static isc_mutex_t wks_lock;
+
 static isc_result_t
 try_proto(int domain) {
 	int s;
@@ -222,6 +226,10 @@ initialize_action(void) {
 #endif
 }
 
+static void init_wks_lock(void) {
+	RUNTIME_CHECK(isc_mutex_init(&wks_lock) == ISC_R_SUCCESS);
+}
+
 static void
 initialize(void) {
 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
@@ -878,6 +886,36 @@ isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
 	return (ISC_R_SUCCESS);	/* we currently never fail in this function */
 }
 
+isc_boolean_t
+isc_net_getprotobyname(const char *name, long *proto) {
+	struct protoent *pe;
+
+	/* TODO: I might use getprotobyname_r, as I am doing implementation
+	 * specific for unix. */
+	RUNTIME_CHECK(isc_once_do(&once_wks, init_wks_lock) == ISC_R_SUCCESS);
+	LOCK(&wks_lock);
+	pe = getprotobyname(name);
+	if (pe != NULL)
+		*proto = pe->p_proto;
+	UNLOCK(&wks_lock);
+	return (ISC_TF(pe != NULL));
+}
+
+isc_boolean_t
+isc_net_getservbyname(const char *name, const char *proto, long *port) {
+	struct servent *se;
+
+	/* TODO: I might use getservbyname_r, as I am doing implementation
+	 * specific for unix. */
+	RUNTIME_CHECK(isc_once_do(&once_wks, init_wks_lock) == ISC_R_SUCCESS);
+	LOCK(&wks_lock);
+	se = getservbyname(name, proto);
+	if (se != NULL)
+		*port = ntohs(se->s_port);
+	UNLOCK(&wks_lock);
+	return (ISC_TF(se != NULL));
+}
+
 void
 isc_net_disableipv4(void) {
 	initialize();
diff --git a/lib/isc/win32/include/isc/net.h b/lib/isc/win32/include/isc/net.h
index ea91064..36caac2 100644
--- a/lib/isc/win32/include/isc/net.h
+++ b/lib/isc/win32/include/isc/net.h
@@ -400,6 +400,32 @@ isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high);
  *	the range.
  */
 
+isc_boolean_t
+isc_net_getprotobyname(const char *name, long *proto);
+/*%<
+ * Get numeric protocol from system by its name.
+ *
+ *\li	'name' is protocol name as in SRV records.
+ *
+ * Returns:
+ *
+ *\li	*proto numeric value of protocol name
+ */
+
+isc_boolean_t
+isc_net_getservbyname(const char *name, const char *proto, long *port);
+/*%<
+ * Get numeric port from system by service 'name' and protocol 'proto'.
+ *
+ *\li	'name' service name as in SRV records.
+ *\li   'proto' protocol name as in SRV records.
+ *
+ * Returns:
+ *
+ *\li	*port numeric port of protocol of service.
+ */
+
+
 #ifdef ISC_PLATFORM_NEEDNTOP
 const char *
 isc_net_ntop(int af, const void *src, char *dst, size_t size);
diff --git a/lib/isc/win32/net.c b/lib/isc/win32/net.c
index 85cb650..b2efd0a 100644
--- a/lib/isc/win32/net.c
+++ b/lib/isc/win32/net.c
@@ -15,6 +15,7 @@
 
 #include <isc/log.h>
 #include <isc/msgs.h>
+#include <isc/mutex.h>
 #include <isc/net.h>
 #include <isc/once.h>
 #include <isc/strerror.h>
@@ -45,6 +46,7 @@ const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT;
 static isc_once_t 	once = ISC_ONCE_INIT;
 static isc_once_t 	once_ipv6only = ISC_ONCE_INIT;
 static isc_once_t 	once_ipv6pktinfo = ISC_ONCE_INIT;
+static isc_once_t	once_wks  = ISC_ONCE_INIT;
 static isc_result_t	ipv4_result = ISC_R_NOTFOUND;
 static isc_result_t	ipv6_result = ISC_R_NOTFOUND;
 static isc_result_t	ipv6only_result = ISC_R_NOTFOUND;
@@ -52,6 +54,8 @@ static isc_result_t	ipv6pktinfo_result = ISC_R_NOTFOUND;
 
 void InitSockets(void);
 
+static isc_mutex_t wks_lock;
+
 static isc_result_t
 try_proto(int domain) {
 	SOCKET s;
@@ -97,6 +101,10 @@ initialize_action(void) {
 #endif
 }
 
+static void init_wks_lock(void) {
+	RUNTIME_CHECK(isc_mutex_init(&wks_lock) == ISC_R_SUCCESS);
+}
+
 static void
 initialize(void) {
 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
@@ -305,6 +313,34 @@ isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
 	return (ISC_R_SUCCESS);	/* we currently never fail in this function */
 }
 
+isc_boolean_t
+isc_net_getprotobyname(const char *name, long *proto) {
+	struct protoent *pe;
+
+	/* TODO: Is locking even required on win32? */
+	RUNTIME_CHECK(isc_once_do(&once_wks, init_wks_lock) == ISC_R_SUCCESS);
+	LOCK(&wks_lock);
+	pe = getprotobyname(name);
+	if (pe != NULL)
+		*proto = pe->p_proto;
+	UNLOCK(&wks_lock);
+	return (ISC_TF(pe != NULL));
+}
+
+isc_boolean_t
+isc_net_getservbyname(const char *name, const char *proto, long *port) {
+	struct servent *se;
+
+	/* TODO: Is locking even required on win32? */
+	RUNTIME_CHECK(isc_once_do(&once_wks, init_wks_lock) == ISC_R_SUCCESS);
+	LOCK(&wks_lock);
+	se = getservbyname(name, proto);
+	if (se != NULL)
+		*port = ntohs(se->s_port);
+	UNLOCK(&wks_lock);
+	return (ISC_TF(se != NULL));
+}
+
 void
 isc_net_disableipv4(void) {
 	initialize();
-- 
2.9.3

