diff --git a/NOTICE.txt b/NOTICE.txt
index f9888e61e1..9cf3e812ad 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -237,3 +237,12 @@ This product contains the Maven wrapper scripts from 'Maven Wrapper', that provi
* license/LICENSE.mvn-wrapper.txt (Apache License 2.0)
* HOMEPAGE:
* https://github.com/takari/maven-wrapper
+
+This product contains the dnsinfo.h header file, that provides a way to retrieve the system DNS configuration on MacOS.
+This private header is also used by Apple's open source
+ mDNSResponder (https://opensource.apple.com/tarballs/mDNSResponder/).
+
+ * LICENSE:
+ * license/LICENSE.dnsinfo.txt (Apache License 2.0)
+ * HOMEPAGE:
+ * http://www.opensource.apple.com/source/configd/configd-453.19/dnsinfo/dnsinfo.h
\ No newline at end of file
diff --git a/all/pom.xml b/all/pom.xml
index da260d80ad..e6fecec1bb 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -65,6 +65,14 @@
compile
true
+
+ ${project.groupId}
+ netty-resolver-dns-native-macos
+ ${project.version}
+ osx-x86_64
+ compile
+ true
+
@@ -89,6 +97,14 @@
compile
true
+
+ ${project.groupId}
+ netty-resolver-dns-native-macos
+ ${project.version}
+ osx-x86_64
+ compile
+ true
+
@@ -120,6 +136,13 @@
compile
true
+
+ ${project.groupId}
+ netty-resolver-dns-native-macos
+ ${project.version}
+ compile
+ true
+
${project.groupId}
diff --git a/license/LICENSE.dnsinfo.txt b/license/LICENSE.dnsinfo.txt
new file mode 100644
index 0000000000..7554838ad7
--- /dev/null
+++ b/license/LICENSE.dnsinfo.txt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2004-2006, 2008, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
diff --git a/pom.xml b/pom.xml
index b37283315d..11098725fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -352,6 +352,7 @@
codec-xml
resolver
resolver-dns
+ resolver-dns-native-macos
tarball
transport
transport-native-unix-common-tests
@@ -713,6 +714,7 @@
0.13.1
+ true
true
true
\d+\.\d+\.\d+\.Final
diff --git a/resolver-dns-native-macos/pom.xml b/resolver-dns-native-macos/pom.xml
new file mode 100644
index 0000000000..6b7558ab94
--- /dev/null
+++ b/resolver-dns-native-macos/pom.xml
@@ -0,0 +1,185 @@
+
+
+
+ 4.0.0
+
+ io.netty
+ netty-parent
+ 5.0.0.Final-SNAPSHOT
+
+ netty-resolver-dns-native-macos
+
+ Netty/Resolver/DNS/MacOS
+ jar
+
+
+
+ mac
+
+
+ mac
+
+
+
+ LDFLAGS=-Wl,-weak_library,${unix.common.lib.unpacked.dir}/lib${unix.common.lib.name}.a
+ false
+
+
+
+
+ maven-dependency-plugin
+
+
+
+ unpack
+ generate-sources
+
+ unpack-dependencies
+
+
+ ${project.groupId}
+ netty-transport-native-unix-common
+ ${jni.classifier}
+ ${unix.common.lib.dir}
+ META-INF/native/**
+ false
+ true
+
+
+
+
+
+
+ org.fusesource.hawtjni
+ maven-hawtjni-plugin
+
+
+ build-native-lib
+
+ netty_resolver_dns_native_macos_${os.detected.arch}
+ ${project.basedir}/src/main/c
+ ${project.build.outputDirectory}
+
+ .
+
+ ${jni.compiler.args.ldflags}
+ ${jni.compiler.args.cflags}
+
+
+
+ generate
+ build
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+
+ native-jar
+
+ jar
+
+
+
+
+ true
+
+
+ META-INF/native/libnetty_resolver_dns_native_macos_${os.detected.arch}.jnilib; osname=MacOSX, processor=${os.detected.arch}"
+ ${javaModuleName}
+
+ true
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+ ${jni.classifier}
+
+
+
+
+
+
+
+
+ io.netty
+ netty-transport-native-unix-common
+ ${project.version}
+ ${jni.classifier}
+
+ true
+
+
+
+
+
+
+ io.netty.resolver.dns.macos
+
+ netty-unix-common
+ ${project.build.directory}/unix-common-lib
+ ${unix.common.lib.dir}/META-INF/native/lib
+ ${unix.common.lib.dir}/META-INF/native/include
+ CFLAGS=-O3 -Werror -fno-omit-frame-pointer -Wunused-variable -fvisibility=hidden -I${unix.common.include.unpacked.dir}
+ LDFLAGS=-z now -L${unix.common.lib.unpacked.dir} -Wl,--whole-archive -l${unix.common.lib.name} -Wl,--no-whole-archive
+
+
+
+
+ io.netty
+ netty-common
+ ${project.version}
+
+
+ io.netty
+ netty-resolver-dns
+ ${project.version}
+
+
+ io.netty
+ netty-transport-native-unix-common
+ ${project.version}
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+
+ default-jar
+
+
+ META-INF/native/**
+
+
+
+
+
+
+
+
+
diff --git a/resolver-dns-native-macos/src/main/c/dnsinfo.h b/resolver-dns-native-macos/src/main/c/dnsinfo.h
new file mode 100644
index 0000000000..8b10e8d4c6
--- /dev/null
+++ b/resolver-dns-native-macos/src/main/c/dnsinfo.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (c) 2004-2006, 2008, 2009, 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef __DNSINFO_H__
+#define __DNSINFO_H__
+/*
+ * These routines provide access to the systems DNS configuration
+ */
+#include
+#include
+#include
+#include
+#include
+#define DNSINFO_VERSION 20111104
+#define DEFAULT_SEARCH_ORDER 200000 /* search order for the "default" resolver domain name */
+#define DNS_PTR(type, name) \
+ union { \
+ type name; \
+ uint64_t _ ## name ## _p; \
+ }
+#define DNS_VAR(type, name) \
+ type name
+#pragma pack(4)
+typedef struct {
+ struct in_addr address;
+ struct in_addr mask;
+} dns_sortaddr_t;
+#pragma pack()
+#pragma pack(4)
+typedef struct {
+ DNS_PTR(char *, domain); /* domain */
+ DNS_VAR(int32_t, n_nameserver); /* # nameserver */
+ DNS_PTR(struct sockaddr **, nameserver);
+ DNS_VAR(uint16_t, port); /* port (in host byte order) */
+ DNS_VAR(int32_t, n_search); /* # search */
+ DNS_PTR(char **, search);
+ DNS_VAR(int32_t, n_sortaddr); /* # sortaddr */
+ DNS_PTR(dns_sortaddr_t **, sortaddr);
+ DNS_PTR(char *, options); /* options */
+ DNS_VAR(uint32_t, timeout); /* timeout */
+ DNS_VAR(uint32_t, search_order); /* search_order */
+ DNS_VAR(uint32_t, if_index);
+ DNS_VAR(uint32_t, flags);
+ DNS_VAR(uint32_t, reach_flags); /* SCNetworkReachabilityFlags */
+ DNS_VAR(uint32_t, reserved[5]);
+} dns_resolver_t;
+#pragma pack()
+#define DNS_RESOLVER_FLAGS_SCOPED 1 /* configuration is for scoped questions */
+#pragma pack(4)
+typedef struct {
+ DNS_VAR(int32_t, n_resolver); /* resolver configurations */
+ DNS_PTR(dns_resolver_t **, resolver);
+ DNS_VAR(int32_t, n_scoped_resolver); /* "scoped" resolver configurations */
+ DNS_PTR(dns_resolver_t **, scoped_resolver);
+ DNS_VAR(uint32_t, reserved[5]);
+} dns_config_t;
+#pragma pack()
+__BEGIN_DECLS
+/*
+ * DNS configuration access APIs
+ */
+const char *
+dns_configuration_notify_key ();
+dns_config_t *
+dns_configuration_copy ();
+void
+dns_configuration_free (dns_config_t *config);
+void
+_dns_configuration_ack (dns_config_t *config,
+ const char *bundle_id);
+__END_DECLS
+#endif /* __DNSINFO_H__ */
\ No newline at end of file
diff --git a/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c b/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c
new file mode 100644
index 0000000000..68df75c6c1
--- /dev/null
+++ b/resolver-dns-native-macos/src/main/c/netty_resolver_dns_macos.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dnsinfo.h"
+#include "netty_unix_jni.h"
+#include "netty_unix_util.h"
+#include "netty_unix_socket.h"
+#include "netty_unix_errors.h"
+
+static jclass dnsResolverClass = NULL;
+static jclass byteArrayClass = NULL;
+static jclass stringClass = NULL;
+static jmethodID dnsResolverMethodId = NULL;
+
+// JNI Registered Methods Begin
+
+// We use the same API as mDNSResponder and Chromium to retrieve the current nameserver configuration for the system:
+// See:
+// https://src.chromium.org/viewvc/chrome?revision=218617&view=revision
+// https://opensource.apple.com/tarballs/mDNSResponder/
+static jobjectArray netty_resolver_dns_macos_resolvers(JNIEnv* env, jclass clazz) {
+ dns_config_t* config = dns_configuration_copy();
+
+ jobjectArray array = (*env)->NewObjectArray(env, config->n_resolver, dnsResolverClass, NULL);
+ if (array == NULL) {
+ goto error;
+ }
+
+ for (int i = 0; i < config->n_resolver; i++) {
+ dns_resolver_t* resolver = config->resolver[i];
+ jstring domain = NULL;
+
+ if (resolver->domain != NULL) {
+ domain = (*env)->NewStringUTF(env, resolver->domain);
+ if (domain == NULL) {
+ goto error;
+ }
+ }
+
+ jobjectArray addressArray = (*env)->NewObjectArray(env, resolver->n_nameserver, byteArrayClass, NULL);
+ if (addressArray == NULL) {
+ goto error;
+ }
+
+ for (int a = 0; a < resolver->n_nameserver; a++) {
+ jbyteArray address = netty_unix_socket_createInetSocketAddressArray(env, (const struct sockaddr_storage *) resolver->nameserver[a]);
+ if (address == NULL) {
+ netty_unix_errors_throwOutOfMemoryError(env);
+ goto error;
+ }
+ (*env)->SetObjectArrayElement(env, addressArray, a, address);
+ }
+
+ jint port = resolver->port;
+
+ jobjectArray searchArray = (*env)->NewObjectArray(env, resolver->n_search, stringClass, NULL);
+ if (searchArray == NULL) {
+ goto error;
+ }
+
+ for (int a = 0; a < resolver->n_search; a++) {
+ jstring search = (*env)->NewStringUTF(env, resolver->search[a]);
+ if (search == NULL) {
+ goto error;
+ }
+
+ (*env)->SetObjectArrayElement(env, searchArray, a, search);
+ }
+
+ jstring options = NULL;
+ if (resolver->options != NULL) {
+ options = (*env)->NewStringUTF(env, resolver->options);
+ if (options == NULL) {
+ goto error;
+ }
+ }
+
+ jint timeout = resolver->timeout;
+ jint searchOrder = resolver->search_order;
+
+ jobject java_resolver = (*env)->NewObject(env, dnsResolverClass, dnsResolverMethodId, domain,
+ addressArray, port, searchArray, options, timeout, searchOrder);
+ if (java_resolver == NULL) {
+ goto error;
+ }
+ (*env)->SetObjectArrayElement(env, array, i, java_resolver);
+ }
+
+ dns_configuration_free(config);
+ return array;
+error:
+ dns_configuration_free(config);
+ return NULL;
+}
+
+
+// JNI Method Registration Table Begin
+
+static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
+ JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * 1);
+
+ char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/resolver/dns/macos/DnsResolver;");
+ JNINativeMethod* dynamicMethod = &dynamicMethods[0];
+ dynamicMethod->name = "resolvers";
+ dynamicMethod->signature = netty_unix_util_prepend("()[L", dynamicTypeName);
+ dynamicMethod->fnPtr = (void *) netty_resolver_dns_macos_resolvers;
+ free(dynamicTypeName);
+ return dynamicMethods;
+}
+
+static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
+ free(dynamicMethods[0].signature);
+ free(dynamicMethods);
+}
+
+// JNI Method Registration Table End
+
+
+static void JNI_OnUnload_netty_resolver_dns_native_macos0(JavaVM* vm, void* reserved) {
+ JNIEnv* env;
+ if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) {
+ // Something is wrong but nothing we can do about this :(
+ return;
+ }
+
+ if (byteArrayClass != NULL) {
+ (*env)->DeleteGlobalRef(env, byteArrayClass);
+ byteArrayClass = NULL;
+ }
+
+ if (stringClass != NULL) {
+ (*env)->DeleteGlobalRef(env, stringClass);
+ stringClass = NULL;
+ }
+}
+
+static void netty_resolver_dns_native_macos0_OnUnLoad(JNIEnv* env) {
+
+}
+
+static jint JNI_OnLoad_netty_resolver_dns_native_macos0(JavaVM* vm, void* reserved) {
+ JNIEnv* env;
+ if ((*vm)->GetEnv(vm, (void**) &env, NETTY_JNI_VERSION) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+#ifndef NETTY_BUILD_STATIC
+ Dl_info dlinfo;
+ jint status = 0;
+ // We need to use an address of a function that is uniquely part of this library, so choose a static
+ // function. See https://github.com/netty/netty/issues/4840.
+ if (!dladdr((void*) netty_resolver_dns_native_macos0_OnUnLoad, &dlinfo)) {
+ fprintf(stderr, "FATAL: resolver-dns-native-macos JNI call to dladdr failed!\n");
+ return JNI_ERR;
+ }
+ char* packagePrefix = netty_unix_util_parse_package_prefix(dlinfo.dli_fname, "netty_resolver_dns_native_macos", &status);
+ if (status == JNI_ERR) {
+ fprintf(stderr, "FATAL: resolver-dns-native-macos JNI encountered unexpected dlinfo.dli_fname: %s\n", dlinfo.dli_fname);
+ return JNI_ERR;
+ }
+#endif /* NETTY_BUILD_STATIC */
+
+ // Register the methods which are not referenced by static member variables
+ JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
+ if (netty_unix_util_register_natives(env,
+ packagePrefix,
+ "io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider",
+ dynamicMethods, 1) != 0) {
+ freeDynamicMethodsTable(dynamicMethods);
+ fprintf(stderr, "FATAL: Couldnt register natives");
+
+ return JNI_ERR;
+ }
+ freeDynamicMethodsTable(dynamicMethods);
+ dynamicMethods = NULL;
+
+
+ char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/resolver/dns/macos/DnsResolver");
+ jclass localDnsResolverClass = (*env)->FindClass(env, nettyClassName);
+ free(nettyClassName);
+ nettyClassName = NULL;
+ if (localDnsResolverClass == NULL) {
+ // pending exception...
+ return JNI_ERR;
+ }
+ dnsResolverClass = (jclass) (*env)->NewGlobalRef(env, localDnsResolverClass);
+ if (dnsResolverClass == NULL) {
+ return JNI_ERR;
+ }
+ dnsResolverMethodId = (*env)->GetMethodID(env, dnsResolverClass, "", "(Ljava/lang/String;[[BI[Ljava/lang/String;Ljava/lang/String;II)V");
+ if (dnsResolverMethodId == NULL) {
+ netty_unix_errors_throwRuntimeException(env, "failed to get method ID: DnsResolver.(String, byte[][], String[], String, int, int)");
+ return JNI_ERR;
+ }
+
+ if (packagePrefix != NULL) {
+ free(packagePrefix);
+ packagePrefix = NULL;
+ }
+
+ jclass byteArrayCls = (*env)->FindClass(env, "[B");
+ if (byteArrayCls == NULL) {
+ // pending exception...
+ return JNI_ERR;
+ }
+ byteArrayClass = (jclass) (*env)->NewGlobalRef(env, byteArrayCls);
+ if (byteArrayClass == NULL) {
+ return JNI_ERR;
+ }
+
+ jclass stringCls = (*env)->FindClass(env, "java/lang/String");
+ if (stringCls == NULL) {
+ // pending exception...
+ return JNI_ERR;
+ }
+ stringClass = (jclass) (*env)->NewGlobalRef(env, stringCls);
+ if (stringClass == NULL) {
+ return JNI_ERR;
+ }
+
+ return NETTY_JNI_VERSION;
+}
+
+// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT
+// http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html
+
+// Invoked by the JVM when statically linked
+JNIEXPORT jint JNI_OnLoad_netty_resolver_dns_native_macos(JavaVM* vm, void* reserved) {
+ return JNI_OnLoad_netty_resolver_dns_native_macos0(vm, reserved);
+}
+
+// Invoked by the JVM when statically linked
+JNIEXPORT void JNI_OnUnload_netty_resolver_dns_native_macos(JavaVM* vm, void* reserved) {
+ JNI_OnUnload_netty_resolver_dns_native_macos0(vm, reserved);
+}
+
+#ifndef NETTY_BUILD_STATIC
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ return JNI_OnLoad_netty_resolver_dns_native_macos0(vm, reserved);
+}
+
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
+ return JNI_OnUnload_netty_resolver_dns_native_macos0(vm, reserved);
+}
+#endif /* NETTY_BUILD_STATIC */
diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java
new file mode 100644
index 0000000000..9744c658cb
--- /dev/null
+++ b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/DnsResolver.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.resolver.dns.macos;
+
+import io.netty.channel.unix.NativeInetAddress;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Represent the {@code dns_resolver_t} struct.
+ */
+final class DnsResolver {
+
+ private final String domain;
+ private final InetSocketAddress[] nameservers;
+ private final int port;
+ private final String[] searches;
+ private final String options;
+ private final int timeout;
+ private final int searchOrder;
+
+ DnsResolver(String domain, byte[][] nameservers, int port,
+ String[] searches, String options, int timeout, int searchOrder) {
+ this.domain = domain;
+ if (nameservers == null) {
+ this.nameservers = new InetSocketAddress[0];
+ } else {
+ this.nameservers = new InetSocketAddress[nameservers.length];
+ for (int i = 0; i < nameservers.length; i++) {
+ byte[] addr = nameservers[i];
+ this.nameservers[i] = NativeInetAddress.address(addr, 0, addr.length);
+ }
+ }
+ this.port = port;
+ this.searches = searches;
+ this.options = options;
+ this.timeout = timeout;
+ this.searchOrder = searchOrder;
+ }
+
+ String domain() {
+ return domain;
+ }
+
+ InetSocketAddress[] nameservers() {
+ return nameservers;
+ }
+
+ int port() {
+ return port;
+ }
+
+ String[] searches() {
+ return searches;
+ }
+
+ String options() {
+ return options;
+ }
+
+ int timeout() {
+ return timeout;
+ }
+
+ int searchOrder() {
+ return searchOrder;
+ }
+}
diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java
new file mode 100644
index 0000000000..f82f571511
--- /dev/null
+++ b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProvider.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.resolver.dns.macos;
+
+import io.netty.resolver.dns.DnsServerAddressStream;
+import io.netty.resolver.dns.DnsServerAddressStreamProvider;
+import io.netty.resolver.dns.DnsServerAddressStreamProviders;
+import io.netty.resolver.dns.DnsServerAddresses;
+import io.netty.util.internal.NativeLibraryLoader;
+import io.netty.util.internal.PlatformDependent;
+import io.netty.util.internal.StringUtil;
+import io.netty.util.internal.SystemPropertyUtil;
+import io.netty.util.internal.ThrowableUtil;
+import io.netty.util.internal.logging.InternalLogger;
+import io.netty.util.internal.logging.InternalLoggerFactory;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * {@link DnsServerAddressStreamProvider} implementation which makes use of the same mechanism as
+ * Apple's open source mDNSResponder to retrieve the
+ * current nameserver configuration of the system.
+ */
+public final class MacOSDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
+
+ // Fallback provider
+ private static final DnsServerAddressStreamProvider DEFAULT_PROVIDER =
+ DnsServerAddressStreamProviders.platformDefault();
+
+ private static final Throwable UNAVAILABILITY_CAUSE;
+
+ private static final InternalLogger logger =
+ InternalLoggerFactory.getInstance(MacOSDnsServerAddressStreamProvider.class);
+
+ // Let's refresh every 10 seconds.
+ private static final long REFRESH_INTERVAL = TimeUnit.SECONDS.toNanos(10);
+
+ static {
+ Throwable cause = null;
+ try {
+ loadNativeLibrary();
+ } catch (Throwable error) {
+ cause = error;
+ }
+ UNAVAILABILITY_CAUSE = cause;
+ }
+
+ private static void loadNativeLibrary() {
+ String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
+ if (!name.startsWith("mac")) {
+ throw new IllegalStateException("Only supported on MacOS");
+ }
+ String staticLibName = "netty_resolver_dns_native_macos";
+ String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
+ ClassLoader cl = PlatformDependent.getClassLoader(MacOSDnsServerAddressStreamProvider.class);
+ try {
+ NativeLibraryLoader.load(sharedLibName, cl);
+ } catch (UnsatisfiedLinkError e1) {
+ try {
+ NativeLibraryLoader.load(staticLibName, cl);
+ logger.debug("Failed to load {}", sharedLibName, e1);
+ } catch (UnsatisfiedLinkError e2) {
+ ThrowableUtil.addSuppressed(e1, e2);
+ throw e1;
+ }
+ }
+ }
+
+ public static boolean isAvailable() {
+ return UNAVAILABILITY_CAUSE == null;
+ }
+
+ public static void ensureAvailability() {
+ if (UNAVAILABILITY_CAUSE != null) {
+ throw (Error) new UnsatisfiedLinkError(
+ "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
+ }
+ }
+
+ public static Throwable unavailabilityCause() {
+ return UNAVAILABILITY_CAUSE;
+ }
+
+ public MacOSDnsServerAddressStreamProvider() {
+ ensureAvailability();
+ }
+
+ private volatile Map currentMappings = retrieveCurrentMappings();
+ private final AtomicLong lastRefresh = new AtomicLong(System.nanoTime());
+
+ private static Map retrieveCurrentMappings() {
+ DnsResolver[] resolvers = resolvers();
+
+ if (resolvers == null || resolvers.length == 0) {
+ return Collections.emptyMap();
+ }
+ Map resolverMap = new HashMap(resolvers.length);
+ for (DnsResolver resolver: resolvers) {
+ // Skip mdns
+ if ("mdns".equalsIgnoreCase(resolver.options())) {
+ continue;
+ }
+ InetSocketAddress[] nameservers = resolver.nameservers();
+ if (nameservers == null || nameservers.length == 0) {
+ continue;
+ }
+ String domain = resolver.domain();
+ if (domain == null) {
+ // Default mapping.
+ domain = StringUtil.EMPTY_STRING;
+ }
+ InetSocketAddress[] servers = resolver.nameservers();
+ for (int a = 0; a < servers.length; a++) {
+ InetSocketAddress address = servers[a];
+ // Check if the default port should be used
+ if (address.getPort() == 0) {
+ int port = resolver.port();
+ if (port == 0) {
+ port = 53;
+ }
+ servers[a] = new InetSocketAddress(address.getAddress(), port);
+ }
+ }
+
+ resolverMap.put(domain, DnsServerAddresses.sequential(servers));
+ }
+ return resolverMap;
+ }
+
+ @Override
+ public DnsServerAddressStream nameServerAddressStream(String hostname) {
+ long last = lastRefresh.get();
+ Map resolverMap = currentMappings;
+ if (System.nanoTime() - last > REFRESH_INTERVAL) {
+ // This is slightly racy which means it will be possible still use the old configuration for a small
+ // amount of time, but that's ok.
+ if (lastRefresh.compareAndSet(last, System.nanoTime())) {
+ resolverMap = currentMappings = retrieveCurrentMappings();
+ }
+ }
+
+ final String originalHostname = hostname;
+ for (;;) {
+ int i = hostname.indexOf('.', 1);
+ if (i < 0 || i == hostname.length() - 1) {
+ // Try access default mapping.
+ DnsServerAddresses addresses = resolverMap.get(StringUtil.EMPTY_STRING);
+ if (addresses != null) {
+ return addresses.stream();
+ }
+ return DEFAULT_PROVIDER.nameServerAddressStream(originalHostname);
+ }
+
+ DnsServerAddresses addresses = resolverMap.get(hostname);
+ if (addresses != null) {
+ return addresses.stream();
+ }
+
+ hostname = hostname.substring(i + 1);
+ }
+ }
+
+ private static native DnsResolver[] resolvers();
+}
diff --git a/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java
new file mode 100644
index 0000000000..d12eeb90a3
--- /dev/null
+++ b/resolver-dns-native-macos/src/main/java/io/netty/resolver/dns/macos/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * MacOS specific nameserver resolution.
+ */
+@UnstableApi
+package io.netty.resolver.dns.macos;
+
+import io.netty.util.internal.UnstableApi;
diff --git a/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java
new file mode 100644
index 0000000000..aafec23727
--- /dev/null
+++ b/resolver-dns-native-macos/src/test/java/io/netty/resolver/dns/macos/MacOSDnsServerAddressStreamProviderTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.resolver.dns.macos;
+
+import io.netty.resolver.dns.DnsServerAddressStream;
+import io.netty.resolver.dns.DnsServerAddressStreamProvider;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class MacOSDnsServerAddressStreamProviderTest {
+
+ @BeforeClass
+ public static void assume() {
+ Assume.assumeTrue(MacOSDnsServerAddressStreamProvider.isAvailable());
+ }
+
+ @Test
+ public void test() {
+ DnsServerAddressStreamProvider provider = new MacOSDnsServerAddressStreamProvider();
+ DnsServerAddressStream stream = provider.nameServerAddressStream("netty.io");
+ Assert.assertNotNull(stream);
+ Assert.assertNotEquals(0, stream.size());
+
+ for (int i = 0; i < stream.size(); i++) {
+ Assert.assertNotEquals(0, stream.next().getPort());
+ }
+ }
+}
diff --git a/tarball/pom.xml b/tarball/pom.xml
index e2d8387c2c..2bbd0613f7 100644
--- a/tarball/pom.xml
+++ b/tarball/pom.xml
@@ -123,6 +123,14 @@
compile
true
+
+ ${project.groupId}
+ netty-resolver-dns-native-macos
+ ${project.version}
+ osx-x86_64
+ compile
+ true
+
@@ -145,6 +153,14 @@
compile
true
+
+ ${project.groupId}
+ netty-resolver-dns-native-macos
+ ${project.version}
+ osx-x86_64
+ compile
+ true
+
diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c
index 5a50efaaa6..8303e23f42 100644
--- a/transport-native-unix-common/src/main/c/netty_unix_socket.c
+++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c
@@ -173,7 +173,7 @@ static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storag
}
}
-static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) {
+jbyteArray netty_unix_socket_createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) {
jsize len = addressLength(addr);
jbyteArray bArray = (*env)->NewByteArray(env, len);
if (bArray == NULL) {
@@ -587,7 +587,7 @@ static jbyteArray netty_unix_socket_remoteAddress(JNIEnv* env, jclass clazz, jin
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
- return createInetSocketAddressArray(env, &addr);
+ return netty_unix_socket_createInetSocketAddressArray(env, &addr);
}
static jbyteArray netty_unix_socket_localAddress(JNIEnv* env, jclass clazz, jint fd) {
@@ -596,7 +596,7 @@ static jbyteArray netty_unix_socket_localAddress(JNIEnv* env, jclass clazz, jint
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
return NULL;
}
- return createInetSocketAddressArray(env, &addr);
+ return netty_unix_socket_createInetSocketAddressArray(env, &addr);
}
static jint netty_unix_socket_newSocketDgramFd(JNIEnv* env, jclass clazz, jboolean ipv6) {
diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.h b/transport-native-unix-common/src/main/c/netty_unix_socket.h
index e24de06b54..96d8794bc2 100644
--- a/transport-native-unix-common/src/main/c/netty_unix_socket.h
+++ b/transport-native-unix-common/src/main/c/netty_unix_socket.h
@@ -21,6 +21,8 @@
// External C methods
int netty_unix_socket_initSockaddr(JNIEnv* env, jboolean ipv6, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr, socklen_t* addrSize);
+jbyteArray netty_unix_socket_createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr);
+
int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen);
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
int netty_unix_socket_ipAddressLength(const struct sockaddr_storage* addr);